security(nextcloud): chroot-aware display paths + recent race fix
strip_chroot_prefix replaces the hardcoded "Personal/" strip
in NC trashbin PROPFIND, OCS unified search, and REPORT
(favorites + search). Handles composed chroots, drops
cross-chroot items instead of surfacing malformed paths, and
fixes the leading-slash mismatch (FolderDto path has '/', DB
paths don't) that silently dropped every NC trashbin item
post-D3. OCS keeps a first-segment fallback (results
legitimately span drives, no single chroot).
uploads_handler switches to nc_to_internal_path(chroot, …)
for the two remaining hardcoded "Personal/" sites, closing
the D1 TODO markers.
RecentService::record_item_access is split from a new
record_item_access_internal (no authz) used by
RecentRecordingHook. Round 1's authz.require widened the
tokio::spawn race past tests/api/recent.hurl step 7; the
internal path skips the redundant Read gate — upstream
_with_perms already enforced it.
Tests: 8 unit tests pin strip_chroot_prefix (leading slash,
composed chroots, sibling-leak rejection, partial-prefix,
empty-chroot). drives_membership.hurl step 21b/22b cover
Editor upload → 201 / Viewer upload → 404 fresh + overwrite
with fixture cleanup at 30c. test_nc_move_copy_delete_trash
K1 pins the actual original-location value.
This commit is contained in:
@@ -29,7 +29,6 @@ use std::time::Duration;
|
||||
use moka::sync::Cache;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::application::ports::recent_ports::RecentItemsUseCase;
|
||||
use crate::application::ports::resource_access_hook::ResourceAccessHook;
|
||||
use crate::application::services::recent_service::RecentService;
|
||||
|
||||
@@ -85,7 +84,16 @@ impl ResourceAccessHook for RecentRecordingHook {
|
||||
let recent = Arc::clone(&self.recent);
|
||||
let (caller_id, file_id) = key;
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = recent.record_item_access(caller_id, &file_id, "file").await {
|
||||
// Fast path: skip the trait's `authz.require(Read, …)`
|
||||
// (upstream `_with_perms` service already gated). The
|
||||
// extra SQL round-trip pushes the upsert past the client's
|
||||
// immediate `GET /api/recent/resources` in
|
||||
// `tests/api/recent.hurl` step 7 — the whole reason for
|
||||
// the internal variant.
|
||||
if let Err(e) = recent
|
||||
.record_item_access_internal(caller_id, &file_id, "file")
|
||||
.await
|
||||
{
|
||||
tracing::warn!(
|
||||
target: "oxicloud::recent",
|
||||
caller_id = %caller_id,
|
||||
|
||||
Reference in New Issue
Block a user