-
Notifications
You must be signed in to change notification settings - Fork 2
64 Access Control
The platform implements a comprehensive three-layer access control system that governs visibility and operations at every level: which classes a user can see, which methods a user can invoke, and which specific objects a user can access. This page documents all three layers, how they interact, their key functions, and area-based scoping for documents.
| Layer | Table | Scope | Bit Width | Bits | Purpose |
|---|---|---|---|---|---|
| ACU | db.acu |
Class + User/Group | 5 | {a,c,s,u,d} |
Can the user see this class? Can they create/read/update/delete objects of this class? |
| AMU | db.amu |
Method + User/Group | 3 | {x,v,e} |
Can the user execute this method? Is it visible? Is it enabled? |
| AOU | db.aou |
Object + User | 3 | {s,u,d} |
Can the user select/update/delete this specific object? |
Additionally, two supplementary tables provide default masks and method-level object overrides:
| Table | Scope | Purpose |
|---|---|---|
AOM (db.aom) |
Object | UNIX-style default mask (9-bit: user/group/other x select/update/delete) |
OMA (db.oma) |
Object + Method + User | Per-object method-level access overrides |
ACU controls which classes a user or group can interact with. It is the broadest layer, determining class-level visibility and permissions before any specific object is considered.
| Bit Position | Code | Permission | Description |
|---|---|---|---|
| 4 (MSB) | a |
access | Can see that the class exists |
| 3 | c |
create | Can create objects of this class |
| 2 | s |
select | Can read/list objects of this class |
| 1 | u |
update | Can edit objects of this class |
| 0 (LSB) | d |
delete | Can delete objects of this class |
Like all access control tables, ACU uses a deny/allow/mask pattern:
mask = allow & ~deny
-
allowbits grant permissions -
denybits explicitly revoke permissions (deny takes precedence) -
maskis the effective permission, computed automatically by thet_acu_beforetrigger
When a new class is created (inserted into db.class_tree), the t_class_tree_insert trigger automatically populates ACU entries:
| Condition | User/Group | ACU Allow |
|---|---|---|
| Root class |
administrator group |
B'11111' (full access) |
| Root class |
apibot user |
B'01110' (create, select, update) |
document subclass |
user group |
B'11000' (access + create) |
reference subclass |
user group |
B'10100' (access + select) |
message subclass |
mailbot user |
B'01110' (create, select, update) |
Child classes inherit their parent's ACU entries via the trigger: if a parent has ACU records, those are copied to the new child class.
| Function | Returns | Purpose |
|---|---|---|
acu(pUserId) |
SETOF record(class, mask) |
All class masks for a user (aggregated across groups via bit_or) |
acu(pUserId, pClass) |
SETOF record(class, mask) |
Mask for a specific class |
GetClassAccessMask(pClass, pUserId) |
bit(5) |
Computed mask for class+user |
CheckClassAccess(pClass, pMask, pUserId) |
boolean |
Test if user has the required mask bits |
DecodeClassAccess(pClass, pUserId) |
record(a,c,s,u,d) |
Human-readable boolean breakdown |
chmodc(pClass, pMask, pUserId, pRecursive, pPropagate) |
void |
Set ACU (admin only); optionally recursive (children) and propagate to existing objects |
GetClassMembers(pClass) |
SETOF record |
All users/groups with ACU entries for a class |
POST /api/v1/workflow/class/access -- Get access info for a class
POST /api/v1/workflow/class/access/set -- Set ACU for a class+user
POST /api/v1/workflow/class/access/list -- List all ACU entries
POST /api/v1/workflow/class/access/decode -- Decode access as boolean fields
AMU controls which workflow methods a user can invoke. It is checked after ACU -- once the system confirms a user can see the class, AMU determines which actions they can perform.
| Bit Position | Code | Permission | Description |
|---|---|---|---|
| 2 (MSB) | x |
execute | Can invoke the method |
| 1 | v |
visible | Method appears in the UI |
| 0 (LSB) | e |
enable | Method button is active/clickable |
When a new method is created (inserted into db.method), the t_method_after_insert trigger automatically populates AMU entries based on the class's existing ACU:
- If the method is visible: AMU allow =
B'111'(execute + visible + enable) - If the method is hidden: AMU allow =
B'101'(execute + enable, but not visible)
This ensures that any user who has class-level access will also have method-level access by default.
| Function | Returns | Purpose |
|---|---|---|
amu(pUserId) |
SETOF record(method, mask) |
All method masks for a user (aggregated across groups) |
amu(pUserId, pMethod) |
SETOF record(method, mask) |
Mask for a specific method |
GetMethodAccessMask(pMethod, pUserId) |
bit(3) |
Computed mask for method+user |
CheckMethodAccess(pMethod, pMask, pUserId) |
boolean |
Test if user has the required mask bits |
DecodeMethodAccess(pMethod, pUserId) |
record(x,v,e) |
Human-readable boolean breakdown |
chmodm(pMethod, pMask, pUserId) |
void |
Set AMU (admin only) |
GetMethodMembers(pMethod) |
SETOF record |
All users/groups with AMU entries for a method |
The AccessMethod view (kernel schema) filters methods by the current user's AMU permissions. It only returns methods where the user has the execute bit set. The api.method view is based on this, so API consumers only see methods they can actually invoke.
POST /api/v1/workflow/method/access -- Get access info for a method
POST /api/v1/workflow/method/access/set -- Set AMU for a method+user
POST /api/v1/workflow/method/access/list -- List all AMU entries
POST /api/v1/workflow/method/access/decode -- Decode access as boolean fields
AOU provides per-object, per-user permissions. This is the most granular layer, allowing fine-grained control over who can see and modify individual objects.
| Bit Position | Code | Permission | Description |
|---|---|---|---|
| 2 (MSB) | s |
select | Can view/read the object |
| 1 | u |
update | Can modify the object |
| 0 (LSB) | d |
delete | Can delete the object |
When an object is created, the ft_object_after_insert trigger inserts a default AOM (Access Object Mask) record into db.aom. The AOM uses a UNIX-style 9-bit mask:
rwx rwx rwx
user group other
Where each rwx corresponds to select, update, delete. The default mask is typically B'111110100' (user: all, group: select+update, other: select).
The AOM serves as a fallback: when no specific AOU entry exists for a user+object pair, the system checks the AOM mask based on whether the user is the owner (user bits), a group member (group bits), or other (other bits).
The db.aou table allows per-object, per-user permissions that override the AOM defaults:
| Column | Purpose |
|---|---|
object |
The object |
userid |
The user (or group) |
deny |
Bits to explicitly deny |
allow |
Bits to explicitly allow |
mask |
Computed: allow & ~deny (via trigger) |
AOU entries are typically created automatically:
- When an object is created, the owner gets full access (
B'111') - For documents, area members may receive access entries
- For messages, the mailbot and relevant users receive appropriate access
The db.oma table provides the finest granularity: per-object, per-method, per-user permissions. It uses the same 3-bit mask as AMU ({x, v, e}).
OMA entries are auto-populated the first time CheckObjectMethodAccess is called for an object+method+user combination (lazy initialization from AMU).
| Function | Returns | Purpose |
|---|---|---|
aou(pUserId) |
SETOF record(object, mask) |
All objects accessible to user (aggregated across groups via bit_or) |
aou(pUserId, pObject) |
SETOF record(object, mask) |
Mask for specific object+user |
access_entity(pUserId, pEntity) |
SETOF record(object, mask) |
All accessible objects of an entity type |
GetObjectAccessMask(pObject, pUserId) |
bit(3) |
Computed mask for object+user |
CheckObjectAccess(pObject, pMask, pUserId) |
boolean |
Test permission (falls back to AOM if no AOU entry) |
DecodeObjectAccess(pObject, pUserId) |
record(s,u,d) |
Human-readable boolean breakdown |
chmodo(pObject, pMask, pUserId) |
void |
Set AOU from 6-bit mask (admin only). The 6 bits encode deny+allow: {deny_s, deny_u, deny_d, allow_s, allow_u, allow_d}
|
AccessObjectUser(pEntity, pUserId, pScope) |
TABLE(object uuid) |
All accessible object IDs in scope |
| Function | Returns | Purpose |
|---|---|---|
GetObjectMethodAccessMask(pObject, pMethod, pUserId) |
bit(3) |
OMA mask for object+method+user |
CheckObjectMethodAccess(pObject, pMethod, pMask, pUserId) |
boolean |
Check; auto-populates OMA from AMU on first check |
POST /api/v1/object/access -- Get access info for an object
POST /api/v1/object/access/set -- Set AOU for an object+user
POST /api/v1/object/access/decode -- Decode access as boolean fields
When a user performs an operation on an object, the system checks all three layers in sequence:
Before any object-level operation, the system verifies that the user has the required class-level permission:
-- Can the user see objects of this class?
PERFORM CheckClassAccess(pClass, B'10000', pUserId); -- 'a' (access) bit
-- Can the user create objects of this class?
PERFORM CheckClassAccess(pClass, B'01000', pUserId); -- 'c' (create) bitIf the ACU check fails, the operation is denied immediately. The AccessObject view pre-filters objects by applying ACU checks, so users never see objects from classes they lack access to.
When executing a method on an object, the system checks that the user has permission to invoke that method:
-- Can the user execute this method?
PERFORM CheckMethodAccess(pMethod, B'100', pUserId); -- 'x' (execute) bitThe AccessMethod view filters methods by AMU, so the API only presents methods the user can actually execute.
For per-object operations (select, update, delete), the system checks the user's specific permissions on that object:
-- Can the user view this object?
PERFORM CheckObjectAccess(pObject, B'100', pUserId); -- 's' (select) bit
-- Can the user modify this object?
PERFORM CheckObjectAccess(pObject, B'010', pUserId); -- 'u' (update) bitThe CheckObjectAccess function implements a fallback chain:
- Check
db.aoufor a direct user entry - Check
db.aoufor entries matching the user's groups - Fall back to
db.aomdefault mask (user/group/other bits based on ownership)
Documents have an additional access dimension: the area. Each document belongs to an area (organizational unit), and users can only see documents in their assigned area and its sub-areas.
- Each user's profile includes an
areafield (set viaSetSessionArea) - Documents have an
areacolumn that determines their organizational scope - The
DocumentAreaTreeview uses a recursive CTE to expand the area hierarchy - The
AccessDocumentview intersects AOU permissions with area membership
Areas form a tree:
root
+-- system
+-- guest
+-- all
+-- {dbname} (default)
+-- Branch A
| +-- Department A1
| +-- Department A2
+-- Branch B
Users in "Branch A" can see documents in "Branch A", "Department A1", and "Department A2", but not "Branch B". Users in "all" can see everything.
The ChangeDocumentArea(pDocument, pArea) function moves a document (and its children) to a new area, recursively updating the area tree. This is exposed via the REST endpoint:
POST /api/v1/document/change/area
In addition to the three-layer entity access control, the admin module provides a separate ACL system for administrative operations. The db.acl table uses a 14-bit mask:
s L l E I D U C p d u c o i
| Bit | Permission |
|---|---|
s |
Substitute user |
L |
Unlock user |
l |
Lock user |
E |
Exclude from group |
I |
Include in group |
D |
Delete group |
U |
Update group |
C |
Create group |
p |
Set password |
d |
Delete user |
u |
Update user |
c |
Create user |
o |
Logout |
i |
Login |
This ACL is checked by the admin module's functions (e.g., CheckAccessControlList) and is separate from the entity-level ACU/AMU/AOU system.
Concepts
API Guide
Authentication & Session
- Connection
- Registration
- Authorization (OAuth 2.0)
- Sign In
- Sign Out
- Password Recovery
- Verification Codes
- Authentication
- Authorization
- Who Am I?
Core Services
Object & Workflow Endpoints
Schema & Internals
Configuration Developer Guide
- Configuration Guide
- Creating an Entity
- Creating a Document
- Creating a Reference
- Workflow Customization
- REST Endpoint Guide
- Event Handler Guide
Operations