import express from 'express'; import { StatusCodes } from 'http-status-codes'; import SecurityCheckType from '../types/SecurityCheckType'; import logger from '../shared/logging/WinstonLogger'; import AssignmentManager from '../managers/AssignmentManager'; class SecurityMiddleware { // First check if connected then check if at least ONE rule match. It's NOT an AND but it's a OR function. check(checkIfConnected: boolean, ...checkTypes: Array<SecurityCheckType>): (req: express.Request, res: express.Response, next: express.NextFunction) => void { return async (req: express.Request, res: express.Response, next: express.NextFunction) => { if ( checkIfConnected ) { if ( req.session.profile === null || req.session.profile === undefined ) { return req.session.sendResponse(res, StatusCodes.UNAUTHORIZED); } } let isAllowed = checkTypes.length === 0; if ( !isAllowed ) { for ( const checkType of checkTypes ) { try { switch ( String(checkType) ) { case SecurityCheckType.TEACHING_STAFF: isAllowed = isAllowed || req.session.profile.isTeachingStaff; break; case SecurityCheckType.ASSIGNMENT_STAFF: isAllowed = isAllowed || await AssignmentManager.isUserAllowedToAccessAssignment(req.boundParams.assignment!, req.session.profile); break; case SecurityCheckType.ASSIGNMENT_IS_PUBLISHED: isAllowed = isAllowed || (req.boundParams.assignment?.published ?? false); break; case SecurityCheckType.EXERCISE_SECRET: isAllowed = isAllowed || (req.headers.exercisesecret as string | undefined) === req.boundParams.exercise!.secret; break; default: break; } } catch ( e ) { logger.error('Security check failed !!! => ' + e); isAllowed = isAllowed || false; } } } if ( !isAllowed ) { return req.session.sendResponse(res, StatusCodes.FORBIDDEN); } return next(); }; } } export default new SecurityMiddleware();