use std::sync::Arc; use async_trait::async_trait; use libertas_core::{ authz::{self, Permission}, error::{CoreError, CoreResult}, models::{Album, AlbumPermission, Media, Person, PersonPermission, User}, repositories::{ AlbumRepository, AlbumShareRepository, FaceRegionRepository, MediaRepository, PersonRepository, PersonShareRepository, UserRepository, }, services::AuthorizationService, }; use uuid::Uuid; pub struct AuthorizationServiceImpl { media_repo: Arc, album_repo: Arc, album_share_repo: Arc, person_repo: Arc, person_share_repo: Arc, face_repo: Arc, user_repo: Arc, } impl AuthorizationServiceImpl { pub fn new( media_repo: Arc, album_repo: Arc, album_share_repo: Arc, person_repo: Arc, person_share_repo: Arc, face_repo: Arc, user_repo: Arc, ) -> Self { Self { media_repo, album_repo, album_share_repo, person_repo, person_share_repo, face_repo, user_repo, } } async fn get_user(&self, user_id: Uuid) -> CoreResult { let user = self .user_repo .find_by_id(user_id) .await? .ok_or(CoreError::NotFound("User".to_string(), user_id))?; Ok(user) } async fn get_media(&self, media_id: Uuid) -> CoreResult { let media = self .media_repo .find_by_id(media_id) .await? .ok_or(CoreError::NotFound("Media".to_string(), media_id))?; Ok(media) } async fn get_album(&self, album_id: Uuid) -> CoreResult { let album = self .album_repo .find_by_id(album_id) .await? .ok_or(CoreError::NotFound("Album".to_string(), album_id))?; Ok(album) } async fn get_album_share_permission( &self, album_id: Uuid, user_id: Uuid, ) -> CoreResult> { let permission = self .album_share_repo .get_user_permission(album_id, user_id) .await?; Ok(permission) } async fn get_person_share_permission( &self, person_id: Uuid, user_id: Uuid, ) -> CoreResult> { let permission = self .person_share_repo .get_user_permission(person_id, user_id) .await?; Ok(permission) } async fn get_person(&self, person_id: Uuid) -> CoreResult { let person = self .person_repo .find_by_id(person_id) .await? .ok_or(CoreError::NotFound("Person".to_string(), person_id))?; Ok(person) } } #[async_trait] impl AuthorizationService for AuthorizationServiceImpl { async fn check_permission( &self, user_id: Option, permission: Permission, ) -> CoreResult<()> { let user = if let Some(id) = user_id { Some(self.get_user(id).await?) } else { None }; if let Some(ref user) = user { if authz::is_admin(user) { // [cite: 115] return Ok(()); } } match permission { Permission::ViewMedia(media_id) => { let media = self.get_media(media_id).await?; if self.album_repo.is_media_in_public_album(media_id).await? { return Ok(()); } if let Some(id) = user_id { if authz::is_owner(id, &media) { // [cite: 117] return Ok(()); } if self .album_share_repo .is_media_in_shared_album(media_id, id) .await? { // [cite: 118-119] return Ok(()); } } Err(CoreError::Auth( "User does not have permission to view this media.".into(), )) } Permission::DeleteMedia(media_id) | Permission::EditMedia(media_id) => { let user_id = user_id.ok_or(CoreError::Auth( "Authentication required for this action".into(), ))?; let media = self.get_media(media_id).await?; if authz::is_owner(user_id, &media) { return Ok(()); } Err(CoreError::Auth( "User does not have permission to modify this media.".into(), )) } Permission::AddTags(media_id) | Permission::RemoveTags(media_id) | Permission::EditTags(media_id) => { let user_id = user_id.ok_or(CoreError::Auth( "Authentication required for this action".into(), ))?; let media = self.get_media(media_id).await?; if authz::is_owner(user_id, &media) { return Ok(()); } let can_contribute = self .album_share_repo .is_media_in_contributable_album(media_id, user_id) .await?; if can_contribute { return Ok(()); } Err(CoreError::Auth( "User does not have permission to modify tags for this media.".into(), )) } Permission::ViewAlbum(album_id) => { let user_id = user_id.ok_or(CoreError::Auth( "Authentication required for this action".into(), ))?; let album = self.get_album(album_id).await?; let share_permission = self.get_album_share_permission(album_id, user_id).await?; if authz::can_view_album(user_id, &album, share_permission) { return Ok(()); } Err(CoreError::Auth( "User does not have permission to view this album.".into(), )) } Permission::AddToAlbum(album_id) | Permission::EditAlbum(album_id) => { let user_id = user_id.ok_or(CoreError::Auth( "Authentication required for this action".into(), ))?; let album = self.get_album(album_id).await?; let share_permission = self.get_album_share_permission(album_id, user_id).await?; if authz::can_contribute_to_album(user_id, &album, share_permission) { return Ok(()); } Err(CoreError::Auth( "User does not have permission to modify this album.".into(), )) } Permission::ShareAlbum(album_id) | Permission::DeleteAlbum(album_id) => { let user_id = user_id.ok_or(CoreError::Auth( "Authentication required for this action".into(), ))?; let album = self.get_album(album_id).await?; if authz::is_owner(user_id, &album) { return Ok(()); } Err(CoreError::Auth( "User does not have permission to share or delete this album.".into(), )) } Permission::ViewPerson(person_id) => { let user_id = user_id.ok_or(CoreError::Auth( "Authentication required for this action".into(), ))?; let person = self.get_person(person_id).await?; let share_permission = self.get_person_share_permission(person_id, user_id).await?; if authz::can_access_person(user_id, &person, share_permission) { return Ok(()); } Err(CoreError::Auth( "User does not have permission to view this person.".into(), )) } Permission::EditPerson(person_id) | Permission::SharePerson(person_id) | Permission::DeletePerson(person_id) => { let user_id = user_id.ok_or(CoreError::Auth( "Authentication required for this action".into(), ))?; let person = self.get_person(person_id).await?; if authz::is_owner(user_id, &person) { return Ok(()); } Err(CoreError::Auth( "User does not have permission to modify this person.".into(), )) } Permission::UsePerson(person_id) => { let user_id = user_id.ok_or(CoreError::Auth( "Authentication required for this action".into(), ))?; let person = self.get_person(person_id).await?; let share_permission = self.get_person_share_permission(person_id, user_id).await?; if authz::can_use_person(user_id, &person, share_permission) { return Ok(()); } Err(CoreError::Auth( "User does not have permission to use this person.".into(), )) } Permission::ViewFaces(media_id) => { self.check_permission(user_id, Permission::ViewMedia(media_id)) .await } Permission::AssignFace(face_region_id) => { let face = self.face_repo .find_by_id(face_region_id) .await? .ok_or(CoreError::NotFound( "FaceRegion".to_string(), face_region_id, ))?; self.check_permission(user_id, Permission::AddTags(face.media_id)) .await } } } }