Access Control
A deep technical overview of the DMART Access Control system. The system implements a hybrid model combining Role-Based Access Control (RBAC) for broad permissions and Access Control Lists (ACLs) for fine-grained, resource-specific overrides.
Core Architecture
The access control logic is centralized in the PermissionService class. Data models are defined in Dmart.Models.Core.
Key Components
Permission The atomic unit of access. Defines what can be done on which resources under what conditions.
Role A named collection of Permission shortnames.
Group A named collection of Roles. Users are assigned to Groups.
User The actor. Can have direct Roles and inherit Roles from Groups.
ACL Optional list embedded in a Resource that grants specific users specific actions, bypassing standard RBAC.
The Authorization Algorithm
The CanAsync method is the gatekeeper. It evaluates requests based on the following precedence order:
graph TD
A["Request: User, Action, Resource"] --> B{Is Resource Space?}
B -- Yes --> C[Check Space Access]
B -- No --> D{Has Item ACL?}
D -- Yes --> E[Check ACL]
E -- Allowed --> F[Access Granted]
E -- Denied --> G[Continue to RBAC]
D -- No --> G
G --> H[Load User Permissions]
H --> I["Determine Context Conditions (is_active, own)"]
I --> J[Traverse Path Hierarchy]
J --> K{Match Found?}
K -- Yes --> L["Check Constraints (Actions, Conditions, Fields)"]
L -- Pass --> F
L -- Fail --> M[Continue Traversal]
K -- No --> M
M --> N{Root Reached?}
N -- No --> J
N -- Yes --> O[Access Denied]
Detailed Evaluation Steps
1. ACL Evaluation (Short-Circuit)
Before evaluating roles, the system checks if the specific resource instance has an Access Control List (ACL) defined.
Check: Per-entry
acllist matchLogic: If
entry.aclexists and contains an entry foruser_shortnamewith the requestedaction_type, access is immediately granted.Note: ACLs are strictly additive. They cannot explicitly deny access if a Role allows it, but they are checked before Roles, allowing for performance optimization on specific items.
2. Permission Aggregation
If no ACL grants access, the system compiles the user's effective permissions.
Source: User's direct Roles + Roles from User's Groups.
Storage: Permissions are stored in the SQL database for fast lookup.
3. Contextual Conditions
The system determines the "state" of the request to match against Permission conditions.
is_active: Set if the resource'sis_activeflag is True.own: Set ifresource.owner_shortname == user.shortnameORresource.owner_groupis inuser.groups.
4. Hierarchy Traversal & Global Wildcards
The system checks permissions starting from the specific resource path up to the root.
/space/folder/subfolder/resource
Traversal Order:
Specific Path:
space:/folder/subfolder/resourceParent:
space:/folder/subfolder...
Root:
space:/
Wildcard Checks:
__all_spaces__— Grants access across all spaces.__all_subpaths__— Grants access to any subpath.
5. Constraint Validation
When a matching Permission key is found, three checks must pass:
Action Check: Is
action_type(e.g.,view,create) inallowed_actions?Condition Check: Does the Permission require conditions (e.g.,
own)? If yes, does the current Context satisfy them?
Field-Level Restrictions:
Restricted Fields: For
update/create, ensures the user is not modifying fields listed inrestricted_fields.Allowed Values: Checks if the values being assigned to fields match
allowed_fields_values.
6. User Profile Protection
For User Profile updates, an additional layer of protection exists via the user_profile_payload_protected_fields setting. This global setting prevents users from modifying specific fields in their own profile payload, even if their Role technically allows "update" access.
Permission JSON Structure
Permissions are defined as JSON objects. Understanding these keys is critical for configuring access.
| Key | Type | Description |
|---|---|---|
subpaths |
Dictionary<string, List<string>> |
Target Scope. Maps Space names to subpaths. Use __all_subpaths__ for recursive access, __all_spaces__ for global access. |
resource_types |
List<string> |
Target Resources. The types of objects this permission applies to — one or more resource types (see Allowed Values below). An empty list applies to all types. |
actions |
List<string> |
Allowed Operations. What the user can do — one or more of the 11 action types (see Allowed Values below). An empty list grants nothing. |
conditions |
List<string> |
Contextual Requirements. own and/or is_active (see Allowed Values below). An empty list = no conditions. |
restricted_fields |
List<string> |
Field Protection. Fields that cannot be modified in create or update requests. |
allowed_fields_values |
Dictionary<string, object> |
Value Constraints. Enforces that specific fields can only take specific values. |
Allowed Values
The actions, conditions, and resource_types arrays only accept the fixed sets below — any other string is rejected.
Actions — the 11 operation types
| Action | Authorizes |
|---|---|
view |
Read a single entry's metadata and payload. |
query |
Search / list entries via the /query endpoint. |
create |
Create a new entry. |
update |
Modify an existing entry's metadata or payload. |
delete |
Remove an entry. |
attach |
Add attachments (comments, media, reactions, relationships) to an entry. |
assign |
Change an entry's ownership (owner or owning group). |
move |
Move or rename an entry to a different subpath / shortname. |
progress_ticket |
Advance a ticket through its workflow states. |
lock |
Place a lock on an entry to block concurrent edits. |
unlock |
Release a lock on an entry. |
Conditions — the 2 contextual gates
| Condition | Requirement |
|---|---|
own |
The requesting user must own the entry — its owner_shortname equals the user, or the entry's owning group is one of the user's groups. |
is_active |
The entry's is_active flag must be true. |
Conditions gate access to existing entries, so the create and query actions are exempt from condition checks.
Resource types — what the permission targets
Any of the platform's resource types may be listed. An empty resource_types array applies to all types. The full set, grouped:
Identity user, group
Structure folder, space
Content content, schema, data_asset, csv, jsonl, sqlite, parquet
Workflow ticket
Social comment, reply, post, reaction, notification, share
Attachments media, log, relationship, alteration, history, lock
Management role, permission, acl
Extensions locator, json, plugin_wrapper
Permission Examples
Super Manager — Full Access
{
"shortname": "super_manager",
"subpaths": {
"__all_spaces__": ["__all_subpaths__"]
},
"resource_types": [
"schema", "space", "content", "user", "..."
],
"actions": [
"delete", "update", "query", "create", "view", "attach"
],
"conditions": [],
"restricted_fields": []
}
Grants unrestricted access to all resources in all spaces. No ownership requirement, no field restrictions.
View Users — Read-Only Scope
{
"shortname": "view_users",
"subpaths": {
"management": ["users"]
},
"resource_types": [
"content", "ticket", "folder"
],
"actions": ["view", "query"],
"conditions": []
}
Read-only access to the users subpath within the management space only.
Edit Own Profile — Restricted Update
{
"shortname": "edit_own_profile",
"resource_types": ["user"],
"actions": ["update"],
"conditions": ["own"],
"restricted_fields": ["roles", "is_active"],
"allowed_fields_values": {}
}
Can only edit their own user record. Cannot modify roles or is_active fields — prevents self-promotion or account manipulation.