283 lines
6.5 KiB
TypeScript
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}`
|
|
);
|
|
}
|
|
}
|
|
} |