2026-10-13 10:33:31 +02:00
|
|
|
// Route permissions are enforced before handlers, including routes installed early.
|
|
|
|
|
// Permissions from a session payload are never accepted as authority.
|
|
|
|
|
export const adminRoles=Object.freeze({
|
2026-10-13 16:58:15 +02:00
|
|
|
admin:Object.freeze(['evidence.read','incidents.read','incidents.triage','overview.read','audit.read','policy.read','policy.prepare','policy.write','devices.manage','bookmarks.read','bookmarks.write','teaching.read','teaching.write','integrations.test','integrations.manage','sessions.manage']),
|
|
|
|
|
analyst:Object.freeze(['evidence.read','incidents.read','incidents.triage','overview.read','audit.read']),
|
2026-10-13 10:33:31 +02:00
|
|
|
operator:Object.freeze(['incidents.read','incidents.triage','overview.read','audit.read','policy.read','policy.prepare','devices.manage','bookmarks.read','bookmarks.write','teaching.read','teaching.write','integrations.test']),
|
|
|
|
|
auditor:Object.freeze(['incidents.read','overview.read','audit.read','policy.read','teaching.read'])
|
|
|
|
|
});
|
|
|
|
|
export function bootstrapAllowed(config){
|
|
|
|
|
try{const url=new URL(config.publicUrl);return url.protocol==='http:'&&['127.0.0.1','localhost','[::1]'].includes(url.hostname);}catch{return false;}
|
|
|
|
|
}
|
|
|
|
|
export function normalizePrincipal(principal){
|
|
|
|
|
const role=principal.role??(principal.admin===true?'admin':null);
|
|
|
|
|
if(!Object.hasOwn(adminRoles,role))return null;
|
|
|
|
|
return {sub:principal.sub,name:principal.name,admin:role==='admin',role,permissions:[...adminRoles[role]]};
|
|
|
|
|
}
|
|
|
|
|
export function principalForGroups(identity,groups,config){
|
|
|
|
|
const list=Array.isArray(groups)?groups:[];
|
2026-10-13 16:58:15 +02:00
|
|
|
const role=list.includes(config.oidcAdminGroup)?'admin':config.oidcOperatorGroup&&list.includes(config.oidcOperatorGroup)?'operator':config.oidcAnalystGroup&&list.includes(config.oidcAnalystGroup)?'analyst':config.oidcAuditorGroup&&list.includes(config.oidcAuditorGroup)?'auditor':null;
|
2026-10-13 10:33:31 +02:00
|
|
|
return role?normalizePrincipal({...identity,role}):{...identity,admin:false};
|
|
|
|
|
}
|
|
|
|
|
export function adminRoutePermission(method,url){
|
|
|
|
|
const path=new URL(url,'http://localhost').pathname;
|
|
|
|
|
if(method==='GET'){
|
2026-10-13 16:58:15 +02:00
|
|
|
if(/^\/api\/admin\/evidence\/[^/]+$/.test(path)||/^\/api\/admin\/incidents\/[^/]+\/evidence$/.test(path))return 'evidence.read';
|
2026-10-13 10:33:31 +02:00
|
|
|
if(/^\/api\/admin\/incidents(?:\/[^/]+)?$/.test(path))return 'incidents.read';
|
|
|
|
|
if(path==='/api/admin/user-profiles')return 'policy.read';
|
|
|
|
|
if(path==='/api/admin/policy-groups')return 'policy.read';
|
|
|
|
|
if(/^\/api\/admin\/policy-reviews(?:\/[^/]+)?$/.test(path))return 'policy.read';
|
|
|
|
|
if(path==='/api/admin/policy/history'||/^\/api\/admin\/policy\/history\/\d+$/.test(path)||path==='/api/admin/policy/diff')return 'policy.read';
|
|
|
|
|
if(path==='/api/admin/sessions')return 'sessions.manage';
|
2026-10-13 16:58:15 +02:00
|
|
|
if(path==='/api/admin/siem')return 'policy.read';
|
2026-10-13 10:33:31 +02:00
|
|
|
if(path==='/api/admin/overview'||path==='/api/admin/session')return 'overview.read';
|
2026-10-13 16:58:15 +02:00
|
|
|
if(path==='/api/admin/audit/integrity'||path==='/api/admin/audit/entries'||path==='/api/admin/events/export')return 'audit.read';
|
2026-10-13 10:33:31 +02:00
|
|
|
if(path==='/api/admin/teaching')return 'teaching.read';
|
|
|
|
|
if(/^\/api\/admin\/devices\/[^/]+\/bookmarks$/.test(path))return 'bookmarks.read';
|
|
|
|
|
if(['/api/admin/management-channel','/api/admin/presets','/api/admin/integrations'].includes(path))return 'policy.read';
|
|
|
|
|
}
|
2026-10-13 16:58:15 +02:00
|
|
|
if(method==='PUT'&&path==='/api/admin/siem')return 'integrations.manage';
|
2026-10-13 10:33:31 +02:00
|
|
|
if(method==='POST'&&path==='/api/admin/incidents')return 'incidents.triage';
|
|
|
|
|
if(method==='PATCH'&&/^\/api\/admin\/incidents\/[^/]+$/.test(path))return 'incidents.triage';
|
|
|
|
|
if(method==='POST'&&path==='/api/admin/policy-reviews')return 'policy.prepare';
|
|
|
|
|
if(method==='POST'&&path==='/api/admin/user-profiles/preview')return 'policy.read';
|
|
|
|
|
if(method==='PUT'&&path==='/api/admin/user-profile-configuration')return 'policy.write';
|
|
|
|
|
if(['PUT','DELETE'].includes(method)&&/^\/api\/admin\/user-profiles\/[^/]+$/.test(path))return 'policy.write';
|
|
|
|
|
if(method==='POST'&&path==='/api/admin/policy-groups/preview')return 'policy.read';
|
|
|
|
|
if(['PUT','DELETE'].includes(method)&&/^\/api\/admin\/policy-groups\/[^/]+$/.test(path))return 'policy.write';
|
|
|
|
|
if(method==='POST'&&/^\/api\/admin\/policy-reviews\/[^/]+\/(approve|reject)$/.test(path))return 'policy.write';
|
|
|
|
|
if(method==='POST'&&path==='/api/admin/policy/restore')return 'policy.write';
|
|
|
|
|
if(method==='POST'&&/^\/api\/admin\/sessions\/[^/]+\/revoke$/.test(path))return 'sessions.manage';
|
|
|
|
|
if(method==='PUT'&&/^\/api\/admin\/devices\/[^/]+\/bookmarks$/.test(path))return 'bookmarks.write';
|
|
|
|
|
if(method==='POST'&&(path==='/api/admin/enrollments'||/^\/api\/admin\/devices\/[^/]+\/revoke$/.test(path)))return 'devices.manage';
|
|
|
|
|
// Per-device inspection changes publish the shared policy version.
|
|
|
|
|
if(method==='PATCH'&&/^\/api\/admin\/devices\/[^/]+\/inspection$/.test(path))return 'policy.write';
|
|
|
|
|
if(method==='PUT'&&['/api/admin/policy','/api/admin/security/blocklists'].includes(path))return 'policy.write';
|
|
|
|
|
if(method==='POST'&&/^\/api\/admin\/teaching\/[^/]+\/publish$/.test(path))return 'policy.write';
|
|
|
|
|
if(method==='POST'&&path==='/api/admin/integrations/icap/probe')return 'integrations.test';
|
|
|
|
|
if(method==='POST'&&['/api/admin/teaching','/api/admin/semantic/reference','/api/admin/semantic/document-reference','/api/admin/security/visual-reference'].includes(path))return 'teaching.write';
|
|
|
|
|
if(method==='POST'&&['/api/admin/policy/test','/api/admin/presets/preview'].includes(path))return 'policy.read';
|
|
|
|
|
return null; // New endpoints require an explicit permission; never infer from HTTP method.
|
|
|
|
|
}
|
|
|
|
|
|