Permission & Access-Control Model
Permission & Access-Control Model
Source: Copied from
Plings-Docs/admin/permission-model-detailed.md(2025-06-18).
Status: Draft v0.1
Audience: Backend & Front-end engineers
Purpose: Describe how global roles, organisation groups, abilities and container-level ACLs interact to decide who can do what, where.
1. Layers of Authorisation
| Layer | Scope | Implemented with | Example |
|---|---|---|---|
| System Role | Whole platform | system_roles table + JWT role_id claim |
system_owner, manufacturer_issuer |
| Org Group | Single organisation | org_permission_groups, org_group_members |
Warehouse Staff, Family, External Auditor |
| Ability | Atomic action | role_abilities, org_group_abilities |
OBJECT_READ, SPATIAL_MOVE, RESET_PWD |
| Container ACL | Sub-tree of spatial graph | container_acl table |
Deny OBJECT_READ in CEO Room for Everyone |
Decision flow (highest → lowest):
- Super-Admin bypasses all checks (
role_id⇒ abilitySUPER_ADMIN). - RLS policy calls
jwt_has_ability('X'). If false → deny.
– Abilities come either from the system role or from one/more org groups. - When ability is spatial (read/move),
jwt_scope_allows_*helper evaluates current container ACL hierarchy.
– Deny rows override allow rows deeper in the path.
2. Tables & Helpers
roles / role_abilities (global)
roles(role_id PK, role_name text, is_internal bool)
role_abilities(role_id fk, ability text)
org-scoped groups
org_permission_groups(group_id PK, organisation_id fk, group_name text)
org_group_members(group_id fk, user_id uuid)
org_group_abilities(group_id fk, ability text)
container ACL
container_acl(
container_id uuid REFERENCES object_instances(id),
group_id uuid REFERENCES org_permission_groups(group_id),
ability text,
mode enum('allow','deny') default 'allow'
)
Helper functions (simplified snippets)
create function jwt_has_ability(_a text) returns boolean ...;
create function jwt_scope_allows_container(_container uuid) returns boolean ...;
create function jwt_can_see_object(_obj uuid) returns boolean ...;
See api_security_guidelines.md → JWT Claim Design for claim format.
3. Ability Catalogue (v1)
| Category | Ability Key | Typical Holders | Description |
|---|---|---|---|
| Object CRUD | OBJECT_READ |
all roles | View object metadata & images |
OBJECT_CREATE |
editors, makers | Create new object instance or class | |
OBJECT_UPDATE |
editors | Edit name/description/properties | |
OBJECT_DELETE |
admins | Hard-delete object & graph links | |
| Spatial | SPATIAL_MOVE |
movers, scanners | Change current spatial relationship |
SPATIAL_SET_EXPECTED |
admins, planners | Set / change expected (home) location | |
| Functional & Lifecycle | FUNCTIONAL_EDIT |
technicians | Add/remove functional relations |
STATUS_UPDATE |
technicians | Add/remove lifecycle statuses | |
| Ownership & Commerce | OWNERSHIP_TRANSFER |
admins | Transfer ownership inside org |
OFFER_LEND |
lend desk | Create & accept lend offers | |
OFFER_RENT |
rental desk | Create & accept rental agreements | |
OFFER_SELL |
sales | Create & accept sale offers | |
OFFER_AUCTION |
auction house | Manage auction lots | |
| Identifiers | IDENTIFIER_MINT |
manufacturer issuers | Mint new PlingsIdentifier |
IDENTIFIER_REVOKE |
super-admin | Revoke compromised identifiers | |
| Batch & Collection | BATCH_CREATE |
camera workflow | Rapid image batch capture |
COLLECTION_BULK_ACTION |
power users | Bulk move / transfer operations | |
| Organisation Admin | ORG_USER_INVITE |
org admin | Invite / remove users in org |
ORG_ROLE_ASSIGN |
org admin | Assign users to permission groups | |
ORG_BILLING_VIEW |
org admin | View usage & invoices | |
ORG_BILLING_PAY |
finance | Approve payments, apply credits | |
ORG_AUDIT_VIEW |
org admin | View audit log for their organisation | |
| Support & Super | RESET_PWD |
support | Reset password / MFA for any user |
IMPERSONATE_USER |
support | Issue short-lived shadow JWT | |
ACL_BYPASS |
support | Temporarily bypass container ACL | |
SUPER_ADMIN |
system owner | Full bypass of all checks |
Abilities can be granted to system roles or org permission groups. The list is data-driven—adding a new ability only requires inserting a row and referencing
jwt_has_ability('NEW_ABILITY')in policies/resolvers.
4. ACL Inheritance Algorithm
- Starting from the current spatial parent (
IN/ON/UNDER), walk upward. - Accumulate ACL rows (deny overrides allow).
- Stop at organisation root.
- If no matching ACL row → fall back to organisation-wide visibility.
A misplaced hammer left in the CEO room becomes hidden because its current path passes through a deny ACL; once scanned and moved the path changes and the ACL check re-evaluates.
5. Example Scenarios
5.1 CEO Private Room
Room-123 ACL: DENY OBJECT_READ group Everyone
Room-123 ACL: ALLOW OBJECT_READ group CEO_Private
Everyone group loses visibility; CEO’s private group keeps it.
5.2 Family vs. Medicine-Box
MedicineBox ACL: DENY OBJECT_READ group Family
Everything else in the house is visible to Family because it inherits the default allow.
5.3 Support Impersonation
Support role has ability IMPERSONATE_USER. Resolver checks that ability before issuing a short-lived scoped JWT; RLS continues to apply for the impersonated identity.
6. Open Items
- Decide on complete
abilityvocabulary (see spreadsheet). - JSON vs. table storage for
groups[]&abilitiesclaim (size trade-off). - Admin UI for inspecting effective ACL of a selected object.