Split up models

Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
This commit is contained in:
2025-06-04 23:55:52 +01:00
parent 455bf7b88d
commit b81a49af3d
7 changed files with 150 additions and 137 deletions

View File

@@ -2,10 +2,7 @@ use non_empty_string::NonEmptyString;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::{
collections::HashMap,
ops::{Deref, DerefMut},
};
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UsersFile {
@@ -27,68 +24,8 @@ pub struct UserFile {
pub extra: Option<HashMap<NonEmptyString, Value>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct User {
pub displayname: NonEmptyString,
pub email: Option<String>,
pub password: NonEmptyString,
pub disabled: bool,
pub groups: Vec<NonEmptyString>,
#[serde(flatten)]
pub extra: Option<HashMap<NonEmptyString, Value>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Users {
pub users: HashMap<NonEmptyString, User>,
#[serde(flatten)]
pub extra: Option<HashMap<NonEmptyString, Value>>,
}
impl Deref for Users {
type Target = HashMap<NonEmptyString, User>;
fn deref(&self) -> &Self::Target {
&self.users
}
}
impl DerefMut for Users {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.users
}
}
impl From<UserFile> for User {
fn from(user_file: UserFile) -> Self {
Self {
displayname: user_file.displayname,
email: user_file.email,
password: user_file.password,
disabled: user_file.disabled.unwrap_or(false),
groups: user_file.groups.unwrap_or_default(),
extra: user_file.extra,
}
}
}
impl From<UsersFile> for Users {
fn from(users_file: UsersFile) -> Self {
Self {
users: users_file
.users
.into_iter()
.map(|(key, user)| (key, User::from(user)))
.collect(),
extra: users_file.extra,
}
}
}
impl From<User> for UserFile {
fn from(user: User) -> Self {
impl From<super::users::User> for UserFile {
fn from(user: super::users::User) -> Self {
Self {
displayname: user.displayname,
email: user.email,
@@ -104,8 +41,8 @@ impl From<User> for UserFile {
}
}
impl From<Users> for UsersFile {
fn from(users: Users) -> Self {
impl From<super::users::Users> for UsersFile {
fn from(users: super::users::Users) -> Self {
Self {
users: users
.users
@@ -116,45 +53,3 @@ impl From<Users> for UsersFile {
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Group {
pub users: Vec<NonEmptyString>,
}
pub struct Groups {
pub groups: HashMap<NonEmptyString, Group>,
}
impl Deref for Groups {
type Target = HashMap<NonEmptyString, Group>;
fn deref(&self) -> &Self::Target {
&self.groups
}
}
impl DerefMut for Groups {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.groups
}
}
impl From<UsersFile> for Groups {
fn from(users_file: UsersFile) -> Self {
users_file.users.into_iter().fold(
Self {
groups: HashMap::new(),
},
|mut acc, (key, user)| {
for group in user.groups.unwrap_or_default() {
acc.entry(group)
.or_insert_with(|| Group { users: Vec::new() })
.users
.push(key.clone());
}
acc
},
)
}
}

49
src/models/groups.rs Normal file
View File

@@ -0,0 +1,49 @@
use non_empty_string::NonEmptyString;
use serde::{Deserialize, Serialize};
use std::{
collections::HashMap,
ops::{Deref, DerefMut},
};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Group {
pub users: Vec<NonEmptyString>,
}
pub struct Groups {
pub groups: HashMap<NonEmptyString, Group>,
}
impl Deref for Groups {
type Target = HashMap<NonEmptyString, Group>;
fn deref(&self) -> &Self::Target {
&self.groups
}
}
impl DerefMut for Groups {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.groups
}
}
impl From<super::authelia::UsersFile> for Groups {
fn from(users_file: super::authelia::UsersFile) -> Self {
users_file.users.into_iter().fold(
Self {
groups: HashMap::new(),
},
|mut acc, (key, user)| {
for group in user.groups.unwrap_or_default() {
acc.entry(group)
.or_insert_with(|| Group { users: Vec::new() })
.users
.push(key.clone());
}
acc
},
)
}
}

View File

@@ -1 +1,3 @@
pub mod authelia;
pub mod groups;
pub mod users;

68
src/models/users.rs Normal file
View File

@@ -0,0 +1,68 @@
use non_empty_string::NonEmptyString;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::{
collections::HashMap,
ops::{Deref, DerefMut},
};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct User {
pub displayname: NonEmptyString,
pub email: Option<String>,
pub password: NonEmptyString,
pub disabled: bool,
pub groups: Vec<NonEmptyString>,
#[serde(flatten)]
pub extra: Option<HashMap<NonEmptyString, Value>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Users {
pub users: HashMap<NonEmptyString, User>,
#[serde(flatten)]
pub extra: Option<HashMap<NonEmptyString, Value>>,
}
impl Deref for Users {
type Target = HashMap<NonEmptyString, User>;
fn deref(&self) -> &Self::Target {
&self.users
}
}
impl DerefMut for Users {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.users
}
}
impl From<super::authelia::UserFile> for User {
fn from(user_file: super::authelia::UserFile) -> Self {
Self {
displayname: user_file.displayname,
email: user_file.email,
password: user_file.password,
disabled: user_file.disabled.unwrap_or(false),
groups: user_file.groups.unwrap_or_default(),
extra: user_file.extra,
}
}
}
impl From<super::authelia::UsersFile> for Users {
fn from(users_file: super::authelia::UsersFile) -> Self {
Self {
users: users_file
.users
.into_iter()
.map(|(key, user)| (key, User::from(user)))
.collect(),
extra: users_file.extra,
}
}
}

View File

@@ -11,7 +11,7 @@ use log::error;
use non_empty_string::NonEmptyString;
use serde::{Deserialize, Serialize};
use crate::{models::authelia, routes::auth, state::State};
use crate::{models::groups, routes::auth, state::State};
#[derive(Debug, Serialize)]
struct GroupResponse {
@@ -19,8 +19,8 @@ struct GroupResponse {
users: Vec<NonEmptyString>,
}
impl From<(NonEmptyString, authelia::Group)> for GroupResponse {
fn from((groupname, group): (NonEmptyString, authelia::Group)) -> Self {
impl From<(NonEmptyString, groups::Group)> for GroupResponse {
fn from((groupname, group): (NonEmptyString, groups::Group)) -> Self {
Self {
groupname,
users: group.users,
@@ -30,8 +30,8 @@ impl From<(NonEmptyString, authelia::Group)> for GroupResponse {
type GroupsResponse = HashMap<NonEmptyString, GroupResponse>;
impl From<authelia::Groups> for GroupsResponse {
fn from(groups: authelia::Groups) -> Self {
impl From<groups::Groups> for GroupsResponse {
fn from(groups: groups::Groups) -> Self {
groups
.groups
.into_iter()
@@ -74,7 +74,7 @@ pub struct GroupCreate {
users: Vec<NonEmptyString>,
}
impl From<GroupCreate> for authelia::Group {
impl From<GroupCreate> for groups::Group {
fn from(update: GroupCreate) -> Self {
Self {
users: update.users,
@@ -97,7 +97,7 @@ pub async fn create(
return Err(StatusCode::CONFLICT);
}
let group_created = authelia::Group::from(group_create);
let group_created = groups::Group::from(group_create);
for username in &group_created.users {
if !users.contains_key(username) {
@@ -125,7 +125,7 @@ pub struct GroupUpdate {
users: Vec<NonEmptyString>,
}
impl From<GroupUpdate> for authelia::Group {
impl From<GroupUpdate> for groups::Group {
fn from(update: GroupUpdate) -> Self {
Self {
users: update.users,
@@ -150,7 +150,7 @@ pub async fn update(
.unwrap_or_else(|| groupname.clone());
let group_existing = groups.get(&groupname).ok_or(StatusCode::NOT_FOUND)?;
let group_updated = authelia::Group::from(group_update);
let group_updated = groups::Group::from(group_update);
if groupname != new_groupname
&& (groupname == state.config.oauth.admin_group

View File

@@ -12,7 +12,7 @@ use non_empty_string::NonEmptyString;
use serde::{Deserialize, Serialize};
use crate::{
models::authelia, routes::auth, state::State, utils::crypto::generate_random_password_hash,
models::users, routes::auth, state::State, utils::crypto::generate_random_password_hash,
};
#[derive(Debug, Serialize)]
@@ -23,8 +23,8 @@ struct UserResponse {
groups: Option<Vec<NonEmptyString>>,
}
impl From<(NonEmptyString, authelia::User)> for UserResponse {
fn from((username, user): (NonEmptyString, authelia::User)) -> Self {
impl From<(NonEmptyString, users::User)> for UserResponse {
fn from((username, user): (NonEmptyString, users::User)) -> Self {
Self {
username,
displayname: user.displayname,
@@ -36,8 +36,8 @@ impl From<(NonEmptyString, authelia::User)> for UserResponse {
type UsersResponse = HashMap<NonEmptyString, UserResponse>;
impl From<authelia::Users> for UsersResponse {
fn from(users: authelia::Users) -> Self {
impl From<users::Users> for UsersResponse {
fn from(users: users::Users) -> Self {
users
.users
.into_iter()
@@ -84,7 +84,7 @@ pub struct UserCreate {
}
#[allow(clippy::fallible_impl_from)]
impl From<UserCreate> for authelia::User {
impl From<UserCreate> for users::User {
fn from(user_create: UserCreate) -> Self {
Self {
displayname: user_create.displayname,
@@ -112,7 +112,7 @@ pub async fn create(
return Err(StatusCode::CONFLICT);
}
let user_created = authelia::User::from(user_create);
let user_created = users::User::from(user_create);
users.users.insert(username.clone(), user_created.clone());
state.save_users(users).map_err(|e| {
@@ -132,7 +132,7 @@ pub struct UserUpdate {
groups: Option<Vec<NonEmptyString>>,
}
impl From<(Self, UserUpdate)> for authelia::User {
impl From<(Self, UserUpdate)> for users::User {
fn from((user_existing, user_update): (Self, UserUpdate)) -> Self {
Self {
displayname: user_update.displayname,
@@ -162,7 +162,7 @@ pub async fn update(
.unwrap_or_else(|| username.clone());
let user_existing = users.remove(&username).ok_or(StatusCode::NOT_FOUND)?;
let user_updated = authelia::User::from((user_existing, user_update));
let user_updated = users::User::from((user_existing, user_update));
users
.users

View File

@@ -3,34 +3,33 @@ use std::error::Error;
use crate::{models, state::State};
impl State {
pub fn load_users(&self) -> Result<models::authelia::Users, Box<dyn Error + Send + Sync>> {
pub fn load_users(&self) -> Result<models::users::Users, Box<dyn Error + Send + Sync>> {
let file_contents = std::fs::read_to_string(&self.config.authelia.user_database)?;
let users_file: models::authelia::UsersFile = serde_yaml::from_str(&file_contents)?;
let users = models::authelia::Users::from(users_file);
let users = models::users::Users::from(users_file);
Ok(users)
}
pub fn load_groups(&self) -> Result<models::authelia::Groups, Box<dyn Error + Send + Sync>> {
pub fn load_groups(&self) -> Result<models::groups::Groups, Box<dyn Error + Send + Sync>> {
let file_contents = std::fs::read_to_string(&self.config.authelia.user_database)?;
let users_file = serde_yaml::from_str::<models::authelia::UsersFile>(&file_contents)?;
let groups = models::authelia::Groups::from(users_file);
let groups = models::groups::Groups::from(users_file);
Ok(groups)
}
pub fn load_users_and_groups(
&self,
) -> Result<(models::authelia::Users, models::authelia::Groups), Box<dyn Error + Send + Sync>>
{
) -> Result<(models::users::Users, models::groups::Groups), Box<dyn Error + Send + Sync>> {
let file_contents = std::fs::read_to_string(&self.config.authelia.user_database)?;
let users_file = serde_yaml::from_str::<models::authelia::UsersFile>(&file_contents)?;
let users = models::authelia::Users::from(users_file.clone());
let groups = models::authelia::Groups::from(users_file);
let users = models::users::Users::from(users_file.clone());
let groups = models::groups::Groups::from(users_file);
Ok((users, groups))
}
pub fn save_users(
&self,
users: models::authelia::Users,
users: models::users::Users,
) -> Result<(), Box<dyn Error + Send + Sync>> {
let users_file = models::authelia::UsersFile::from(users);
let file_contents = serde_yaml::to_string(&users_file)?;