UNPKG

business-as-code

Version:

Primitives for expressing business logic and processes as code

173 lines 5.72 kB
/** * Organization Structure - Flows to FGA/RBAC * * Defines the complete organizational hierarchy: * * Organization * └── Department * └── Team * └── Position (Role + Worker) * └── Permissions (FGA/RBAC) * * This structure enables: * - Hierarchical permission inheritance * - Role-based task assignment * - Approval chains based on org structure * - Resource access control based on department/team * * @packageDocumentation */ /** * Resolve permissions for a position in the org hierarchy */ export function resolvePermissions(org, positionId) { // Find the position let position; let team; let department; // Search through hierarchy for (const dept of org.departments || []) { for (const t of dept.teams || []) { const pos = t.positions?.find((p) => p.id === positionId); if (pos) { position = pos; team = t; department = dept; break; } } if (position) break; } // Also check standalone teams if (!position) { for (const t of org.teams || []) { const pos = t.positions?.find((p) => p.id === positionId); if (pos) { position = pos; team = t; break; } } } if (!position) return null; // Find the role const role = org.roles?.find((r) => r['id'] === position.roleId); // Build inheritance chain const inheritanceChain = []; const permissions = {}; const resourcePermissions = {}; const canApprove = []; const canHandle = []; // 1. Department defaults if (department?.defaultPermissions) { inheritanceChain.push(`department:${department.id}`); mergePermissions(permissions, department.defaultPermissions); } // 2. Team defaults if (team?.defaultPermissions) { inheritanceChain.push(`team:${team.id}`); mergePermissions(permissions, team.defaultPermissions); } // 3. Team resources (scoped permissions) if (team?.resources) { for (const [resourceType, resourceIds] of Object.entries(team.resources)) { if (resourceIds) { for (const resourceId of resourceIds) { const key = `${resourceType}:${resourceId}`; resourcePermissions[key] = resourcePermissions[key] || {}; mergePermissions(resourcePermissions[key], team.defaultPermissions || {}); } } } } // 4. Role permissions if (role?.permissions) { inheritanceChain.push(`role:${role['id']}`); mergePermissions(permissions, role.permissions); } // 5. Role capabilities if (role?.['canApprove']) { canApprove.push(...role['canApprove']); } if (role?.['canHandle']) { canHandle.push(...role['canHandle']); } // 6. Position-specific permissions if (position.additionalPermissions) { inheritanceChain.push(`position:${position.id}`); mergePermissions(permissions, position.additionalPermissions); // Handle resource-specific permissions for (const [key, perms] of Object.entries(position.additionalPermissions)) { if (key.includes(':')) { resourcePermissions[key] = resourcePermissions[key] || {}; resourcePermissions[key] = { ...resourcePermissions[key], _direct: perms }; } } } return { workerId: position.workerId || '', positionId: position.id, permissions, resourcePermissions, canApprove: [...new Set(canApprove)], canHandle: [...new Set(canHandle)], inheritanceChain, }; } /** * Merge permissions into target */ function mergePermissions(target, source) { for (const [key, perms] of Object.entries(source)) { if (!target[key]) { target[key] = []; } for (const perm of perms) { if (!target[key].includes(perm)) { target[key].push(perm); } } } } /** * Get approval chain for a request */ export function getApprovalChainForRequest(org, requestType, amount) { const chain = org.approvalChains?.find((c) => c.type === requestType && c.active !== false); if (!chain) return []; // Find the appropriate level based on amount const levels = [...chain.levels].sort((a, b) => (a.threshold || 0) - (b.threshold || 0)); for (const level of levels.reverse()) { if (amount === undefined || (level.threshold && amount <= level.threshold)) { return level.approvers; } } // Return highest level if amount exceeds all thresholds return levels[levels.length - 1]?.approvers || []; } /** * Find manager for a position (follows reportsTo chain) */ export function findManager(org, positionId) { // Find the position for (const dept of org.departments || []) { for (const team of dept.teams || []) { const position = team.positions?.find((p) => p.id === positionId); if (position?.reportsTo) { // Find the manager position for (const d of org.departments || []) { for (const t of d.teams || []) { const manager = t.positions?.find((p) => p.id === position.reportsTo); if (manager) return manager; } } } } } return null; } //# sourceMappingURL=organization.js.map