130 lines
3.0 KiB
TypeScript
130 lines
3.0 KiB
TypeScript
import {
|
|
Injectable,
|
|
CanActivate,
|
|
ExecutionContext,
|
|
UnauthorizedException,
|
|
ForbiddenException,
|
|
} from '@nestjs/common';
|
|
import { Reflector } from '@nestjs/core';
|
|
import { AuthGuard } from '@nestjs/passport';
|
|
import { Request } from 'express';
|
|
import {
|
|
IS_PUBLIC_KEY,
|
|
ROLES_KEY,
|
|
PERMISSIONS_KEY,
|
|
} from '../../../common/decorators';
|
|
|
|
interface AuthenticatedUser {
|
|
id: string;
|
|
email: string;
|
|
roles: string[];
|
|
permissions: string[];
|
|
}
|
|
|
|
/**
|
|
* JWT Auth Guard - Validates JWT token
|
|
*/
|
|
@Injectable()
|
|
export class JwtAuthGuard extends AuthGuard('jwt') {
|
|
constructor(private reflector: Reflector) {
|
|
super();
|
|
}
|
|
|
|
canActivate(context: ExecutionContext) {
|
|
// Check if route is public
|
|
const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
|
|
context.getHandler(),
|
|
context.getClass(),
|
|
]);
|
|
|
|
if (isPublic) {
|
|
return true;
|
|
}
|
|
|
|
return super.canActivate(context);
|
|
}
|
|
|
|
handleRequest<TUser = AuthenticatedUser>(
|
|
err: Error | null,
|
|
user: TUser | false,
|
|
info: any,
|
|
): TUser {
|
|
if (err || !user) {
|
|
if (info?.name === 'TokenExpiredError') {
|
|
throw new UnauthorizedException('TOKEN_EXPIRED');
|
|
}
|
|
throw err || new UnauthorizedException('AUTH_REQUIRED');
|
|
}
|
|
return user;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Roles Guard - Check if user has required roles
|
|
*/
|
|
@Injectable()
|
|
export class RolesGuard implements CanActivate {
|
|
constructor(private reflector: Reflector) {}
|
|
|
|
canActivate(context: ExecutionContext): boolean {
|
|
const requiredRoles = this.reflector.getAllAndOverride<string[]>(
|
|
ROLES_KEY,
|
|
[context.getHandler(), context.getClass()],
|
|
);
|
|
|
|
if (!requiredRoles || requiredRoles.length === 0) {
|
|
return true;
|
|
}
|
|
|
|
const request = context.switchToHttp().getRequest<Request>();
|
|
const user = request.user as AuthenticatedUser | undefined;
|
|
|
|
if (!user || !user.roles) {
|
|
return false;
|
|
}
|
|
|
|
const hasRole = requiredRoles.some((role) => user.roles.includes(role));
|
|
if (!hasRole) {
|
|
throw new ForbiddenException('PERMISSION_DENIED');
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Permissions Guard - Check if user has required permissions
|
|
*/
|
|
@Injectable()
|
|
export class PermissionsGuard implements CanActivate {
|
|
constructor(private reflector: Reflector) {}
|
|
|
|
canActivate(context: ExecutionContext): boolean {
|
|
const requiredPermissions = this.reflector.getAllAndOverride<string[]>(
|
|
PERMISSIONS_KEY,
|
|
[context.getHandler(), context.getClass()],
|
|
);
|
|
|
|
if (!requiredPermissions || requiredPermissions.length === 0) {
|
|
return true;
|
|
}
|
|
|
|
const request = context.switchToHttp().getRequest<Request>();
|
|
const user = request.user as AuthenticatedUser | undefined;
|
|
|
|
if (!user || !user.permissions) {
|
|
return false;
|
|
}
|
|
|
|
const hasPermission = requiredPermissions.every((permission) =>
|
|
user.permissions.includes(permission),
|
|
);
|
|
|
|
if (!hasPermission) {
|
|
throw new ForbiddenException('PERMISSION_DENIED');
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|