diff --git a/src/db/domain.rs b/src/db/domain.rs index 5ea6d43..6cca261 100644 --- a/src/db/domain.rs +++ b/src/db/domain.rs @@ -1,35 +1,4 @@ -use crate::db::error::UserCreationError; -use crate::db::{Group, User, UserRef}; - #[derive(Clone, Debug, Default)] pub struct Domain { pub name: String, - pub users: Vec, - #[expect(unused)] - pub groups: Vec, -} - -impl Domain { - pub fn get_user(&self, req_user: &UserRef) -> Option { - assert!( - req_user.domain.as_deref().unwrap_or("") == self.name, - "Should only call Domain::get_user on the matching domain. Asked for {:?} on domain {}", - req_user, - self.name - ); - - self.users - .iter() - .find(|u| u.username == req_user.username) - .cloned() - } - - pub fn create_user(&mut self, user: User, user_ref: UserRef) -> Result<(), UserCreationError> { - if self.get_user(&user_ref).is_some() { - return Err(UserCreationError::UserAlreadyExists(user_ref)); - } - - self.users.push(user); - Ok(()) - } } diff --git a/src/db/interface.rs b/src/db/interface.rs index ed6d86c..e5d58ec 100644 --- a/src/db/interface.rs +++ b/src/db/interface.rs @@ -36,8 +36,12 @@ impl DatabaseInterface for Database { .await } - async fn list_users(&self, domain: Option) -> Result, BoxedError> { - self.inner.read().await.list_users(domain).await + async fn list_all_users(&self) -> Result, BoxedError> { + self.inner.read().await.list_all_users().await + } + + async fn list_domain_users(&self, domain: Option) -> Result, BoxedError> { + self.inner.read().await.list_domain_users(domain).await } } @@ -63,10 +67,13 @@ pub trait DatabaseInterface: Clone + Send + Sync + 'static { current_user: &User, ) -> Result, BoxedError>; - /// Use empty string domain to request global users (TODO: this is not very DX) + #[expect(unused)] + fn list_all_users(&self) -> impl Future, BoxedError>>; + + /// List users on a specific domain. /// - /// When domain does not exist, the returned list is empty. - fn list_users( + /// A `None` domain requested lists global service users. + fn list_domain_users( &self, domain: Option, ) -> impl std::future::Future, BoxedError>> + Send; diff --git a/src/db/memory.rs b/src/db/memory.rs index b9b8545..1ba13dd 100644 --- a/src/db/memory.rs +++ b/src/db/memory.rs @@ -1,23 +1,21 @@ use std::future::{Future, ready}; use crate::db::error::{BoxedError, DomainCreationError, UserCreationError}; -use crate::db::{Database, DatabaseInterface, Domain, Operation, User, UserRef}; +use crate::db::{Database, DatabaseInterface, Domain, Group, Operation, User, UserRef}; #[derive(Clone, Debug, Default)] pub struct MemoryDatabase { + // We store data in tables like in SQL pub domains: Vec, - /// Where global users/groups are registered - pub global_domain: Domain, + #[expect(unused)] + pub groups: Vec, + pub users: Vec, } impl MemoryDatabase { pub fn new() -> Database { Database::new(Self::default()) } - - pub fn get_domain_mut(&mut self, domain: &str) -> Option<&mut Domain> { - self.domains.iter_mut().find(|d| d.name == domain) - } } impl DatabaseInterface for MemoryDatabase { @@ -39,8 +37,6 @@ impl DatabaseInterface for MemoryDatabase { self.domains.push(Domain { name: domain.to_string(), - users: vec![], - groups: vec![], }); ready(Ok(Ok(()))) @@ -54,37 +50,36 @@ impl DatabaseInterface for MemoryDatabase { ready(Ok(Some(domain.clone()))) } - async fn get_user(&self, req_user: &UserRef) -> Result, BoxedError> { - let Some(req_domain) = &req_user.domain else { - // If no domain is provided for the user query, look up the global users - return Ok(self.global_domain.get_user(req_user)); - }; - - let Some(domain) = self.get_domain(req_domain).await? else { - return Ok(None); - }; - - Ok(domain.get_user(req_user)) + fn get_user( + &self, + req_user: &UserRef, + ) -> impl Future, BoxedError>> { + ready(Ok(self + .users + .iter() + .find(|u| u.username == req_user.username && u.domain == req_user.domain) + .cloned())) } - fn create_user( + async fn create_user( &mut self, user: User, - ) -> impl Future, BoxedError>> { + ) -> Result, BoxedError> { let user_ref = user.user_ref(); - let Some(req_domain) = &user.domain else { - // No domain requested, this is a global user creation - return ready(Ok(self.global_domain.create_user(user, user_ref))); - }; + if self.get_user(&user_ref).await?.is_some() { + return Ok(Err(UserCreationError::UserAlreadyExists(user_ref))); + } - let Some(domain) = self.get_domain_mut(req_domain) else { - return ready(Ok(Err(UserCreationError::DomainNotFound( - req_domain.clone(), - )))); - }; + // If a domain is requested (i.e. not a global user), make sure the domain exists + if let Some(req_domain) = &user.domain + && self.get_domain(req_domain).await?.is_none() + { + return Ok(Err(UserCreationError::DomainNotFound(req_domain.clone()))); + } - ready(Ok(domain.create_user(user, user_ref))) + self.users.push(user); + Ok(Ok(())) } async fn try_create_user( @@ -109,23 +104,19 @@ impl DatabaseInterface for MemoryDatabase { self.create_user(new_user).await } - async fn list_users(&self, domain: Option) -> Result, BoxedError> { - if let Some(domain) = domain { - if domain.is_empty() { - Ok(self.global_domain.users.clone()) - } else { - let Some(domain) = self.get_domain(&domain).await? else { - return Ok(vec![]); - }; - Ok(domain.users.clone()) - } - } else { - // Aggregate all users - let mut users = self.global_domain.users.clone(); - for domain in &self.domains { - users.extend(domain.users.clone()); - } - Ok(users) - } + fn list_all_users(&self) -> impl Future, BoxedError>> { + ready(Ok(self.users.clone())) + } + + fn list_domain_users( + &self, + domain: Option, + ) -> impl Future, BoxedError>> { + ready(Ok(self + .users + .iter() + .filter(|u| u.domain == domain) + .cloned() + .collect())) } } diff --git a/src/http/mod.rs b/src/http/mod.rs index 98a20ed..bd5b2ec 100644 --- a/src/http/mod.rs +++ b/src/http/mod.rs @@ -115,7 +115,11 @@ pub async fn home( // When the user has no domain (service admin) list all domains let op = Operation::ListUsers(session.user.domain.clone()); let other_users = if session.user.can_perform(&op) { - match state.db.list_users(session.user.domain.clone()).await { + match state + .db + .list_domain_users(session.user.domain.clone()) + .await + { Ok(other_users) => other_users, Err(e) => { return format!("Database error: {e}").into_response(); diff --git a/templates/home.html b/templates/home.html index 584fad4..e9f4925 100644 --- a/templates/home.html +++ b/templates/home.html @@ -8,7 +8,7 @@ {% if other_users %}
-

Other users you have permission to see

+

Other users you have permission to see on your own domain

    {% for user in other_users %}
  • {{ user.mail }}