feat(authz): check permission on read handlers + check create permission on folder
This commit is contained in:
@@ -36,12 +36,25 @@ pub trait AuthorizationEngine: Send + Sync + 'static {
|
||||
resource: Resource,
|
||||
) -> Result<(), DomainError> {
|
||||
if self.check(subject, permission, resource).await? {
|
||||
tracing::debug!(
|
||||
"👮🏻♂️ perms: ✔ Subject '{}' has permission to '{}' on resource '{}'",
|
||||
subject,
|
||||
permission,
|
||||
resource
|
||||
);
|
||||
Ok(())
|
||||
} else {
|
||||
let (kind, id) = match resource {
|
||||
Resource::Folder(id) => ("Folder", id),
|
||||
Resource::File(id) => ("File", id),
|
||||
};
|
||||
// log it for audit
|
||||
tracing::info!(
|
||||
"👮🏻♂️ perms: ⛔ Subject '{}' hasn't permission to '{}' on resource '{}'",
|
||||
subject,
|
||||
permission,
|
||||
resource
|
||||
);
|
||||
Err(DomainError::not_found(kind, id.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ use crate::application::services::file_management_service::FileManagementService
|
||||
use crate::application::services::file_retrieval_service::FileRetrievalService;
|
||||
use crate::application::services::file_upload_service::FileUploadService;
|
||||
use crate::common::errors::DomainError;
|
||||
use crate::domain::services::authorization::Permission;
|
||||
|
||||
// ─────────────────────────────────────────────────────
|
||||
// Upload port
|
||||
@@ -119,7 +120,7 @@ pub trait FileRetrievalUseCase: Send + Sync + 'static {
|
||||
///
|
||||
/// Returns `NotFound` if the file does not exist **or** belongs to
|
||||
/// another user. All user-facing handlers should use this method.
|
||||
async fn get_file_owned(&self, id: &str, caller_id: Uuid) -> Result<FileDto, DomainError>;
|
||||
async fn get_file_with_perms(&self, id: &str, caller_id: Uuid) -> Result<FileDto, DomainError>;
|
||||
|
||||
/// Gets a file by its path (for WebDAV)
|
||||
async fn get_file_by_path(&self, path: &str) -> Result<FileDto, DomainError>;
|
||||
@@ -131,7 +132,7 @@ pub trait FileRetrievalUseCase: Send + Sync + 'static {
|
||||
///
|
||||
/// Uses SQL-level `AND user_id` filtering — no in-memory post-filter.
|
||||
/// All user-facing list handlers should use this method.
|
||||
async fn list_files_owned(
|
||||
async fn list_files_with_perms(
|
||||
&self,
|
||||
folder_id: Option<&str>,
|
||||
owner_id: Uuid,
|
||||
@@ -144,7 +145,7 @@ pub trait FileRetrievalUseCase: Send + Sync + 'static {
|
||||
) -> Result<Box<dyn Stream<Item = Result<Bytes, std::io::Error>> + Send>, DomainError>;
|
||||
|
||||
/// Gets file content as a stream, enforcing that `caller_id` is the owner.
|
||||
async fn get_file_stream_owned(
|
||||
async fn get_file_stream_with_perms(
|
||||
&self,
|
||||
id: &str,
|
||||
caller_id: Uuid,
|
||||
@@ -166,7 +167,7 @@ pub trait FileRetrievalUseCase: Send + Sync + 'static {
|
||||
///
|
||||
/// Verifies `caller_id` owns the file before returning content.
|
||||
/// All user-facing download handlers should use this.
|
||||
async fn get_file_optimized_owned(
|
||||
async fn get_file_optimized_with_perms(
|
||||
&self,
|
||||
id: &str,
|
||||
caller_id: Uuid,
|
||||
@@ -198,7 +199,7 @@ pub trait FileRetrievalUseCase: Send + Sync + 'static {
|
||||
) -> Result<Box<dyn Stream<Item = Result<Bytes, std::io::Error>> + Send>, DomainError>;
|
||||
|
||||
/// Ownership-scoped range stream — verifies caller owns the file first.
|
||||
async fn get_file_range_stream_owned(
|
||||
async fn get_file_range_stream_with_perms(
|
||||
&self,
|
||||
id: &str,
|
||||
caller_id: Uuid,
|
||||
@@ -238,7 +239,7 @@ pub trait FileRetrievalUseCase: Send + Sync + 'static {
|
||||
///
|
||||
/// Used by streaming WebDAV PROPFIND so that each user only sees their
|
||||
/// own files, even in shared folder_id namespaces.
|
||||
async fn list_files_batch_for_owner(
|
||||
async fn list_files_batch_with_perms(
|
||||
&self,
|
||||
folder_id: Option<&str>,
|
||||
owner_id: Uuid,
|
||||
@@ -256,6 +257,13 @@ pub trait FileRetrievalUseCase: Send + Sync + 'static {
|
||||
|
||||
/// Primary port for file management operations
|
||||
pub trait FileManagementUseCase: Send + Sync + 'static {
|
||||
async fn has_permission(
|
||||
&self,
|
||||
caller_id: Uuid,
|
||||
permission: Permission,
|
||||
file_id: &str,
|
||||
) -> Result<(), DomainError>;
|
||||
|
||||
/// Moves a file, enforcing that `caller_id` is the owner.
|
||||
async fn move_file_with_perms(
|
||||
&self,
|
||||
|
||||
@@ -6,8 +6,16 @@ use crate::application::dtos::folder_dto::{
|
||||
};
|
||||
|
||||
use crate::common::errors::DomainError;
|
||||
use crate::domain::services::authorization::Permission;
|
||||
|
||||
pub trait FolderUseCase: Send + Sync + 'static {
|
||||
async fn has_permission(
|
||||
&self,
|
||||
caller_id: Uuid,
|
||||
permission: Permission,
|
||||
folder_id: &str,
|
||||
) -> Result<(), DomainError>;
|
||||
|
||||
/// Creates a new folder
|
||||
async fn create_folder_with_perms(
|
||||
&self,
|
||||
@@ -36,7 +44,7 @@ pub trait FolderUseCase: Send + Sync + 'static {
|
||||
|
||||
/// Lists folders scoped to a specific owner (for user-facing endpoints).
|
||||
/// At root level, only returns folders belonging to this user.
|
||||
async fn list_folders_for_owner(
|
||||
async fn list_folders_with_perms(
|
||||
&self,
|
||||
parent_id: Option<&str>,
|
||||
owner_id: Uuid,
|
||||
@@ -50,7 +58,7 @@ pub trait FolderUseCase: Send + Sync + 'static {
|
||||
) -> Result<crate::application::dtos::pagination::PaginatedResponseDto<FolderDto>, DomainError>;
|
||||
|
||||
/// Lists folders with pagination, scoped to a specific owner.
|
||||
async fn list_folders_for_owner_paginated(
|
||||
async fn list_folders_paginated_with_perms(
|
||||
&self,
|
||||
parent_id: Option<&str>,
|
||||
owner_id: Uuid,
|
||||
|
||||
Reference in New Issue
Block a user