fix: critical bugs from deep audit
- Fix copy_files() data loss: implement real copy_file across full stack (FileWritePort, FileManagementUseCase, stubs, service, repository with atomic CTE + dedup ref_count increment, batch_operations caller) - Fix plaintext password in replace_default_admin: hash password via PasswordHasherPort before User::new() - Fix CalendarService hardcoded user_id: unify CalendarUseCase trait with explicit user_id parameter on all methods, remove zombie _for_user duplicates and hardcoded 'current_user_id', update 20 CalDAV handler call sites - Previous session: migrate DedupService to PostgreSQL (storage.blobs), atomic CTEs with compensation for file/folder repository operations
This commit is contained in:
@@ -106,23 +106,33 @@ pub trait CalendarStoragePort: Send + Sync + 'static {
|
||||
) -> Result<Vec<CalendarEventDto>, DomainError>;
|
||||
}
|
||||
|
||||
/// Port for calendar use cases
|
||||
/// Port for calendar use cases.
|
||||
///
|
||||
/// All methods require an explicit `user_id` parameter for authorization.
|
||||
/// The CalDAV protocol handler extracts the user identity from JWT claims
|
||||
/// and passes it through.
|
||||
#[async_trait]
|
||||
pub trait CalendarUseCase: Send + Sync + 'static {
|
||||
// Calendar operations
|
||||
async fn create_calendar(
|
||||
&self,
|
||||
calendar: CreateCalendarDto,
|
||||
user_id: &str,
|
||||
) -> Result<CalendarDto, DomainError>;
|
||||
async fn update_calendar(
|
||||
&self,
|
||||
calendar_id: &str,
|
||||
update: UpdateCalendarDto,
|
||||
user_id: &str,
|
||||
) -> Result<CalendarDto, DomainError>;
|
||||
async fn delete_calendar(&self, calendar_id: &str) -> Result<(), DomainError>;
|
||||
async fn get_calendar(&self, calendar_id: &str) -> Result<CalendarDto, DomainError>;
|
||||
async fn list_my_calendars(&self) -> Result<Vec<CalendarDto>, DomainError>;
|
||||
async fn list_shared_calendars(&self) -> Result<Vec<CalendarDto>, DomainError>;
|
||||
async fn delete_calendar(&self, calendar_id: &str, user_id: &str) -> Result<(), DomainError>;
|
||||
async fn get_calendar(
|
||||
&self,
|
||||
calendar_id: &str,
|
||||
user_id: &str,
|
||||
) -> Result<CalendarDto, DomainError>;
|
||||
async fn list_my_calendars(&self, user_id: &str) -> Result<Vec<CalendarDto>, DomainError>;
|
||||
async fn list_shared_calendars(&self, user_id: &str) -> Result<Vec<CalendarDto>, DomainError>;
|
||||
async fn list_public_calendars(
|
||||
&self,
|
||||
limit: Option<i64>,
|
||||
@@ -133,90 +143,57 @@ pub trait CalendarUseCase: Send + Sync + 'static {
|
||||
async fn share_calendar(
|
||||
&self,
|
||||
calendar_id: &str,
|
||||
user_id: &str,
|
||||
target_user_id: &str,
|
||||
access_level: &str,
|
||||
caller_user_id: &str,
|
||||
) -> Result<(), DomainError>;
|
||||
async fn remove_calendar_sharing(
|
||||
&self,
|
||||
calendar_id: &str,
|
||||
user_id: &str,
|
||||
target_user_id: &str,
|
||||
caller_user_id: &str,
|
||||
) -> Result<(), DomainError>;
|
||||
async fn get_calendar_shares(
|
||||
&self,
|
||||
calendar_id: &str,
|
||||
user_id: &str,
|
||||
) -> Result<Vec<(String, String)>, DomainError>;
|
||||
|
||||
// Event operations
|
||||
async fn create_event(&self, event: CreateEventDto) -> Result<CalendarEventDto, DomainError>;
|
||||
async fn create_event(
|
||||
&self,
|
||||
event: CreateEventDto,
|
||||
user_id: &str,
|
||||
) -> Result<CalendarEventDto, DomainError>;
|
||||
async fn create_event_from_ical(
|
||||
&self,
|
||||
event: CreateEventICalDto,
|
||||
user_id: &str,
|
||||
) -> Result<CalendarEventDto, DomainError>;
|
||||
async fn update_event(
|
||||
&self,
|
||||
event_id: &str,
|
||||
update: UpdateEventDto,
|
||||
user_id: &str,
|
||||
) -> Result<CalendarEventDto, DomainError>;
|
||||
async fn delete_event(&self, event_id: &str, user_id: &str) -> Result<(), DomainError>;
|
||||
async fn get_event(
|
||||
&self,
|
||||
event_id: &str,
|
||||
user_id: &str,
|
||||
) -> Result<CalendarEventDto, DomainError>;
|
||||
async fn delete_event(&self, event_id: &str) -> Result<(), DomainError>;
|
||||
async fn get_event(&self, event_id: &str) -> Result<CalendarEventDto, DomainError>;
|
||||
async fn list_events(
|
||||
&self,
|
||||
calendar_id: &str,
|
||||
limit: Option<i64>,
|
||||
offset: Option<i64>,
|
||||
user_id: &str,
|
||||
) -> Result<Vec<CalendarEventDto>, DomainError>;
|
||||
async fn get_events_in_range(
|
||||
&self,
|
||||
calendar_id: &str,
|
||||
start: DateTime<Utc>,
|
||||
end: DateTime<Utc>,
|
||||
) -> Result<Vec<CalendarEventDto>, DomainError>;
|
||||
|
||||
// ─── User-contextualized variants (for CalDAV protocol handler) ──
|
||||
async fn create_calendar_for_user(
|
||||
&self,
|
||||
calendar: CreateCalendarDto,
|
||||
user_id: &str,
|
||||
) -> Result<CalendarDto, DomainError>;
|
||||
async fn update_calendar_for_user(
|
||||
&self,
|
||||
calendar_id: &str,
|
||||
update: UpdateCalendarDto,
|
||||
user_id: &str,
|
||||
) -> Result<CalendarDto, DomainError>;
|
||||
async fn delete_calendar_for_user(
|
||||
&self,
|
||||
calendar_id: &str,
|
||||
user_id: &str,
|
||||
) -> Result<(), DomainError>;
|
||||
async fn get_calendar_for_user(
|
||||
&self,
|
||||
calendar_id: &str,
|
||||
user_id: &str,
|
||||
) -> Result<CalendarDto, DomainError>;
|
||||
async fn list_my_calendars_for_user(
|
||||
&self,
|
||||
user_id: &str,
|
||||
) -> Result<Vec<CalendarDto>, DomainError>;
|
||||
async fn list_events_for_user(
|
||||
&self,
|
||||
calendar_id: &str,
|
||||
limit: Option<i64>,
|
||||
offset: Option<i64>,
|
||||
user_id: &str,
|
||||
) -> Result<Vec<CalendarEventDto>, DomainError>;
|
||||
async fn get_events_in_range_for_user(
|
||||
&self,
|
||||
calendar_id: &str,
|
||||
start: DateTime<Utc>,
|
||||
end: DateTime<Utc>,
|
||||
user_id: &str,
|
||||
) -> Result<Vec<CalendarEventDto>, DomainError>;
|
||||
async fn create_event_from_ical_for_user(
|
||||
&self,
|
||||
event: CreateEventICalDto,
|
||||
user_id: &str,
|
||||
) -> Result<CalendarEventDto, DomainError>;
|
||||
async fn delete_event_for_user(&self, event_id: &str, user_id: &str)
|
||||
-> Result<(), DomainError>;
|
||||
}
|
||||
|
||||
@@ -138,6 +138,13 @@ pub trait FileManagementUseCase: Send + Sync + 'static {
|
||||
folder_id: Option<String>,
|
||||
) -> Result<FileDto, DomainError>;
|
||||
|
||||
/// Copies a file to another folder (zero-copy with dedup).
|
||||
async fn copy_file(
|
||||
&self,
|
||||
file_id: &str,
|
||||
target_folder_id: Option<String>,
|
||||
) -> Result<FileDto, DomainError>;
|
||||
|
||||
/// Renames a file
|
||||
async fn rename_file(&self, file_id: &str, new_name: &str) -> Result<FileDto, DomainError>;
|
||||
|
||||
|
||||
@@ -115,6 +115,16 @@ pub trait FileWritePort: Send + Sync + 'static {
|
||||
size: u64,
|
||||
) -> Result<(File, PathBuf), DomainError>;
|
||||
|
||||
/// Copies a file to a (possibly different) folder.
|
||||
///
|
||||
/// With blob-dedup, this only creates a new metadata row and increments
|
||||
/// the blob reference count — zero disk I/O for the content.
|
||||
async fn copy_file(
|
||||
&self,
|
||||
file_id: &str,
|
||||
target_folder_id: Option<String>,
|
||||
) -> Result<File, DomainError>;
|
||||
|
||||
// ── Trash operations ──
|
||||
|
||||
/// Moves a file to the trash
|
||||
|
||||
@@ -168,7 +168,7 @@ impl AuthApplicationService {
|
||||
/// Returns whether OIDC is configured and enabled
|
||||
pub fn oidc_enabled(&self) -> bool {
|
||||
let state = self.oidc.read().unwrap();
|
||||
state.service.is_some() && state.config.as_ref().is_some_and(|c| c.enabled)
|
||||
state.service.is_some() && state.config.as_ref().map_or(false, |c| c.enabled)
|
||||
}
|
||||
|
||||
/// Returns whether password login is disabled (OIDC-only mode)
|
||||
@@ -177,7 +177,7 @@ impl AuthApplicationService {
|
||||
state
|
||||
.config
|
||||
.as_ref()
|
||||
.is_some_and(|c| c.disable_password_login)
|
||||
.map_or(false, |c| c.disable_password_login)
|
||||
}
|
||||
|
||||
/// Returns a clone of the OIDC config if available
|
||||
@@ -664,11 +664,23 @@ impl AuthApplicationService {
|
||||
// Admin quota, capped to available disk space
|
||||
let admin_quota = self.capped_quota(&admin_role);
|
||||
|
||||
// Hash the password (same as register / admin_create_user)
|
||||
let password_hash = self
|
||||
.password_hasher
|
||||
.hash_password(&dto.password)
|
||||
.map_err(|e| {
|
||||
DomainError::new(
|
||||
ErrorKind::InternalError,
|
||||
"User",
|
||||
format!("Error hashing password: {}", e),
|
||||
)
|
||||
})?;
|
||||
|
||||
// Create the new admin user
|
||||
let user = User::new(
|
||||
dto.username.clone(),
|
||||
dto.email.clone(),
|
||||
dto.password.clone(),
|
||||
password_hash,
|
||||
admin_role,
|
||||
admin_quota,
|
||||
)
|
||||
|
||||
@@ -129,7 +129,7 @@ impl BatchOperationService {
|
||||
// Acquire semaphore permit
|
||||
let permit = semaphore.acquire().await.unwrap();
|
||||
|
||||
let copy_result = mgmt.move_file(&file_id, target_folder.clone()).await;
|
||||
let copy_result = mgmt.copy_file(&file_id, target_folder.clone()).await;
|
||||
|
||||
// Release the permit explicitly (also released on drop)
|
||||
drop(permit);
|
||||
|
||||
@@ -24,13 +24,8 @@ impl CalendarUseCase for CalendarService {
|
||||
async fn create_calendar(
|
||||
&self,
|
||||
calendar: CreateCalendarDto,
|
||||
user_id: &str,
|
||||
) -> Result<CalendarDto, DomainError> {
|
||||
// This function requires the current user context which will come from middleware
|
||||
// For now, we'll use a dummy implementation that needs to be completed
|
||||
|
||||
// In a real implementation, get user_id from current user context
|
||||
let user_id = "current_user_id"; // This should come from middleware
|
||||
|
||||
self.calendar_storage
|
||||
.create_calendar(calendar, user_id)
|
||||
.await
|
||||
@@ -40,20 +35,12 @@ impl CalendarUseCase for CalendarService {
|
||||
&self,
|
||||
calendar_id: &str,
|
||||
update: UpdateCalendarDto,
|
||||
user_id: &str,
|
||||
) -> Result<CalendarDto, DomainError> {
|
||||
// In a real implementation, we would:
|
||||
// 1. Get the current user ID from middleware
|
||||
// 2. Verify that the user has access to this calendar
|
||||
// 3. Update the calendar if they have permission
|
||||
|
||||
let user_id = "current_user_id"; // This should come from middleware
|
||||
|
||||
// Check if user has access
|
||||
let has_access = self
|
||||
.calendar_storage
|
||||
.check_calendar_access(calendar_id, user_id)
|
||||
.await?;
|
||||
|
||||
if !has_access {
|
||||
return Err(DomainError::new(
|
||||
ErrorKind::AccessDenied,
|
||||
@@ -61,21 +48,16 @@ impl CalendarUseCase for CalendarService {
|
||||
"You don't have permission to update this calendar",
|
||||
));
|
||||
}
|
||||
|
||||
self.calendar_storage
|
||||
.update_calendar(calendar_id, update)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn delete_calendar(&self, calendar_id: &str) -> Result<(), DomainError> {
|
||||
let user_id = "current_user_id"; // This should come from middleware
|
||||
|
||||
// Check if user has access
|
||||
async fn delete_calendar(&self, calendar_id: &str, user_id: &str) -> Result<(), DomainError> {
|
||||
let has_access = self
|
||||
.calendar_storage
|
||||
.check_calendar_access(calendar_id, user_id)
|
||||
.await?;
|
||||
|
||||
if !has_access {
|
||||
return Err(DomainError::new(
|
||||
ErrorKind::AccessDenied,
|
||||
@@ -83,22 +65,19 @@ impl CalendarUseCase for CalendarService {
|
||||
"You don't have permission to delete this calendar",
|
||||
));
|
||||
}
|
||||
|
||||
self.calendar_storage.delete_calendar(calendar_id).await
|
||||
}
|
||||
|
||||
async fn get_calendar(&self, calendar_id: &str) -> Result<CalendarDto, DomainError> {
|
||||
let user_id = "current_user_id"; // This should come from middleware
|
||||
|
||||
// Get the calendar
|
||||
async fn get_calendar(
|
||||
&self,
|
||||
calendar_id: &str,
|
||||
user_id: &str,
|
||||
) -> Result<CalendarDto, DomainError> {
|
||||
let calendar = self.calendar_storage.get_calendar(calendar_id).await?;
|
||||
|
||||
// Check if user has access or if calendar is public
|
||||
let has_access = self
|
||||
.calendar_storage
|
||||
.check_calendar_access(calendar_id, user_id)
|
||||
.await?;
|
||||
|
||||
if !has_access && !calendar.is_public {
|
||||
return Err(DomainError::new(
|
||||
ErrorKind::AccessDenied,
|
||||
@@ -106,19 +85,14 @@ impl CalendarUseCase for CalendarService {
|
||||
"You don't have permission to view this calendar",
|
||||
));
|
||||
}
|
||||
|
||||
Ok(calendar)
|
||||
}
|
||||
|
||||
async fn list_my_calendars(&self) -> Result<Vec<CalendarDto>, DomainError> {
|
||||
let user_id = "current_user_id"; // This should come from middleware
|
||||
|
||||
async fn list_my_calendars(&self, user_id: &str) -> Result<Vec<CalendarDto>, DomainError> {
|
||||
self.calendar_storage.list_calendars_by_owner(user_id).await
|
||||
}
|
||||
|
||||
async fn list_shared_calendars(&self) -> Result<Vec<CalendarDto>, DomainError> {
|
||||
let user_id = "current_user_id"; // This should come from middleware
|
||||
|
||||
async fn list_shared_calendars(&self, user_id: &str) -> Result<Vec<CalendarDto>, DomainError> {
|
||||
self.calendar_storage
|
||||
.list_calendars_shared_with_user(user_id)
|
||||
.await
|
||||
@@ -131,7 +105,6 @@ impl CalendarUseCase for CalendarService {
|
||||
) -> Result<Vec<CalendarDto>, DomainError> {
|
||||
let limit = limit.unwrap_or(100);
|
||||
let offset = offset.unwrap_or(0);
|
||||
|
||||
self.calendar_storage
|
||||
.list_public_calendars(limit, offset)
|
||||
.await
|
||||
@@ -140,24 +113,18 @@ impl CalendarUseCase for CalendarService {
|
||||
async fn share_calendar(
|
||||
&self,
|
||||
calendar_id: &str,
|
||||
user_id: &str,
|
||||
target_user_id: &str,
|
||||
access_level: &str,
|
||||
caller_user_id: &str,
|
||||
) -> Result<(), DomainError> {
|
||||
let current_user_id = "current_user_id"; // This should come from middleware
|
||||
|
||||
// Check if current user has access
|
||||
let calendar = self.calendar_storage.get_calendar(calendar_id).await?;
|
||||
|
||||
// Only the owner can share the calendar
|
||||
if calendar.owner_id != current_user_id {
|
||||
if calendar.owner_id != caller_user_id {
|
||||
return Err(DomainError::new(
|
||||
ErrorKind::AccessDenied,
|
||||
"Calendar",
|
||||
"Only the calendar owner can change sharing settings",
|
||||
));
|
||||
}
|
||||
|
||||
// Validate access_level
|
||||
match access_level {
|
||||
"read" | "write" | "owner" => {}
|
||||
_ => {
|
||||
@@ -171,66 +138,55 @@ impl CalendarUseCase for CalendarService {
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
self.calendar_storage
|
||||
.share_calendar(calendar_id, user_id, access_level)
|
||||
.share_calendar(calendar_id, target_user_id, access_level)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn remove_calendar_sharing(
|
||||
&self,
|
||||
calendar_id: &str,
|
||||
user_id: &str,
|
||||
target_user_id: &str,
|
||||
caller_user_id: &str,
|
||||
) -> Result<(), DomainError> {
|
||||
let current_user_id = "current_user_id"; // This should come from middleware
|
||||
|
||||
// Check if current user has access
|
||||
let calendar = self.calendar_storage.get_calendar(calendar_id).await?;
|
||||
|
||||
// Only the owner can change sharing settings
|
||||
if calendar.owner_id != current_user_id {
|
||||
if calendar.owner_id != caller_user_id {
|
||||
return Err(DomainError::new(
|
||||
ErrorKind::AccessDenied,
|
||||
"Calendar",
|
||||
"Only the calendar owner can change sharing settings",
|
||||
));
|
||||
}
|
||||
|
||||
self.calendar_storage
|
||||
.remove_calendar_sharing(calendar_id, user_id)
|
||||
.remove_calendar_sharing(calendar_id, target_user_id)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_calendar_shares(
|
||||
&self,
|
||||
calendar_id: &str,
|
||||
user_id: &str,
|
||||
) -> Result<Vec<(String, String)>, DomainError> {
|
||||
let current_user_id = "current_user_id"; // This should come from middleware
|
||||
|
||||
// Check if current user has access
|
||||
let calendar = self.calendar_storage.get_calendar(calendar_id).await?;
|
||||
|
||||
// Only the owner can view sharing settings
|
||||
if calendar.owner_id != current_user_id {
|
||||
if calendar.owner_id != user_id {
|
||||
return Err(DomainError::new(
|
||||
ErrorKind::AccessDenied,
|
||||
"Calendar",
|
||||
"Only the calendar owner can view sharing settings",
|
||||
));
|
||||
}
|
||||
|
||||
self.calendar_storage.get_calendar_shares(calendar_id).await
|
||||
}
|
||||
|
||||
async fn create_event(&self, event: CreateEventDto) -> Result<CalendarEventDto, DomainError> {
|
||||
let user_id = "current_user_id"; // This should come from middleware
|
||||
|
||||
// Check if user has access to the calendar
|
||||
async fn create_event(
|
||||
&self,
|
||||
event: CreateEventDto,
|
||||
user_id: &str,
|
||||
) -> Result<CalendarEventDto, DomainError> {
|
||||
let has_access = self
|
||||
.calendar_storage
|
||||
.check_calendar_access(&event.calendar_id, user_id)
|
||||
.await?;
|
||||
|
||||
if !has_access {
|
||||
return Err(DomainError::new(
|
||||
ErrorKind::AccessDenied,
|
||||
@@ -238,22 +194,18 @@ impl CalendarUseCase for CalendarService {
|
||||
"You don't have permission to add events to this calendar",
|
||||
));
|
||||
}
|
||||
|
||||
self.calendar_storage.create_event(event).await
|
||||
}
|
||||
|
||||
async fn create_event_from_ical(
|
||||
&self,
|
||||
event: CreateEventICalDto,
|
||||
user_id: &str,
|
||||
) -> Result<CalendarEventDto, DomainError> {
|
||||
let user_id = "current_user_id"; // This should come from middleware
|
||||
|
||||
// Check if user has access to the calendar
|
||||
let has_access = self
|
||||
.calendar_storage
|
||||
.check_calendar_access(&event.calendar_id, user_id)
|
||||
.await?;
|
||||
|
||||
if !has_access {
|
||||
return Err(DomainError::new(
|
||||
ErrorKind::AccessDenied,
|
||||
@@ -261,7 +213,6 @@ impl CalendarUseCase for CalendarService {
|
||||
"You don't have permission to add events to this calendar",
|
||||
));
|
||||
}
|
||||
|
||||
self.calendar_storage.create_event_from_ical(event).await
|
||||
}
|
||||
|
||||
@@ -269,18 +220,13 @@ impl CalendarUseCase for CalendarService {
|
||||
&self,
|
||||
event_id: &str,
|
||||
update: UpdateEventDto,
|
||||
user_id: &str,
|
||||
) -> Result<CalendarEventDto, DomainError> {
|
||||
let user_id = "current_user_id"; // This should come from middleware
|
||||
|
||||
// Get the event to find its calendar
|
||||
let event = self.calendar_storage.get_event(event_id).await?;
|
||||
|
||||
// Check if user has access to the calendar
|
||||
let has_access = self
|
||||
.calendar_storage
|
||||
.check_calendar_access(&event.calendar_id, user_id)
|
||||
.await?;
|
||||
|
||||
if !has_access {
|
||||
return Err(DomainError::new(
|
||||
ErrorKind::AccessDenied,
|
||||
@@ -288,22 +234,15 @@ impl CalendarUseCase for CalendarService {
|
||||
"You don't have permission to update events in this calendar",
|
||||
));
|
||||
}
|
||||
|
||||
self.calendar_storage.update_event(event_id, update).await
|
||||
}
|
||||
|
||||
async fn delete_event(&self, event_id: &str) -> Result<(), DomainError> {
|
||||
let user_id = "current_user_id"; // This should come from middleware
|
||||
|
||||
// Get the event to find its calendar
|
||||
async fn delete_event(&self, event_id: &str, user_id: &str) -> Result<(), DomainError> {
|
||||
let event = self.calendar_storage.get_event(event_id).await?;
|
||||
|
||||
// Check if user has access to the calendar
|
||||
let has_access = self
|
||||
.calendar_storage
|
||||
.check_calendar_access(&event.calendar_id, user_id)
|
||||
.await?;
|
||||
|
||||
if !has_access {
|
||||
return Err(DomainError::new(
|
||||
ErrorKind::AccessDenied,
|
||||
@@ -311,28 +250,23 @@ impl CalendarUseCase for CalendarService {
|
||||
"You don't have permission to delete events in this calendar",
|
||||
));
|
||||
}
|
||||
|
||||
self.calendar_storage.delete_event(event_id).await
|
||||
}
|
||||
|
||||
async fn get_event(&self, event_id: &str) -> Result<CalendarEventDto, DomainError> {
|
||||
let user_id = "current_user_id"; // This should come from middleware
|
||||
|
||||
// Get the event
|
||||
async fn get_event(
|
||||
&self,
|
||||
event_id: &str,
|
||||
user_id: &str,
|
||||
) -> Result<CalendarEventDto, DomainError> {
|
||||
let event = self.calendar_storage.get_event(event_id).await?;
|
||||
|
||||
// Check if user has access to the calendar
|
||||
let has_access = self
|
||||
.calendar_storage
|
||||
.check_calendar_access(&event.calendar_id, user_id)
|
||||
.await?;
|
||||
|
||||
// Check if calendar is public
|
||||
let calendar = self
|
||||
.calendar_storage
|
||||
.get_calendar(&event.calendar_id)
|
||||
.await?;
|
||||
|
||||
if !has_access && !calendar.is_public {
|
||||
return Err(DomainError::new(
|
||||
ErrorKind::AccessDenied,
|
||||
@@ -340,7 +274,6 @@ impl CalendarUseCase for CalendarService {
|
||||
"You don't have permission to view events in this calendar",
|
||||
));
|
||||
}
|
||||
|
||||
Ok(event)
|
||||
}
|
||||
|
||||
@@ -349,18 +282,13 @@ impl CalendarUseCase for CalendarService {
|
||||
calendar_id: &str,
|
||||
limit: Option<i64>,
|
||||
offset: Option<i64>,
|
||||
user_id: &str,
|
||||
) -> Result<Vec<CalendarEventDto>, DomainError> {
|
||||
let user_id = "current_user_id"; // This should come from middleware
|
||||
|
||||
// Check if user has access to the calendar
|
||||
let has_access = self
|
||||
.calendar_storage
|
||||
.check_calendar_access(calendar_id, user_id)
|
||||
.await?;
|
||||
|
||||
// Check if calendar is public
|
||||
let calendar = self.calendar_storage.get_calendar(calendar_id).await?;
|
||||
|
||||
if !has_access && !calendar.is_public {
|
||||
return Err(DomainError::new(
|
||||
ErrorKind::AccessDenied,
|
||||
@@ -368,12 +296,9 @@ impl CalendarUseCase for CalendarService {
|
||||
"You don't have permission to view events in this calendar",
|
||||
));
|
||||
}
|
||||
|
||||
// Use pagination if provided
|
||||
if limit.is_some() || offset.is_some() {
|
||||
let limit = limit.unwrap_or(100);
|
||||
let offset = offset.unwrap_or(0);
|
||||
|
||||
self.calendar_storage
|
||||
.list_events_by_calendar_paginated(calendar_id, limit, offset)
|
||||
.await
|
||||
@@ -389,148 +314,6 @@ impl CalendarUseCase for CalendarService {
|
||||
calendar_id: &str,
|
||||
start: DateTime<Utc>,
|
||||
end: DateTime<Utc>,
|
||||
) -> Result<Vec<CalendarEventDto>, DomainError> {
|
||||
let user_id = "current_user_id"; // This should come from middleware
|
||||
|
||||
// Check if user has access to the calendar
|
||||
let has_access = self
|
||||
.calendar_storage
|
||||
.check_calendar_access(calendar_id, user_id)
|
||||
.await?;
|
||||
|
||||
// Check if calendar is public
|
||||
let calendar = self.calendar_storage.get_calendar(calendar_id).await?;
|
||||
|
||||
if !has_access && !calendar.is_public {
|
||||
return Err(DomainError::new(
|
||||
ErrorKind::AccessDenied,
|
||||
"Calendar",
|
||||
"You don't have permission to view events in this calendar",
|
||||
));
|
||||
}
|
||||
|
||||
self.calendar_storage
|
||||
.get_events_in_time_range(calendar_id, &start, &end)
|
||||
.await
|
||||
}
|
||||
|
||||
// ─── User-contextualized variants (for CalDAV protocol handler) ──
|
||||
|
||||
async fn create_calendar_for_user(
|
||||
&self,
|
||||
calendar: CreateCalendarDto,
|
||||
user_id: &str,
|
||||
) -> Result<CalendarDto, DomainError> {
|
||||
self.calendar_storage
|
||||
.create_calendar(calendar, user_id)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn update_calendar_for_user(
|
||||
&self,
|
||||
calendar_id: &str,
|
||||
update: UpdateCalendarDto,
|
||||
user_id: &str,
|
||||
) -> Result<CalendarDto, DomainError> {
|
||||
let has_access = self
|
||||
.calendar_storage
|
||||
.check_calendar_access(calendar_id, user_id)
|
||||
.await?;
|
||||
if !has_access {
|
||||
return Err(DomainError::new(
|
||||
ErrorKind::AccessDenied,
|
||||
"Calendar",
|
||||
"You don't have permission to update this calendar",
|
||||
));
|
||||
}
|
||||
self.calendar_storage
|
||||
.update_calendar(calendar_id, update)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn delete_calendar_for_user(
|
||||
&self,
|
||||
calendar_id: &str,
|
||||
user_id: &str,
|
||||
) -> Result<(), DomainError> {
|
||||
let has_access = self
|
||||
.calendar_storage
|
||||
.check_calendar_access(calendar_id, user_id)
|
||||
.await?;
|
||||
if !has_access {
|
||||
return Err(DomainError::new(
|
||||
ErrorKind::AccessDenied,
|
||||
"Calendar",
|
||||
"You don't have permission to delete this calendar",
|
||||
));
|
||||
}
|
||||
self.calendar_storage.delete_calendar(calendar_id).await
|
||||
}
|
||||
|
||||
async fn get_calendar_for_user(
|
||||
&self,
|
||||
calendar_id: &str,
|
||||
user_id: &str,
|
||||
) -> Result<CalendarDto, DomainError> {
|
||||
let calendar = self.calendar_storage.get_calendar(calendar_id).await?;
|
||||
let has_access = self
|
||||
.calendar_storage
|
||||
.check_calendar_access(calendar_id, user_id)
|
||||
.await?;
|
||||
if !has_access && !calendar.is_public {
|
||||
return Err(DomainError::new(
|
||||
ErrorKind::AccessDenied,
|
||||
"Calendar",
|
||||
"You don't have permission to view this calendar",
|
||||
));
|
||||
}
|
||||
Ok(calendar)
|
||||
}
|
||||
|
||||
async fn list_my_calendars_for_user(
|
||||
&self,
|
||||
user_id: &str,
|
||||
) -> Result<Vec<CalendarDto>, DomainError> {
|
||||
self.calendar_storage.list_calendars_by_owner(user_id).await
|
||||
}
|
||||
|
||||
async fn list_events_for_user(
|
||||
&self,
|
||||
calendar_id: &str,
|
||||
limit: Option<i64>,
|
||||
offset: Option<i64>,
|
||||
user_id: &str,
|
||||
) -> Result<Vec<CalendarEventDto>, DomainError> {
|
||||
let has_access = self
|
||||
.calendar_storage
|
||||
.check_calendar_access(calendar_id, user_id)
|
||||
.await?;
|
||||
let calendar = self.calendar_storage.get_calendar(calendar_id).await?;
|
||||
if !has_access && !calendar.is_public {
|
||||
return Err(DomainError::new(
|
||||
ErrorKind::AccessDenied,
|
||||
"Calendar",
|
||||
"You don't have permission to view events in this calendar",
|
||||
));
|
||||
}
|
||||
if limit.is_some() || offset.is_some() {
|
||||
let limit = limit.unwrap_or(100);
|
||||
let offset = offset.unwrap_or(0);
|
||||
self.calendar_storage
|
||||
.list_events_by_calendar_paginated(calendar_id, limit, offset)
|
||||
.await
|
||||
} else {
|
||||
self.calendar_storage
|
||||
.list_events_by_calendar(calendar_id)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_events_in_range_for_user(
|
||||
&self,
|
||||
calendar_id: &str,
|
||||
start: DateTime<Utc>,
|
||||
end: DateTime<Utc>,
|
||||
user_id: &str,
|
||||
) -> Result<Vec<CalendarEventDto>, DomainError> {
|
||||
let has_access = self
|
||||
@@ -549,43 +332,4 @@ impl CalendarUseCase for CalendarService {
|
||||
.get_events_in_time_range(calendar_id, &start, &end)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn create_event_from_ical_for_user(
|
||||
&self,
|
||||
event: CreateEventICalDto,
|
||||
user_id: &str,
|
||||
) -> Result<CalendarEventDto, DomainError> {
|
||||
let has_access = self
|
||||
.calendar_storage
|
||||
.check_calendar_access(&event.calendar_id, user_id)
|
||||
.await?;
|
||||
if !has_access {
|
||||
return Err(DomainError::new(
|
||||
ErrorKind::AccessDenied,
|
||||
"Calendar",
|
||||
"You don't have permission to add events to this calendar",
|
||||
));
|
||||
}
|
||||
self.calendar_storage.create_event_from_ical(event).await
|
||||
}
|
||||
|
||||
async fn delete_event_for_user(
|
||||
&self,
|
||||
event_id: &str,
|
||||
user_id: &str,
|
||||
) -> Result<(), DomainError> {
|
||||
let event = self.calendar_storage.get_event(event_id).await?;
|
||||
let has_access = self
|
||||
.calendar_storage
|
||||
.check_calendar_access(&event.calendar_id, user_id)
|
||||
.await?;
|
||||
if !has_access {
|
||||
return Err(DomainError::new(
|
||||
ErrorKind::AccessDenied,
|
||||
"Calendar",
|
||||
"You don't have permission to delete events in this calendar",
|
||||
));
|
||||
}
|
||||
self.calendar_storage.delete_event(event_id).await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,6 +121,35 @@ impl FileManagementUseCase for FileManagementService {
|
||||
Ok(FileDto::from(moved_file))
|
||||
}
|
||||
|
||||
async fn copy_file(
|
||||
&self,
|
||||
file_id: &str,
|
||||
target_folder_id: Option<String>,
|
||||
) -> Result<FileDto, DomainError> {
|
||||
info!(
|
||||
"Copying file with ID: {} to folder: {:?}",
|
||||
file_id, target_folder_id
|
||||
);
|
||||
|
||||
let copied_file = self
|
||||
.file_repository
|
||||
.copy_file(file_id, target_folder_id)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
error!("Error copying file (ID: {}): {}", file_id, e);
|
||||
e
|
||||
})?;
|
||||
|
||||
info!(
|
||||
"File copied successfully: {} (ID: {}) to folder: {:?}",
|
||||
copied_file.name(),
|
||||
copied_file.id(),
|
||||
copied_file.folder_id()
|
||||
);
|
||||
|
||||
Ok(FileDto::from(copied_file))
|
||||
}
|
||||
|
||||
async fn rename_file(&self, file_id: &str, new_name: &str) -> Result<FileDto, DomainError> {
|
||||
info!("Renaming file with ID: {} to \"{}\"", file_id, new_name);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user