// Capability catalog — the granular permissions the application understands. Routes are // guarded by these keys (see middleware/auth requireCapability); roles are sets of them. // This is pure data (no DB) so it can be required anywhere without circular dependencies. const CAPABILITIES = [ // Assets { key: 'assets.view', label: 'View assets', group: 'Assets' }, { key: 'assets.edit', label: 'Create & edit assets (files, photos, warranties, tasks, notes)', group: 'Assets' }, { key: 'assets.delete', label: 'Delete assets', group: 'Assets' }, { key: 'assets.bulk', label: 'Import, export, mass-update & CMDB sync', group: 'Assets' }, { key: 'assets.assign', label: 'Assign assets & manage employees', group: 'Assets' }, // Maintenance { key: 'pm.view', label: 'View maintenance', group: 'Maintenance' }, { key: 'pm.manage', label: 'Create & edit PM plans and defaults', group: 'Maintenance' }, { key: 'pm.complete', label: 'Attach & complete PM on assets', group: 'Maintenance' }, { key: 'parts.manage', label: 'Manage parts inventory', group: 'Maintenance' }, { key: 'alerts.manage', label: 'Work alerts (scan, acknowledge, dismiss, ticket)', group: 'Maintenance' }, // Contacts { key: 'contacts.manage', label: 'Manage contacts & CRM', group: 'Contacts' }, // Finance { key: 'finance.view', label: 'View reports, books & tax rules', group: 'Finance' }, { key: 'finance.manage', label: 'Manage books, tax rules, disposals & leases', group: 'Finance' }, { key: 'reports.save', label: 'Save & delete reports', group: 'Finance' }, // Configuration { key: 'config.manage', label: 'Manage templates, categories & ID templates', group: 'Configuration' }, // Administration { key: 'admin.users', label: 'Manage users & teams', group: 'Administration' }, { key: 'admin.roles', label: 'Manage roles', group: 'Administration' }, { key: 'admin.settings', label: 'Manage application settings', group: 'Administration' }, { key: 'admin.integrations', label: 'Manage integrations (email, webhook, ServiceNow, Workday)', group: 'Administration' } ]; const CAPABILITY_KEYS = CAPABILITIES.map((c) => c.key); // '*' is a wildcard granting every capability (used by the admin role and future-proofs new ones). const WILDCARD = '*'; const FINANCE_CAPS = [ 'assets.view', 'assets.edit', 'assets.delete', 'assets.bulk', 'assets.assign', 'pm.view', 'pm.manage', 'pm.complete', 'parts.manage', 'alerts.manage', 'contacts.manage', 'finance.view', 'finance.manage', 'reports.save', 'config.manage' ]; const OPERATIONS_CAPS = [ 'assets.view', 'assets.edit', 'assets.assign', 'pm.view', 'pm.manage', 'pm.complete', 'parts.manage', 'alerts.manage', 'contacts.manage', 'reports.save' ]; // Built-in roles. is_system roles cannot be deleted (admin/built-ins) but their // capabilities can be edited, except admin which always keeps the wildcard. const DEFAULT_ROLES = [ { key: 'admin', name: 'Administrator', description: 'Full system access, including users, roles, settings, and integrations.', capabilities: [WILDCARD], is_system: 1, locked: 1, sort_order: 0 }, { key: 'finance', name: 'Finance', description: 'Full asset, maintenance, and financial management; no system administration.', capabilities: FINANCE_CAPS, is_system: 1, locked: 0, sort_order: 1 }, { key: 'operations', name: 'Operations', description: 'Day-to-day asset, maintenance, parts, contacts, and alert work.', capabilities: OPERATIONS_CAPS, is_system: 1, locked: 0, sort_order: 2 }, { key: 'pm_admin', name: 'PM Admin', description: 'Manages preventative maintenance: plans, defaults, completion, parts, and alerts.', capabilities: ['assets.view', 'pm.view', 'pm.manage', 'pm.complete', 'parts.manage', 'alerts.manage', 'reports.save'], is_system: 1, locked: 0, sort_order: 3 }, { key: 'pm_user', name: 'PM User', description: 'Completes preventative maintenance and views assets, parts, and alerts.', capabilities: ['assets.view', 'pm.view', 'pm.complete', 'alerts.manage'], is_system: 1, locked: 0, sort_order: 4 }, { key: 'viewer', name: 'Viewer', description: 'Read-only access to assets, maintenance, and reports.', capabilities: ['assets.view', 'pm.view', 'finance.view'], is_system: 1, locked: 0, sort_order: 5 } ]; // Does a capability set satisfy a required capability? Honors the wildcard. function capabilitySetIncludes(capabilities, required) { if (!Array.isArray(capabilities)) return false; return capabilities.includes(WILDCARD) || capabilities.includes(required); } // Normalize an arbitrary capability list to known keys (admin/wildcard preserved). function sanitizeCapabilities(list) { if (Array.isArray(list) && list.includes(WILDCARD)) return [WILDCARD]; const set = new Set((Array.isArray(list) ? list : []).filter((key) => CAPABILITY_KEYS.includes(key))); return CAPABILITY_KEYS.filter((key) => set.has(key)); } module.exports = { CAPABILITIES, CAPABILITY_KEYS, DEFAULT_ROLES, WILDCARD, capabilitySetIncludes, sanitizeCapabilities };