Permissions and ACLs
ScaiDrive's permission model is Discretionary Access Control with inheritance — the same shape as Windows NTFS. Every file and folder can have its own ACL with allow/deny entries granting specific permissions to users and groups. ACLs inherit from the parent unless explicitly broken.
The six permissions#
| Permission | Flag value | Meaning |
|---|---|---|
READ |
1 | View contents, list folder entries, read metadata |
WRITE |
2 | Modify contents, rename, update metadata |
DELETE |
4 | Move to trash |
CREATE |
8 | Create new items inside (folders/files) |
SHARE |
16 | Create external share links |
MANAGE_PERMISSIONS |
32 | Modify the ACL, transfer ownership |
Permissions are modeled as a bitfield. A principal with READ | WRITE = 3 can read and write but not delete. The API represents permissions as arrays of strings in request bodies (["READ", "WRITE"]) and as integer bitfields in effective-permission responses.
The resolution algorithm#
When a user attempts an action, ScaiDrive computes their effective permissions on the target resource:
- Check global admin. If the user has
super_adminortenant_adminrole (database-stored, not JWT-only), grant all permissions. - Check share membership. Determine the user's role in the share (via direct membership or any group they're in).
ownerandadminget all permissions;contributorgetsREAD | WRITE | DELETE | CREATE;readergets onlyREAD. - Apply ACL. If the resource has an ACL, evaluate ACEs:
- Deny entries are collected first — any explicit deny overrides allow.
- Allow entries are OR'd together.
- An ACE matches if its principal is the user, a group the user is in, or
Everyone. - If the ACL is set to inherit from the parent, walk up to the parent folder and include its ACEs.
- Combine. Effective = (share role permissions ∪ allow ACEs) – deny ACEs.
This mirrors NTFS: deny beats allow, explicit beats inherited, and inheritance walks up the tree.
Checking permissions#
To ask "can this user do X on resource Y?":
1 2 3 4 5 | |
1 2 3 4 5 6 | |
1 2 3 4 5 6 7 8 9 | |
Or get the full set of effective permissions in one call:
1 2 3 4 | |
1 2 3 4 5 6 7 8 | |
For UI rendering (showing/hiding buttons), batch checks reduce round trips:
1 2 3 4 5 6 7 8 9 10 | |
ACL entries#
An ACL is a list of ACEs (Access Control Entries). Each ACE has:
| Field | Values |
|---|---|
principal_type |
user, group, everyone |
principal_id |
usr_..., grp_..., or "everyone" |
permissions |
Array of permission names, e.g., ["READ", "WRITE"] |
ace_type |
allow or deny |
inherit_to_children |
true — ACE applies to this resource and descendants. false — applies only here |
inherited |
Server-set. true if this ACE came from an ancestor, false if set directly |
Reading the ACL#
1 2 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | |
Adding an ACE#
1 2 3 4 5 6 7 8 9 10 | |
You need MANAGE_PERMISSIONS on the resource to modify its ACL.
Breaking inheritance#
By default, a resource's ACL is "the parent's ACL plus anything set directly here." To stop inheriting:
1 2 3 4 5 6 7 | |
copy_inherited: true copies the inherited ACEs into this resource as direct entries before breaking — the effective permissions don't change immediately, but further ancestor modifications stop propagating. copy_inherited: false drops inherited rights entirely.
To restore inheritance:
1 2 3 4 | |
Deny overrides allow#
Classic NTFS rule: if any ACE denies a permission, the user doesn't have it, regardless of other allows.
Example: user alice is in group engineering which has WRITE allowed on a folder. A direct deny WRITE ACE on alice overrides — she can't write, even though her group can.
This lets you carve exceptions without restructuring group membership.
Ownership is separate from permissions#
A resource has an owner_id (user or group). The owner has MANAGE_PERMISSIONS implicitly, whether or not it's in the ACL — owners can always change who has access.
Ownership can be transferred via POST /api/v1/permissions/ownership/{resource_type}/{resource_id}/transfer.
Common pitfall: inheritance is tree, not DAG#
Each resource has exactly one parent. ACL inheritance walks up the tree and stops at the share. It doesn't follow move operations — if you move a file to a new folder, the file's inherited ACEs change.
If you need a resource that inherits from multiple sources (rare), break inheritance and set the ACEs explicitly.
What's next#
- Shares and Ownership — the share-level baseline.
- Permissions Reference — all permission endpoints.
- Files Guide — permission checks in action.