feat(drive): prepare removal of user_id
this commit changes GET /api/<resources> to return resource caller has access to
this is not anymmore resources users is owner of
This commit is contained in:
@@ -52,7 +52,7 @@ pub struct DriveWithRootName {
|
||||
/// of the root folder via JOIN at read time.
|
||||
pub root_folder_name: String,
|
||||
/// Highest role the calling user holds on this drive (direct OR
|
||||
/// group-mediated). Populated by `list_for_subjects` (which already
|
||||
/// group-mediated). Populated by `list_readable_by` (which already
|
||||
/// JOINs `role_grants` for accessibility, so the role is in scope at
|
||||
/// query time). `None` for repo methods called without a caller
|
||||
/// context (`get_by_id`, `get_by_ids`, `find_default_for_user`,
|
||||
@@ -164,17 +164,17 @@ pub trait DriveRepository: Send + Sync + 'static {
|
||||
}
|
||||
|
||||
/// List drives the caller can read, resolved via `role_grants` for
|
||||
/// `resource_type='drive'`. The caller's group memberships are
|
||||
/// expanded by the engine's `subject_match_set`; that expanded set
|
||||
/// is what this method's `subject_ids` argument carries.
|
||||
/// `resource_type='drive'`. Group memberships (direct + transitive)
|
||||
/// are expanded inline by the `storage.caller_group_ids(caller)`
|
||||
/// SQL function — callers pass only the caller's uuid, no
|
||||
/// expansion ceremony.
|
||||
///
|
||||
/// Returns rows in a stable order: default drive first (if any),
|
||||
/// then by display name. The `/api/drives` handler relies on that
|
||||
/// order for the picker UI without a follow-up sort.
|
||||
async fn list_for_subjects(
|
||||
async fn list_readable_by(
|
||||
&self,
|
||||
subject_types: &[&str],
|
||||
subject_ids: &[Uuid],
|
||||
caller_id: Uuid,
|
||||
) -> Result<Vec<DriveWithRootName>, DriveRepositoryError>;
|
||||
|
||||
/// `true` when the drive holds no live (non-trashed) folders other
|
||||
@@ -193,7 +193,7 @@ pub trait DriveRepository: Send + Sync + 'static {
|
||||
/// List every drive on the system, regardless of caller membership.
|
||||
///
|
||||
/// Used by the admin panel's `GET /api/admin/drives`. Distinct from
|
||||
/// `list_for_subjects` (which filters by `role_grants`) because an
|
||||
/// `list_readable_by` (which filters by `role_grants`) because an
|
||||
/// admin who creates a shared drive for someone else has no grant
|
||||
/// on it — but still needs to see, audit, and manage it. The HTTP
|
||||
/// gate (admin-only middleware) is what makes the unrestricted
|
||||
|
||||
@@ -13,6 +13,17 @@ use crate::domain::entities::folder::Folder;
|
||||
use crate::domain::services::path_service::StoragePath;
|
||||
use uuid::Uuid;
|
||||
|
||||
// NOTE on `caller_role` for the two listing methods below:
|
||||
// We deliberately do NOT compute or return the caller's role per row.
|
||||
// The frontend already fetches `/api/drives` (which surfaces
|
||||
// `caller_role` per drive) and cross-references by `folder.drive_id` —
|
||||
// see `MoveDialog.svelte` and the config/drive page. Adding
|
||||
// `caller_role` to `FolderDto` would either (a) mean redundant
|
||||
// server-side work for a client-side concern the client already
|
||||
// handles, or (b) drag folder-level grant cascades into the query
|
||||
// which is real cost for a rare edge case. Punted; see
|
||||
// `project_caller_role_on_file_folder_dto` memory.
|
||||
|
||||
/// Domain port for folder persistence.
|
||||
///
|
||||
/// Defines the CRUD and management operations required for
|
||||
@@ -51,13 +62,20 @@ pub trait FolderRepository: Send + Sync + 'static {
|
||||
/// Lists folders within a parent folder
|
||||
async fn list_folders(&self, parent_id: Option<&str>) -> Result<Vec<Folder>, DomainError>;
|
||||
|
||||
/// Lists root-level folders owned by a specific user.
|
||||
/// For non-root queries (parent_id is Some), ownership is implicit
|
||||
/// because the parent already belongs to the user.
|
||||
async fn list_folders_by_owner(
|
||||
/// Lists root-level folders the caller can read — scoped through
|
||||
/// drive-membership grants (`role_grants` on `resource_type='drive'`)
|
||||
/// rather than the legacy `folders.user_id` column. Group memberships
|
||||
/// are expanded inline by `storage.caller_group_ids($caller)` in the
|
||||
/// SQL. Closes [[bug-root-folder-listing-legacy-user-id]] — root
|
||||
/// folders admin created for other users but has no role on no
|
||||
/// longer surface in the admin's `GET /api/folders`.
|
||||
///
|
||||
/// Non-root queries (parent_id != None) go through `list_folders`
|
||||
/// with the parent already permission-checked at the service layer,
|
||||
/// so this method carries no `parent_id` parameter.
|
||||
async fn list_root_folders_for_caller(
|
||||
&self,
|
||||
parent_id: Option<&str>,
|
||||
owner_id: Uuid,
|
||||
caller_id: Uuid,
|
||||
) -> Result<Vec<Folder>, DomainError>;
|
||||
|
||||
/// Lists folders with pagination
|
||||
@@ -69,13 +87,12 @@ pub trait FolderRepository: Send + Sync + 'static {
|
||||
include_total: bool,
|
||||
) -> Result<(Vec<Folder>, Option<usize>), DomainError>;
|
||||
|
||||
/// Lists folders with pagination, scoped to a specific owner.
|
||||
/// Combines the owner filtering of `list_folders_by_owner` with
|
||||
/// the pagination of `list_folders_paginated`.
|
||||
async fn list_folders_by_owner_paginated(
|
||||
/// Paginated companion to `list_root_folders_for_caller` — same
|
||||
/// drive-scoped predicate, adds LIMIT/OFFSET + optional
|
||||
/// window-function COUNT.
|
||||
async fn list_root_folders_for_caller_paginated(
|
||||
&self,
|
||||
parent_id: Option<&str>,
|
||||
owner_id: Uuid,
|
||||
caller_id: Uuid,
|
||||
offset: usize,
|
||||
limit: usize,
|
||||
include_total: bool,
|
||||
|
||||
@@ -27,7 +27,7 @@ pub trait TrashRepository: Send + Sync {
|
||||
///
|
||||
/// **Caller contract**: pass only drive UUIDs the caller has
|
||||
/// `Permission::Delete` on (resolved by the service via
|
||||
/// `DriveRepository::list_for_subjects` + role-bundle filter). This
|
||||
/// `DriveRepository::list_readable_by` + role-bundle filter). This
|
||||
/// repository performs no authorization — see
|
||||
/// `TrashService::empty_trash` for the canonical call site.
|
||||
async fn clear_trash(&self, drive_ids: &[Uuid]) -> Result<()>;
|
||||
|
||||
Reference in New Issue
Block a user