Authorization
Accountability implements a hybrid RBAC (Role-Based Access Control) and ABAC (Attribute-Based Access Control) authorization system for fine-grained access control.Architecture Overview
The authorization system combines:- Base Roles - Hierarchical roles (owner, admin, member, viewer)
- Functional Roles - Task-specific capabilities (controller, accountant, etc.)
- ABAC Policies - Attribute-based rules for fine-grained control
Base Roles
Base roles define hierarchical access levels within an organization:| Role | Description | Key Permissions |
|---|---|---|
| owner | Organization creator/owner | Full access, can delete org and transfer ownership |
| admin | Organization administrator | Full data operations and member management (cannot delete org) |
| member | Standard user | Access based on assigned functional roles |
| viewer | Read-only user | View data and reports only |
The
owner role cannot be assigned via invitation - only transferred from the current owner.Functional Roles
Functional roles grant additional capabilities to members:| Role | Key Permissions | Use Case |
|---|---|---|
| controller | Period lock/unlock, consolidation oversight | CFO or controller role |
| finance_manager | Account management, exchange rates | Finance team leads |
| accountant | Journal entry operations | Accounting staff |
| period_admin | Period open/close operations | Month-end close managers |
| consolidation_manager | Consolidation group management | Consolidation team |
Functional roles are additive. A member can have multiple functional roles simultaneously.
Permission Matrix (RBAC)
The hardcoded permission matrix defines base permissions:Organization Permissions
| Action | Owner | Admin | Member | Viewer |
|---|---|---|---|---|
organization:read | ✅ | ✅ | ✅ | ✅ |
organization:update | ✅ | ✅ | ❌ | ❌ |
organization:delete | ✅ | ❌ | ❌ | ❌ |
organization:invite_member | ✅ | ✅ | ❌ | ❌ |
organization:remove_member | ✅ | ✅ | ❌ | ❌ |
Company Permissions
| Action | Owner | Admin | Member | Viewer |
|---|---|---|---|---|
company:read | ✅ | ✅ | ✅ | ✅ |
company:create | ✅ | ✅ | ❌ | ❌ |
company:update | ✅ | ✅ | ❌ | ❌ |
company:delete | ✅ | ✅ | ❌ | ❌ |
Account Permissions
| Action | Owner | Admin | Member + Finance Manager | Viewer |
|---|---|---|---|---|
account:read | ✅ | ✅ | ✅ | ✅ |
account:create | ✅ | ✅ | ✅ | ❌ |
account:update | ✅ | ✅ | ✅ | ❌ |
account:delete | ✅ | ✅ | ✅ | ❌ |
Journal Entry Permissions
| Action | Owner | Admin | Member + Accountant | Viewer |
|---|---|---|---|---|
journal_entry:read | ✅ | ✅ | ✅ | ✅ |
journal_entry:create | ✅ | ✅ | ✅ | ❌ |
journal_entry:update | ✅ | ✅ | ✅ | ❌ |
journal_entry:post | ✅ | ✅ | ✅ | ❌ |
journal_entry:reverse | ✅ | ✅ | ✅ | ❌ |
Period Permissions
| Action | Owner | Admin | Member + Period Admin | Viewer |
|---|---|---|---|---|
fiscal_period:read | ✅ | ✅ | ✅ | ✅ |
fiscal_period:open | ✅ | ✅ | ✅ | ❌ |
fiscal_period:close | ✅ | ✅ | ✅ | ❌ |
fiscal_period:lock | ✅ | ✅ | Member + Controller | ❌ |
fiscal_period:unlock | ✅ | ✅ | Member + Controller | ❌ |
Consolidation Permissions
| Action | Owner | Admin | Member + Consolidation Manager | Viewer |
|---|---|---|---|---|
consolidation_group:read | ✅ | ✅ | ✅ | ✅ |
consolidation_group:create | ✅ | ✅ | ✅ | ❌ |
consolidation_group:update | ✅ | ✅ | ✅ | ❌ |
consolidation_group:delete | ✅ | ✅ | ✅ | ❌ |
consolidation_group:run | ✅ | ✅ | ✅ | ❌ |
ABAC Policies
ABAC policies provide fine-grained control beyond the base permission matrix.Policy Structure
Each policy consists of:packages/core/src/authorization/AuthorizationPolicy.ts
Policy Evaluation
Evaluate allow policies
Allow policies evaluated by priority (highest first). First match grants access.
Deny policies always take precedence over allow policies at the same priority level.
Subject Conditions
Define who the policy applies to:Resource Conditions
Define what resources the policy applies to:Action Conditions
Define what actions the policy applies to:Environment Conditions
Define when/where the policy applies (optional):System Policies
Four system policies are automatically created for each organization:1. Platform Admin Full Access
Priority: 1000 (highest)2. Organization Owner Full Access
Priority: 9003. Locked Period Protection
Priority: 999 (higher than owner access)4. Viewer Read-Only Access
Priority: 100 (lowest)System policies cannot be modified or deleted. Custom policies use priority 0-899.
Permission Checking
In Backend Code
Use theAuthorizationService to check permissions:
packages/core/src/authorization/AuthorizationService.ts
With Resource Context
For attribute-based checks (e.g., period status):In Frontend
Use theusePermissions hook:
packages/web/src/hooks/usePermissions.ts
Organization Context
Authorization decisions require organization membership context:Current Organization Membership
packages/core/src/membership/CurrentOrganizationMembership.ts
Loading Membership
API handlers automatically load membership context:packages/api/src/Layers/OrganizationContextMiddlewareLive.ts
Audit Logging
All permission denials are logged to the audit log:Audit logging is fire-and-forget - logging failures do not block the main operation.
packages/persistence/src/Services/AuthorizationAuditRepository.ts
Policy Management API
Admins and owners can manage custom policies:| Method | Path | Description | Required Permission |
|---|---|---|---|
| GET | /v1/organizations/:orgId/policies | List all policies | Admin/Owner |
| GET | /v1/organizations/:orgId/policies/:id | Get policy details | Admin/Owner |
| POST | /v1/organizations/:orgId/policies | Create custom policy | Admin/Owner |
| PATCH | /v1/organizations/:orgId/policies/:id | Update policy | Admin/Owner |
| DELETE | /v1/organizations/:orgId/policies/:id | Delete policy | Admin/Owner |
| POST | /v1/organizations/:orgId/policies/test | Test policy evaluation | Admin/Owner |
Common Use Cases
Restrict Account Access by Number Range
Restrict Account Access by Number Range
Scenario: Only finance managers can modify expense accounts (6000-6999).
Prevent Modifications During Soft Close
Prevent Modifications During Soft Close
Scenario: Only controllers can modify entries during soft close period.Step 1: Deny all users from modifying soft-closed periods:Step 2: Allow controllers to modify:
Time-Based Access Control
Time-Based Access Control
Scenario: Journal entries can only be created during business hours.
IP-Based Access Control
IP-Based Access Control
Scenario: Financial operations can only be performed from office network.
Troubleshooting
Permission Denied Issues
Permission Denied Issues
Check membership:
- Verify user has active membership in organization
- Check membership status is “active” (not “suspended” or “removed”)
- Confirm user’s base role and functional roles
- View all active policies for organization
- Check if deny policy is blocking access
- Use policy test tool to simulate decision
- Verify action requires correct base role
- Check if functional role is required
- Confirm permission is in the hardcoded matrix
Policy Not Working
Policy Not Working
Verify policy is active:
- Check
isActiveflag is true - Confirm policy belongs to correct organization
- Higher priority policies evaluated first
- Deny policies take precedence over allow
- System policies use 900-1000, custom use 0-899
- Subject condition matches user’s roles
- Resource condition matches resource type and attributes
- Action condition matches the action being performed
- Environment condition matches current context (if specified)
- Use policy test API endpoint
- Simulate the exact scenario
- Check which policies match and in what order
System Policy Protection
System Policy Protection
Cannot modify system policy:
- System policies are read-only
- Create custom policies to override (with correct priority)
- Custom deny policies can block system allow policies
- Priority 999 - blocks even owners
- Only way to modify locked entries is to unlock period
- Controllers can unlock periods via API