feat: add flexible capability-based RBAC with per-tenant customization
Introduces a capability layer on top of existing roles that controls feature visibility and access. Capabilities follow an area.feature.action taxonomy (~35 capabilities) with sensible defaults per role. Tenant admins can customize via grant/revoke overrides stored in org settings JSONB. Key changes: - Add vice_president role to DB schema - Backend: capability constants, resolution logic, CapabilityGuard (global), @RequireCapability decorator on all 16 tenant controllers - Frontend: permission hooks (useCanEdit, useHasCapability), CapabilityGate component, sidebar filtering by capability, all 17 pages migrated from useIsReadOnly to capability-based checks - New admin UI: /settings/permissions matrix page for per-tenant role customization with grant/revoke delta model - GET /organizations/my-capabilities endpoint for capability refresh - Validation of permissionOverrides in settings updates Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
65
backend/src/common/permissions/capabilities.ts
Normal file
65
backend/src/common/permissions/capabilities.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Capability taxonomy for the HOA Financial Platform.
|
||||
*
|
||||
* Pattern: {area}.{feature}.{action}
|
||||
* Actions: view, edit, approve, manage
|
||||
*
|
||||
* Add new capabilities here when new features are built.
|
||||
* The default role matrix in ./default-role-capabilities.ts must also be updated.
|
||||
*/
|
||||
export const CAPABILITIES = {
|
||||
// Dashboard
|
||||
DASHBOARD_VIEW: 'dashboard.view',
|
||||
|
||||
// Financials
|
||||
FINANCIALS_ACCOUNTS_VIEW: 'financials.accounts.view',
|
||||
FINANCIALS_ACCOUNTS_EDIT: 'financials.accounts.edit',
|
||||
FINANCIALS_CASHFLOW_VIEW: 'financials.cashflow.view',
|
||||
FINANCIALS_CASHFLOW_EDIT: 'financials.cashflow.edit',
|
||||
FINANCIALS_ACTUALS_VIEW: 'financials.actuals.view',
|
||||
FINANCIALS_ACTUALS_EDIT: 'financials.actuals.edit',
|
||||
FINANCIALS_BUDGETS_VIEW: 'financials.budgets.view',
|
||||
FINANCIALS_BUDGETS_EDIT: 'financials.budgets.edit',
|
||||
FINANCIALS_BUDGETS_APPROVE: 'financials.budgets.approve',
|
||||
|
||||
// Assessments
|
||||
ASSESSMENTS_UNITS_VIEW: 'assessments.units.view',
|
||||
ASSESSMENTS_UNITS_EDIT: 'assessments.units.edit',
|
||||
ASSESSMENTS_GROUPS_VIEW: 'assessments.groups.view',
|
||||
ASSESSMENTS_GROUPS_EDIT: 'assessments.groups.edit',
|
||||
|
||||
// Board Planning
|
||||
PLANNING_BUDGETS_VIEW: 'planning.budgets.view',
|
||||
PLANNING_BUDGETS_EDIT: 'planning.budgets.edit',
|
||||
PLANNING_PROJECTS_VIEW: 'planning.projects.view',
|
||||
PLANNING_PROJECTS_EDIT: 'planning.projects.edit',
|
||||
PLANNING_SCENARIOS_VIEW: 'planning.scenarios.view',
|
||||
PLANNING_SCENARIOS_EDIT: 'planning.scenarios.edit',
|
||||
PLANNING_SCENARIOS_APPROVE: 'planning.scenarios.approve',
|
||||
PLANNING_INVESTMENTS_VIEW: 'planning.investments.view',
|
||||
PLANNING_INVESTMENTS_EDIT: 'planning.investments.edit',
|
||||
|
||||
// Board Reference
|
||||
REFERENCE_VENDORS_VIEW: 'reference.vendors.view',
|
||||
REFERENCE_VENDORS_EDIT: 'reference.vendors.edit',
|
||||
|
||||
// Transactions
|
||||
TRANSACTIONS_VIEW: 'transactions.view',
|
||||
TRANSACTIONS_EDIT: 'transactions.edit',
|
||||
TRANSACTIONS_APPROVE: 'transactions.approve',
|
||||
|
||||
// Reports
|
||||
REPORTS_VIEW: 'reports.view',
|
||||
|
||||
// Settings & Administration
|
||||
SETTINGS_ORG_VIEW: 'settings.org.view',
|
||||
SETTINGS_ORG_EDIT: 'settings.org.edit',
|
||||
SETTINGS_MEMBERS_VIEW: 'settings.members.view',
|
||||
SETTINGS_MEMBERS_MANAGE: 'settings.members.manage',
|
||||
SETTINGS_PERMISSIONS_MANAGE: 'settings.permissions.manage',
|
||||
} as const;
|
||||
|
||||
export type Capability = (typeof CAPABILITIES)[keyof typeof CAPABILITIES];
|
||||
|
||||
/** Set of all valid capability strings, for validation */
|
||||
export const ALL_CAPABILITIES = new Set<string>(Object.values(CAPABILITIES));
|
||||
Reference in New Issue
Block a user