Files
backend/src/guards/permissions.guard.ts

68 lines
2.0 KiB
TypeScript

import {
Injectable,
ExecutionContext,
UnauthorizedException,
} from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { Reflector } from '@nestjs/core';
import { RolePermissions } from '@app/common/constants/role-permissions';
import { RoleType } from '@app/common/constants/role.type.enum';
@Injectable()
export class PermissionsGuard extends AuthGuard('jwt') {
constructor(private reflector: Reflector) {
super();
}
async canActivate(context: ExecutionContext): Promise<boolean> {
// First, run the AuthGuard logic to validate the JWT
const isAuthenticated = await super.canActivate(context);
if (!isAuthenticated) {
return false;
}
// Authorization logic
const requiredPermissions = this.reflector.get<string[]>(
'permissions',
context.getHandler(),
);
if (!requiredPermissions) {
return true; // Allow if no permissions are specified
}
const request = context.switchToHttp().getRequest();
const user = request.user; // User is now available after AuthGuard
const userRole = user?.role?.type as RoleType;
if (!userRole || !RolePermissions[userRole]) {
throw new UnauthorizedException({
message: `Only ${this.getAllowedRoles(requiredPermissions)} role(s) can access this route.`,
});
}
const userPermissions = RolePermissions[userRole];
const hasRequiredPermissions = requiredPermissions.every((perm) =>
userPermissions.includes(perm),
);
if (!hasRequiredPermissions) {
throw new UnauthorizedException({
message: `Only ${this.getAllowedRoles(requiredPermissions)} role(s) can access this route.`,
});
}
return true;
}
private getAllowedRoles(requiredPermissions: string[]): string {
const allowedRoles = Object.entries(RolePermissions)
.filter(([, permissions]) =>
requiredPermissions.every((perm) => permissions.includes(perm)),
)
.map(([role]) => role);
return allowedRoles.join(', ');
}
}