Files
gridpilot.gg/core/admin/domain/services/AuthorizationService.ts
2026-01-01 12:10:35 +01:00

283 lines
6.5 KiB
TypeScript

import { AdminUser } from '../entities/AdminUser';
import { AuthorizationError } from '../errors/AdminDomainError';
/**
* Domain service for authorization checks
* Stateless service that enforces access control rules
*/
export class AuthorizationService {
/**
* Check if an actor can perform an action on a target user
*/
static canPerformAction(
actor: AdminUser,
action: 'view' | 'manage' | 'modify_roles' | 'change_status' | 'delete',
target?: AdminUser
): boolean {
// Actors must be system admins and active
if (!actor.isSystemAdmin() || !actor.isActive()) {
return false;
}
switch (action) {
case 'view':
return this.canView(actor, target);
case 'manage':
return this.canManage(actor, target);
case 'modify_roles':
return this.canModifyRoles(actor, target);
case 'change_status':
return this.canChangeStatus(actor, target);
case 'delete':
return this.canDelete(actor, target);
default:
return false;
}
}
/**
* Check if actor can view target user
*/
private static canView(actor: AdminUser, target?: AdminUser): boolean {
if (!target) {
// Viewing list - only admins can view
return actor.isSystemAdmin();
}
// Can always view self
if (actor.id.equals(target.id)) {
return true;
}
// Owner can view everyone
if (actor.hasRole('owner')) {
return true;
}
// Admin can view non-admin users
if (actor.hasRole('admin')) {
return !target.isSystemAdmin();
}
return false;
}
/**
* Check if actor can manage target user
*/
private static canManage(actor: AdminUser, target?: AdminUser): boolean {
if (!target) {
return false;
}
// Can always manage self
if (actor.id.equals(target.id)) {
return true;
}
// Owner can manage everyone
if (actor.hasRole('owner')) {
return true;
}
// Admin can manage non-admin users
if (actor.hasRole('admin')) {
return !target.isSystemAdmin();
}
return false;
}
/**
* Check if actor can modify roles of target user
*/
private static canModifyRoles(actor: AdminUser, target?: AdminUser): boolean {
if (!target) {
return false;
}
// Only owner can modify roles
if (!actor.hasRole('owner')) {
return false;
}
// Cannot modify own roles (prevents accidental lockout)
if (actor.id.equals(target.id)) {
return false;
}
return true;
}
/**
* Check if actor can change status of target user
*/
private static canChangeStatus(actor: AdminUser, target?: AdminUser): boolean {
if (!target) {
return false;
}
// Cannot change own status
if (actor.id.equals(target.id)) {
return false;
}
// Owner can change anyone's status
if (actor.hasRole('owner')) {
return true;
}
// Admin can change user status but not other admins/owners
if (actor.hasRole('admin')) {
return !target.isSystemAdmin();
}
return false;
}
/**
* Check if actor can delete target user
*/
private static canDelete(actor: AdminUser, target?: AdminUser): boolean {
if (!target) {
return false;
}
// Cannot delete self
if (actor.id.equals(target.id)) {
return false;
}
// Owner can delete anyone
if (actor.hasRole('owner')) {
return true;
}
// Admin can delete users but not admins/owners
if (actor.hasRole('admin')) {
return !target.isSystemAdmin();
}
return false;
}
/**
* Enforce authorization - throws if not authorized
*/
static enforce(
actor: AdminUser,
action: 'view' | 'manage' | 'modify_roles' | 'change_status' | 'delete',
target?: AdminUser
): void {
if (!this.canPerformAction(actor, action, target)) {
const actionLabel = action.replace('_', ' ');
const targetLabel = target ? `user ${target.email.value}` : 'user list';
throw new AuthorizationError(
`User ${actor.email.value} is not authorized to ${actionLabel} ${targetLabel}`
);
}
}
/**
* Check if actor can list users
*/
static canListUsers(actor: AdminUser): boolean {
return actor.isSystemAdmin() && actor.isActive();
}
/**
* Check if actor can create users
*/
static canCreateUsers(actor: AdminUser): boolean {
// Only owner can create users with admin roles
// Admin can create regular users
return actor.isSystemAdmin() && actor.isActive();
}
/**
* Check if actor can assign a specific role
*/
static canAssignRole(actor: AdminUser, roleToAssign: string, target?: AdminUser): boolean {
// Only owner can assign owner or admin roles
if (roleToAssign === 'owner' || roleToAssign === 'admin') {
return actor.hasRole('owner');
}
// Admin can assign user role
if (roleToAssign === 'user') {
// Admin can assign user role to non-admin users
// Owner can assign user role to anyone
if (actor.hasRole('owner')) {
return true;
}
if (actor.hasRole('admin')) {
if (!target) {
return true;
}
return !target.isSystemAdmin();
}
}
return false;
}
/**
* Get permissions for actor
*/
static getPermissions(actor: AdminUser): string[] {
const permissions: string[] = [];
if (!actor.isSystemAdmin() || !actor.isActive()) {
return permissions;
}
// Base permissions for all admins
permissions.push(
'users.view',
'users.list'
);
// Admin permissions
if (actor.hasRole('admin')) {
permissions.push(
'users.manage',
'users.status.change',
'users.create',
'users.delete'
);
}
// Owner permissions
if (actor.hasRole('owner')) {
permissions.push(
'users.manage',
'users.roles.modify',
'users.status.change',
'users.create',
'users.delete',
'users.export'
);
}
return permissions;
}
/**
* Check if actor has specific permission
*/
static hasPermission(actor: AdminUser, permission: string): boolean {
const permissions = this.getPermissions(actor);
return permissions.includes(permission);
}
/**
* Enforce permission - throws if not authorized
*/
static enforcePermission(actor: AdminUser, permission: string): void {
if (!this.hasPermission(actor, permission)) {
throw new AuthorizationError(
`User ${actor.email.value} does not have permission: ${permission}`
);
}
}
}