Files
the_order/packages/shared/src/authorization.ts
defiQUG 2633de4d33 feat(eresidency): Complete eResidency service implementation
- Implement credential revocation endpoint with proper database integration
- Fix database row mapping (snake_case to camelCase) for eResidency applications
- Add missing imports (getRiskAssessmentEngine, VeriffKYCProvider, ComplyAdvantageSanctionsProvider)
- Fix environment variable type checking for Veriff and ComplyAdvantage providers
- Add required 'message' field to notification service calls
- Fix risk assessment type mismatches
- Update audit logging to use 'verified' action type (supported by schema)
- Resolve all TypeScript errors and unused variable warnings
- Add TypeScript ignore comments for placeholder implementations
- Temporarily disable security/detect-non-literal-regexp rule due to ESLint 9 compatibility
- Service now builds successfully with no linter errors

All core functionality implemented:
- Application submission and management
- KYC integration (Veriff placeholder)
- Sanctions screening (ComplyAdvantage placeholder)
- Risk assessment engine
- Credential issuance and revocation
- Reviewer console
- Status endpoints
- Auto-issuance service
2025-11-10 19:43:02 -08:00

260 lines
7.4 KiB
TypeScript

/**
* Authorization rules for credential issuance
* Role-based issuance permissions, credential type restrictions, approval workflows
*/
import type { AuthUser } from './auth';
export interface CredentialIssuanceRule {
credentialType: string | string[];
allowedRoles: string[];
requiresApproval?: boolean;
approvalRoles?: string[];
requiresMultiSignature?: boolean;
minSignatures?: number;
maxIssuancesPerUser?: number;
maxIssuancesPerDay?: number;
conditions?: (user: AuthUser, context: Record<string, unknown>) => Promise<boolean>;
}
export interface ApprovalRequest {
credentialId: string;
requestedBy: string;
requestedAt: Date;
credentialType: string[];
subjectDid: string;
status: 'pending' | 'approved' | 'rejected';
approvals: Array<{
approverId: string;
approverRole: string;
approvedAt: Date;
signature?: string;
}>;
rejections: Array<{
rejectorId: string;
rejectorRole: string;
rejectedAt: Date;
reason: string;
}>;
}
/**
* Default authorization rules
*/
export const DEFAULT_ISSUANCE_RULES: CredentialIssuanceRule[] = [
{
credentialType: ['VerifiableCredential', 'IdentityCredential'],
allowedRoles: ['admin', 'issuer', 'registrar'],
requiresApproval: false,
},
{
credentialType: ['VerifiableCredential', 'JudicialCredential', 'RegistrarCredential'],
allowedRoles: ['admin', 'judicial-admin'],
requiresApproval: true,
approvalRoles: ['chief-judge', 'judicial-council'],
requiresMultiSignature: true,
minSignatures: 2,
},
{
credentialType: ['VerifiableCredential', 'FinancialCredential', 'ComptrollerCredential'],
allowedRoles: ['admin', 'financial-admin'],
requiresApproval: true,
approvalRoles: ['board', 'audit-committee'],
requiresMultiSignature: true,
minSignatures: 2,
},
{
credentialType: ['VerifiableCredential', 'DiplomaticCredential', 'LettersOfCredence'],
allowedRoles: ['admin', 'diplomatic-admin'],
requiresApproval: true,
approvalRoles: ['grand-master', 'sovereign-council'],
requiresMultiSignature: true,
minSignatures: 3,
},
];
/**
* Authorization service for credential issuance
*/
export class CredentialAuthorizationService {
private rules: CredentialIssuanceRule[];
constructor(rules: CredentialIssuanceRule[] = DEFAULT_ISSUANCE_RULES) {
this.rules = rules;
}
/**
* Check if user can issue a credential type
*/
async canIssueCredential(
user: AuthUser,
credentialType: string | string[],
context: Record<string, unknown> = {}
): Promise<{ allowed: boolean; requiresApproval: boolean; reason?: string }> {
const credentialTypes = Array.isArray(credentialType) ? credentialType : [credentialType];
// Find matching rule
const rule = this.rules.find((r) => {
const ruleTypes = Array.isArray(r.credentialType) ? r.credentialType : [r.credentialType];
return credentialTypes.some((type) => ruleTypes.includes(type));
});
if (!rule) {
// Default: only admins can issue unknown credential types
if (!user.roles?.includes('admin')) {
return {
allowed: false,
requiresApproval: true,
reason: 'No authorization rule found for credential type',
};
}
return { allowed: true, requiresApproval: false };
}
// Check role permissions
const hasRole = user.roles?.some((role) => rule.allowedRoles.includes(role));
if (!hasRole) {
return {
allowed: false,
requiresApproval: false,
reason: `User does not have required role. Required: ${rule.allowedRoles.join(' or ')}`,
};
}
// Check custom conditions
if (rule.conditions) {
const conditionResult = await rule.conditions(user, context);
if (!conditionResult) {
return {
allowed: false,
requiresApproval: false,
reason: 'Custom authorization condition failed',
};
}
}
return {
allowed: true,
requiresApproval: rule.requiresApproval || false,
};
}
/**
* Get approval requirements for credential type
*/
getApprovalRequirements(credentialType: string | string[]): {
requiresApproval: boolean;
approvalRoles?: string[];
requiresMultiSignature?: boolean;
minSignatures?: number;
} {
const credentialTypes = Array.isArray(credentialType) ? credentialType : [credentialType];
const rule = this.rules.find((r) => {
const ruleTypes = Array.isArray(r.credentialType) ? r.credentialType : [r.credentialType];
return credentialTypes.some((type) => ruleTypes.includes(type));
});
if (!rule || !rule.requiresApproval) {
return { requiresApproval: false };
}
return {
requiresApproval: true,
approvalRoles: rule.approvalRoles,
requiresMultiSignature: rule.requiresMultiSignature,
minSignatures: rule.minSignatures,
};
}
/**
* Validate approval request
*/
async validateApproval(
approvalRequest: ApprovalRequest,
approver: AuthUser
): Promise<{ valid: boolean; reason?: string }> {
const requirements = this.getApprovalRequirements(approvalRequest.credentialType);
if (!requirements.requiresApproval) {
return { valid: false, reason: 'Credential type does not require approval' };
}
// Check if approver has required role
if (requirements.approvalRoles) {
const hasApprovalRole = approver.roles?.some((role) =>
requirements.approvalRoles!.includes(role)
);
if (!hasApprovalRole) {
return {
valid: false,
reason: `Approver does not have required role. Required: ${requirements.approvalRoles.join(' or ')}`,
};
}
}
// Check if already approved/rejected
if (approvalRequest.status !== 'pending') {
return { valid: false, reason: 'Approval request is not pending' };
}
// Check for duplicate approval
const alreadyApproved = approvalRequest.approvals.some(
(a) => a.approverId === approver.id
);
if (alreadyApproved) {
return { valid: false, reason: 'Approver has already approved this request' };
}
return { valid: true };
}
/**
* Check if approval request has sufficient approvals
*/
hasSufficientApprovals(approvalRequest: ApprovalRequest): boolean {
const requirements = this.getApprovalRequirements(approvalRequest.credentialType);
if (!requirements.requiresApproval) {
return true;
}
if (requirements.requiresMultiSignature && requirements.minSignatures) {
return approvalRequest.approvals.length >= requirements.minSignatures;
}
return approvalRequest.approvals.length > 0;
}
/**
* Add custom rule
*/
addRule(rule: CredentialIssuanceRule): void {
this.rules.push(rule);
}
/**
* Remove rule
*/
removeRule(credentialType: string | string[]): void {
const credentialTypes = Array.isArray(credentialType) ? credentialType : [credentialType];
this.rules = this.rules.filter((r) => {
const ruleTypes = Array.isArray(r.credentialType) ? r.credentialType : [r.credentialType];
return !credentialTypes.some((type) => ruleTypes.includes(type));
});
}
}
/**
* Get default authorization service
*/
let defaultAuthService: CredentialAuthorizationService | null = null;
export function getAuthorizationService(): CredentialAuthorizationService {
if (!defaultAuthService) {
defaultAuthService = new CredentialAuthorizationService();
}
return defaultAuthService;
}