feat: Add public album routes and enhance authorization checks for media and albums
This commit is contained in:
@@ -5,7 +5,7 @@ use chrono::Utc;
|
||||
use libertas_core::{
|
||||
authz::{self, Permission},
|
||||
error::{CoreError, CoreResult},
|
||||
models::Album,
|
||||
models::{Album, PublicAlbumBundle},
|
||||
repositories::{AlbumRepository, AlbumShareRepository},
|
||||
schema::{AddMediaToAlbumData, CreateAlbumData, ShareAlbumData, UpdateAlbumData},
|
||||
services::{AlbumService, AuthorizationService},
|
||||
@@ -57,7 +57,7 @@ impl AlbumService for AlbumServiceImpl {
|
||||
|
||||
async fn get_album_details(&self, album_id: Uuid, user_id: Uuid) -> CoreResult<Album> {
|
||||
self.auth_service
|
||||
.check_permission(user_id, Permission::ViewAlbum(album_id))
|
||||
.check_permission(Some(user_id), Permission::ViewAlbum(album_id))
|
||||
.await?;
|
||||
|
||||
let album = self
|
||||
@@ -71,12 +71,12 @@ impl AlbumService for AlbumServiceImpl {
|
||||
|
||||
async fn add_media_to_album(&self, data: AddMediaToAlbumData, user_id: Uuid) -> CoreResult<()> {
|
||||
self.auth_service
|
||||
.check_permission(user_id, Permission::AddToAlbum(data.album_id))
|
||||
.check_permission(Some(user_id), Permission::AddToAlbum(data.album_id))
|
||||
.await?;
|
||||
|
||||
for media_id in &data.media_ids {
|
||||
self.auth_service
|
||||
.check_permission(*media_id, Permission::ViewMedia(*media_id))
|
||||
.check_permission(Some(user_id), Permission::ViewMedia(*media_id))
|
||||
.await?;
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ impl AlbumService for AlbumServiceImpl {
|
||||
|
||||
async fn share_album(&self, data: ShareAlbumData, owner_id: Uuid) -> CoreResult<()> {
|
||||
self.auth_service
|
||||
.check_permission(owner_id, Permission::ShareAlbum(data.album_id))
|
||||
.check_permission(Some(owner_id), Permission::ShareAlbum(data.album_id))
|
||||
.await?;
|
||||
|
||||
if data.target_user_id == owner_id {
|
||||
@@ -112,7 +112,7 @@ impl AlbumService for AlbumServiceImpl {
|
||||
data: UpdateAlbumData<'_>,
|
||||
) -> CoreResult<Album> {
|
||||
self.auth_service
|
||||
.check_permission(user_id, Permission::EditAlbum(album_id))
|
||||
.check_permission(Some(user_id), Permission::EditAlbum(album_id))
|
||||
.await?;
|
||||
|
||||
let mut album = self
|
||||
@@ -150,9 +150,24 @@ impl AlbumService for AlbumServiceImpl {
|
||||
|
||||
async fn delete_album(&self, album_id: Uuid, user_id: Uuid) -> CoreResult<()> {
|
||||
self.auth_service
|
||||
.check_permission(user_id, Permission::DeleteAlbum(album_id))
|
||||
.check_permission(Some(user_id), Permission::DeleteAlbum(album_id))
|
||||
.await?;
|
||||
|
||||
self.album_repo.delete(album_id).await
|
||||
}
|
||||
|
||||
async fn get_public_album_bundle(&self, album_id: Uuid) -> CoreResult<PublicAlbumBundle> {
|
||||
let album = self
|
||||
.album_repo
|
||||
.find_by_id(album_id)
|
||||
.await?
|
||||
.ok_or(CoreError::NotFound("Album".to_string(), album_id))?;
|
||||
|
||||
if !album.is_public {
|
||||
return Err(CoreError::Auth("Album is not public".to_string()));
|
||||
}
|
||||
|
||||
let media = self.album_repo.list_media_by_album_id(album_id).await?;
|
||||
Ok(PublicAlbumBundle { album, media })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,27 +107,46 @@ impl AuthorizationServiceImpl {
|
||||
|
||||
#[async_trait]
|
||||
impl AuthorizationService for AuthorizationServiceImpl {
|
||||
async fn check_permission(&self, user_id: Uuid, permission: Permission) -> CoreResult<()> {
|
||||
let user = self.get_user(user_id).await?;
|
||||
async fn check_permission(
|
||||
&self,
|
||||
user_id: Option<Uuid>,
|
||||
permission: Permission,
|
||||
) -> CoreResult<()> {
|
||||
let user = if let Some(id) = user_id {
|
||||
Some(self.get_user(id).await?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if authz::is_admin(&user) {
|
||||
return Ok(());
|
||||
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 authz::is_owner(user_id, &media) {
|
||||
|
||||
if self.album_repo.is_media_in_public_album(media_id).await? {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let is_shared = self
|
||||
.album_share_repo
|
||||
.is_media_in_shared_album(media_id, user_id)
|
||||
.await?;
|
||||
if let Some(id) = user_id {
|
||||
if authz::is_owner(id, &media) {
|
||||
// [cite: 117]
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if is_shared {
|
||||
return Ok(());
|
||||
if self
|
||||
.album_share_repo
|
||||
.is_media_in_shared_album(media_id, id)
|
||||
.await?
|
||||
{
|
||||
// [cite: 118-119]
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
Err(CoreError::Auth(
|
||||
@@ -136,6 +155,9 @@ impl AuthorizationService for AuthorizationServiceImpl {
|
||||
}
|
||||
|
||||
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(());
|
||||
@@ -149,6 +171,9 @@ impl AuthorizationService for AuthorizationServiceImpl {
|
||||
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) {
|
||||
@@ -170,6 +195,9 @@ impl AuthorizationService for AuthorizationServiceImpl {
|
||||
}
|
||||
|
||||
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?;
|
||||
@@ -184,6 +212,9 @@ impl AuthorizationService for AuthorizationServiceImpl {
|
||||
}
|
||||
|
||||
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?;
|
||||
|
||||
@@ -197,6 +228,9 @@ impl AuthorizationService for AuthorizationServiceImpl {
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -209,6 +243,9 @@ impl AuthorizationService for AuthorizationServiceImpl {
|
||||
}
|
||||
|
||||
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?;
|
||||
|
||||
@@ -224,6 +261,9 @@ impl AuthorizationService for AuthorizationServiceImpl {
|
||||
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) {
|
||||
@@ -236,6 +276,9 @@ impl AuthorizationService for AuthorizationServiceImpl {
|
||||
}
|
||||
|
||||
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?;
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ impl MediaService for MediaServiceImpl {
|
||||
Ok(media)
|
||||
}
|
||||
|
||||
async fn get_media_details(&self, id: Uuid, user_id: Uuid) -> CoreResult<MediaBundle> {
|
||||
async fn get_media_details(&self, id: Uuid, user_id: Option<Uuid>) -> CoreResult<MediaBundle> {
|
||||
self.auth_service
|
||||
.check_permission(user_id, authz::Permission::ViewMedia(id))
|
||||
.await?;
|
||||
@@ -113,7 +113,7 @@ impl MediaService for MediaServiceImpl {
|
||||
self.repo.list_by_user(user_id, &options).await
|
||||
}
|
||||
|
||||
async fn get_media_filepath(&self, id: Uuid, user_id: Uuid) -> CoreResult<String> {
|
||||
async fn get_media_filepath(&self, id: Uuid, user_id: Option<Uuid>) -> CoreResult<String> {
|
||||
self.auth_service
|
||||
.check_permission(user_id, authz::Permission::ViewMedia(id))
|
||||
.await?;
|
||||
@@ -129,7 +129,7 @@ impl MediaService for MediaServiceImpl {
|
||||
|
||||
async fn delete_media(&self, id: Uuid, user_id: Uuid) -> CoreResult<()> {
|
||||
self.auth_service
|
||||
.check_permission(user_id, authz::Permission::DeleteMedia(id))
|
||||
.check_permission(Some(user_id), authz::Permission::DeleteMedia(id))
|
||||
.await?;
|
||||
|
||||
let media = self
|
||||
|
||||
@@ -59,7 +59,7 @@ impl PersonService for PersonServiceImpl {
|
||||
|
||||
async fn get_person(&self, person_id: Uuid, user_id: Uuid) -> CoreResult<Person> {
|
||||
self.auth_service
|
||||
.check_permission(user_id, authz::Permission::ViewPerson(person_id))
|
||||
.check_permission(Some(user_id), authz::Permission::ViewPerson(person_id))
|
||||
.await?;
|
||||
|
||||
self.person_repo
|
||||
@@ -93,7 +93,7 @@ impl PersonService for PersonServiceImpl {
|
||||
user_id: Uuid,
|
||||
) -> CoreResult<Person> {
|
||||
self.auth_service
|
||||
.check_permission(user_id, authz::Permission::EditPerson(person_id))
|
||||
.check_permission(Some(user_id), authz::Permission::EditPerson(person_id))
|
||||
.await?;
|
||||
|
||||
let mut person = self.get_person(person_id).await?;
|
||||
@@ -105,7 +105,7 @@ impl PersonService for PersonServiceImpl {
|
||||
|
||||
async fn delete_person(&self, person_id: Uuid, user_id: Uuid) -> CoreResult<()> {
|
||||
self.auth_service
|
||||
.check_permission(user_id, authz::Permission::DeletePerson(person_id))
|
||||
.check_permission(Some(user_id), authz::Permission::DeletePerson(person_id))
|
||||
.await?;
|
||||
|
||||
self.person_repo.delete(person_id).await
|
||||
@@ -118,10 +118,10 @@ impl PersonService for PersonServiceImpl {
|
||||
user_id: Uuid,
|
||||
) -> CoreResult<FaceRegion> {
|
||||
self.auth_service
|
||||
.check_permission(user_id, authz::Permission::UsePerson(person_id))
|
||||
.check_permission(Some(user_id), authz::Permission::UsePerson(person_id))
|
||||
.await?;
|
||||
self.auth_service
|
||||
.check_permission(user_id, authz::Permission::AssignFace(face_region_id))
|
||||
.check_permission(Some(user_id), authz::Permission::AssignFace(face_region_id))
|
||||
.await?;
|
||||
|
||||
let mut face =
|
||||
@@ -147,7 +147,7 @@ impl PersonService for PersonServiceImpl {
|
||||
user_id: Uuid,
|
||||
) -> CoreResult<Vec<FaceRegion>> {
|
||||
self.auth_service
|
||||
.check_permission(user_id, authz::Permission::ViewFaces(media_id))
|
||||
.check_permission(Some(user_id), authz::Permission::ViewFaces(media_id))
|
||||
.await?;
|
||||
|
||||
self.face_repo.find_by_media_id(media_id).await
|
||||
@@ -161,7 +161,7 @@ impl PersonService for PersonServiceImpl {
|
||||
owner_id: Uuid,
|
||||
) -> CoreResult<()> {
|
||||
self.auth_service
|
||||
.check_permission(owner_id, authz::Permission::SharePerson(person_id))
|
||||
.check_permission(Some(owner_id), authz::Permission::SharePerson(person_id))
|
||||
.await?;
|
||||
|
||||
self.person_share_repo
|
||||
@@ -176,7 +176,7 @@ impl PersonService for PersonServiceImpl {
|
||||
owner_id: Uuid,
|
||||
) -> CoreResult<()> {
|
||||
self.auth_service
|
||||
.check_permission(owner_id, authz::Permission::SharePerson(person_id))
|
||||
.check_permission(Some(owner_id), authz::Permission::SharePerson(person_id))
|
||||
.await?;
|
||||
|
||||
self.person_share_repo
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use libertas_core::{authz::Permission, error::CoreResult, models::Tag, repositories::TagRepository, services::{AuthorizationService, TagService}};
|
||||
use libertas_core::{
|
||||
authz::Permission,
|
||||
error::CoreResult,
|
||||
models::Tag,
|
||||
repositories::TagRepository,
|
||||
services::{AuthorizationService, TagService},
|
||||
};
|
||||
use uuid::Uuid;
|
||||
|
||||
pub struct TagServiceImpl {
|
||||
@@ -28,9 +34,10 @@ impl TagService for TagServiceImpl {
|
||||
media_id: Uuid,
|
||||
tag_names: &[String],
|
||||
user_id: Uuid,
|
||||
|
||||
) -> CoreResult<Vec<Tag>> {
|
||||
self.auth_service.check_permission(user_id, Permission::AddTags(media_id)).await?;
|
||||
self.auth_service
|
||||
.check_permission(Some(user_id), Permission::AddTags(media_id))
|
||||
.await?;
|
||||
|
||||
let mut tag_ids = Vec::new();
|
||||
let tags = self.tag_repo.find_or_create_tags(tag_names).await?;
|
||||
@@ -49,7 +56,9 @@ impl TagService for TagServiceImpl {
|
||||
tag_names: &[String],
|
||||
user_id: Uuid,
|
||||
) -> CoreResult<()> {
|
||||
self.auth_service.check_permission(user_id, Permission::RemoveTags(media_id)).await?;
|
||||
self.auth_service
|
||||
.check_permission(Some(user_id), Permission::RemoveTags(media_id))
|
||||
.await?;
|
||||
|
||||
let tags = self.tag_repo.find_or_create_tags(tag_names).await?;
|
||||
let mut tag_ids = Vec::new();
|
||||
@@ -57,20 +66,20 @@ impl TagService for TagServiceImpl {
|
||||
tag_ids.push(tag.id);
|
||||
}
|
||||
|
||||
self.tag_repo.remove_tags_from_media(media_id, &tag_ids).await?;
|
||||
self.tag_repo
|
||||
.remove_tags_from_media(media_id, &tag_ids)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn list_tags_for_media(
|
||||
&self,
|
||||
media_id: Uuid,
|
||||
user_id: Uuid,
|
||||
) -> CoreResult<Vec<Tag>> {
|
||||
self.auth_service.check_permission(user_id, Permission::ViewMedia(media_id)).await?;
|
||||
async fn list_tags_for_media(&self, media_id: Uuid, user_id: Uuid) -> CoreResult<Vec<Tag>> {
|
||||
self.auth_service
|
||||
.check_permission(Some(user_id), Permission::ViewMedia(media_id))
|
||||
.await?;
|
||||
|
||||
let tags = self.tag_repo.list_tags_for_media(media_id).await?;
|
||||
|
||||
Ok(tags)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user