feat(drive): ensure drive_id updated on file|folder moved to another drive

This commit is contained in:
Edouard Vanbelle
2026-06-29 21:11:40 +02:00
parent acf2311cea
commit ee92d365b9
7 changed files with 506 additions and 42 deletions
+22 -2
View File
@@ -558,11 +558,13 @@ impl FolderUseCase for FolderService {
// TODO: full descendant-cycle check (moving a folder into one of its own descendants)
}
// D5 `forbid_cross_drive_move`: refuse when src and dst sit in
// different drives and the source drive's policy is on.
// D5 `forbid_cross_drive_move` + D6 `resource.moved_between_drives`
// audit share the same src/dst lookup. Gate before the move,
// audit after a successful move when the two drives differ.
// Skipped for parent_id=None (root namespace, same-drive
// semantics) and when drive_repo isn't wired (stubs/tests) —
// same shape as `move_file_with_perms`.
let mut cross_drive: Option<(Uuid, Uuid)> = None;
if let Some(drive_repo) = &self.drive_repo
&& let Some(parent_id) = &dto.parent_id
{
@@ -592,6 +594,7 @@ impl FolderUseCase for FolderService {
dst_drive_id,
},
)?;
cross_drive = Some((src_drive_id, dst_drive_id));
}
}
@@ -607,6 +610,23 @@ impl FolderUseCase for FolderService {
)
})?;
// D6 audit: only emit when the move crossed a drive boundary.
// The cascade trigger has already propagated drive_id to the
// subtree at this point (see migration
// `20260807000000_cascade_drive_id_on_folder_move.sql`).
if let Some((src_drive_id, dst_drive_id)) = cross_drive {
tracing::info!(
target: "audit",
event = "resource.moved_between_drives",
resource_type = "folder",
resource_id = %folder.id(),
src_drive_id = %src_drive_id,
dst_drive_id = %dst_drive_id,
by = %caller_id,
"📦 folder moved between drives",
);
}
Ok(FolderDto::from(folder))
}