11import { Request , Response , NextFunction } from 'express'
22import { UserRole } from '../types/user.js'
33
4- export function requireRole ( ...allowedRoles : UserRole [ ] ) {
5- return ( req : Request , res : Response , next : NextFunction ) : void => {
6- if ( ! req . user ) {
7- res . status ( 401 ) . json ( { error : 'Unauthenticated' } )
8- return
9- }
4+ type RBACOptions = {
5+ allow : UserRole [ ] ;
6+ } ;
107
11- if ( ! allowedRoles . includes ( req . user . role ) ) {
12- res . status ( 403 ) . json ( {
13- error : `Forbidden: requires role ${ allowedRoles . join ( ' or ' ) } , got '${ req . user . role } '` ,
14- } )
15- return
16- }
8+ const logRBACDenied = ( req : Request , reason : string ) => {
9+ console . warn (
10+ JSON . stringify ( {
11+ level : "warn" ,
12+ event : "security.rbac_denied" ,
13+ service : "disciplr-backend" ,
14+ userId : req . user ?. userId ?? "unknown" ,
15+ role : req . user ?. role ?? "unknown" ,
16+ path : req . originalUrl ,
17+ method : req . method ,
18+ reason,
19+ timestamp : new Date ( ) . toISOString ( ) ,
20+ } ) ,
21+ ) ;
22+ } ;
1723
18- next ( )
19- }
20- }
24+ export const enforceRBAC = ( options : RBACOptions ) => {
25+ return ( req : Request , res : Response , next : NextFunction ) : void => {
26+ // Deny by default
27+ if ( ! req . user ) {
28+ logRBACDenied ( req , "missing_user" ) ;
29+ res . status ( 401 ) . json ( { error : "Unauthorized" } ) ;
30+ return ;
31+ }
2132
22- // Convenience helpers
23- export const requireUser = requireRole ( UserRole . USER , UserRole . VERIFIER , UserRole . ADMIN )
24- export const requireVerifier = requireRole ( UserRole . VERIFIER , UserRole . ADMIN )
25- export const requireAdmin = requireRole ( UserRole . ADMIN )
33+ if ( ! options . allow . includes ( req . user . role ) ) {
34+ logRBACDenied ( req , "insufficient_role" ) ;
35+ res . status ( 403 ) . json ( {
36+ error : "Forbidden" ,
37+ message : `Requires role: ${ options . allow . join ( ", " ) } ` ,
38+ } ) ;
39+ return ;
40+ }
41+
42+ next ( ) ;
43+ } ;
44+ } ;
45+
46+ // Convenience
47+ export const requireAdmin = enforceRBAC ( {
48+ allow : [ UserRole . ADMIN ] ,
49+ } ) ;
0 commit comments