use crate::common::errors::DomainError; use crate::common::value_objects::{DateTimeStamp, Email, PasswordHash, SystemId}; use chrono::{DateTime, Utc}; use std::collections::HashSet; // --- Permission --- #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)] pub enum PermissionAction { ReadAsset, ReadMetadata, ReadLocation, ReadPerson, WriteMetadata, DeleteAsset, ManageAccess, ManageUsers, ManageSystem, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)] pub enum ResourceType { Asset, Album, Collection, Person, Directory, Global, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)] pub struct Permission { pub action: PermissionAction, pub resource_type: ResourceType, } impl Permission { pub fn new(action: PermissionAction, resource_type: ResourceType) -> Self { Self { action, resource_type, } } } pub fn viewer_permissions() -> HashSet { HashSet::from([ Permission::new(PermissionAction::ReadAsset, ResourceType::Global), Permission::new(PermissionAction::ReadMetadata, ResourceType::Global), ]) } pub fn contributor_permissions() -> HashSet { let mut perms = viewer_permissions(); perms.insert(Permission::new( PermissionAction::WriteMetadata, ResourceType::Global, )); perms } pub fn admin_permissions() -> HashSet { let mut perms = contributor_permissions(); perms.insert(Permission::new( PermissionAction::DeleteAsset, ResourceType::Global, )); perms.insert(Permission::new( PermissionAction::ManageAccess, ResourceType::Global, )); perms.insert(Permission::new( PermissionAction::ManageUsers, ResourceType::Global, )); perms.insert(Permission::new( PermissionAction::ManageSystem, ResourceType::Global, )); perms.insert(Permission::new( PermissionAction::ReadLocation, ResourceType::Global, )); perms.insert(Permission::new( PermissionAction::ReadPerson, ResourceType::Global, )); perms } // --- Role --- #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] pub struct Role { pub role_id: SystemId, pub name: String, pub permissions: HashSet, pub is_system_default: bool, } impl Role { pub fn new( name: impl Into, permissions: HashSet, is_system_default: bool, ) -> Self { Self { role_id: SystemId::new(), name: name.into(), permissions, is_system_default, } } pub fn has_permission(&self, action: PermissionAction, resource_type: ResourceType) -> bool { self.permissions .contains(&Permission::new(action, resource_type)) } } // --- User --- #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] pub struct User { pub id: SystemId, pub username: String, pub email: Email, pub password_hash: PasswordHash, pub created_at: DateTime, } impl User { pub fn new(username: impl Into, email: Email, password_hash: PasswordHash) -> Self { Self { id: SystemId::new(), username: username.into(), email, password_hash, created_at: Utc::now(), } } } // --- RefreshToken --- #[derive(Debug, Clone)] pub struct RefreshToken { pub token_id: SystemId, pub user_id: SystemId, pub token_hash: String, pub expires_at: DateTimeStamp, pub revoked: bool, pub created_at: DateTimeStamp, } impl RefreshToken { pub fn new(user_id: SystemId, token_hash: String, expires_at: DateTimeStamp) -> Self { Self { token_id: SystemId::new(), user_id, token_hash, expires_at, revoked: false, created_at: DateTimeStamp::now(), } } pub fn is_valid(&self) -> bool { !self.revoked && *self.expires_at.as_datetime() > Utc::now() } } // --- Group --- #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] pub struct Group { pub group_id: SystemId, pub name: String, pub owner_user_id: SystemId, pub members: HashSet, } impl Group { pub fn new(name: impl Into, owner_user_id: SystemId) -> Self { let mut members = HashSet::new(); members.insert(owner_user_id); Self { group_id: SystemId::new(), name: name.into(), owner_user_id, members, } } pub fn add_member(&mut self, user_id: SystemId) -> Result<(), DomainError> { if self.members.contains(&user_id) { return Err(DomainError::Conflict(format!( "User {user_id} is already a member" ))); } self.members.insert(user_id); Ok(()) } pub fn remove_member(&mut self, user_id: SystemId) -> Result<(), DomainError> { if user_id == self.owner_user_id { return Err(DomainError::Validation( "Cannot remove the group owner".to_string(), )); } if !self.members.remove(&user_id) { return Err(DomainError::NotFound(format!( "User {user_id} is not a member" ))); } Ok(()) } pub fn is_member(&self, user_id: &SystemId) -> bool { self.members.contains(user_id) } }