import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; import { ApiError } from '../../common/errors/api-error'; import { ErrorCode } from '../../common/errors/error-codes'; import { AppConfigService } from '../../config/config.service'; import { SessionsService } from '../../sessions/sessions.service'; import type { Permission } from '../../roles/permissions'; import { IS_PUBLIC_KEY } from './public.decorator'; import { REQUIRED_PERMISSIONS_KEY } from './require-permissions.decorator'; import type { AuthenticatedRequest } from '../authenticated-request'; @Injectable() export class PermissionsGuard implements CanActivate { constructor( private readonly reflector: Reflector, private readonly sessions: SessionsService, private readonly config: AppConfigService, ) {} async canActivate(context: ExecutionContext): Promise { const isPublic = this.reflector.getAllAndOverride(IS_PUBLIC_KEY, [ context.getHandler(), context.getClass(), ]); const required = this.reflector.getAllAndOverride( REQUIRED_PERMISSIONS_KEY, [context.getHandler(), context.getClass()], ); if (isPublic || !required?.length) { return true; } const request = context.switchToHttp().getRequest(); const sessionId = request.signedCookies?.[ this.config.session.cookieName ] as string | undefined; if (!sessionId) { throw new ApiError( ErrorCode.Unauthorized, 'Bitte melden Sie sich an.', 401, ); } const session = await this.sessions.resolveSession( sessionId, request.ip, request.get('user-agent'), ); request.user = { id: session.user.id, sessionId, permissions: session.permissions, }; const allowed = required.every((permission) => session.permissions.includes(permission), ); if (!allowed) { throw new ApiError( ErrorCode.PermissionDenied, 'Keine Berechtigung fuer diese Aktion.', 403, ); } return true; } }