-
Notifications
You must be signed in to change notification settings - Fork 87
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
33 changed files
with
946 additions
and
43 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { SetMetadata } from '@nestjs/common'; | ||
import { AuthType } from '../../src/common/enums/auth-type.enum'; | ||
|
||
export const AUTH_KEY = 'authType'; | ||
|
||
export const Auth = (type: AuthType = AuthType.Bearer) => | ||
SetMetadata(AUTH_KEY, type); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import { SetMetadata } from '@nestjs/common'; | ||
import { UserRole } from '../../src/common/enums/users-roles.enum'; | ||
|
||
export const ROLES_KEY = 'roles'; | ||
export const RoleDecorator = (...roles: [UserRole, ...UserRole[]]) => | ||
SetMetadata(ROLES_KEY, roles); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import { | ||
Injectable, | ||
CanActivate, | ||
ExecutionContext, | ||
ForbiddenException, | ||
} from '@nestjs/common'; | ||
import { Reflector } from '@nestjs/core'; | ||
import { ROLES_KEY } from '../../decorators/roles.decorator'; | ||
import { UserRole } from 'src/common/enums/users-roles.enum'; | ||
|
||
@Injectable() | ||
export class RolesGuard implements CanActivate { | ||
constructor(private reflector: Reflector) {} | ||
|
||
canActivate(context: ExecutionContext): boolean { | ||
// Get required roles from metadata | ||
const requiredRoles = this.reflector.getAllAndOverride<UserRole[]>( | ||
ROLES_KEY, | ||
[context.getHandler(), context.getClass()], | ||
); | ||
|
||
// Get user from request | ||
const request = context.switchToHttp().getRequest(); | ||
const user = request.user; // Ensure user is attached to request (e.g., via JWT auth) | ||
console.log('User from request:', user); | ||
|
||
if (!user || !user.role) { | ||
throw new ForbiddenException('Access denied: No role assigned'); | ||
} | ||
|
||
// Check if the user has at least one required role | ||
const hasRequiredRoles = requiredRoles.includes(user.role); | ||
if (!hasRequiredRoles) { | ||
throw new ForbiddenException('Access denied: role not found'); | ||
} | ||
|
||
return hasRequiredRoles; | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
export enum AuthType { | ||
Bearer = 'Bearer', | ||
None = 'None' | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
export enum UserRole { | ||
Admin = 'Admin', | ||
User = 'User', | ||
Guest = 'Guest' | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import { HttpStatus, Injectable } from '@nestjs/common'; | ||
import { | ||
DatabaseException, | ||
BlockchainException, | ||
SessionException, | ||
} from './exceptions'; | ||
|
||
@Injectable() | ||
export class SomeService { | ||
async performDatabaseOperation() { | ||
try { | ||
// Database operations... | ||
} catch (error) { | ||
if (error.code === '23505') { | ||
throw new DatabaseException( | ||
'A record with the same ID already exists', | ||
HttpStatus.CONFLICT, | ||
); | ||
} | ||
throw new DatabaseException('Failed to perform database operation'); | ||
} | ||
} | ||
|
||
async executeBlockchainTransaction() { | ||
try { | ||
// Blockchain operations... | ||
} catch (error) { | ||
throw new BlockchainException('Smart contract execution failed'); | ||
} | ||
} | ||
|
||
async validateSession(sessionId: string) { | ||
const isValid = /* check session validity */ false; | ||
if (!isValid) { | ||
throw new SessionException( | ||
'Your session has expired, please log in again', | ||
); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import { HttpException, HttpStatus } from '@nestjs/common'; | ||
|
||
export class ValidationException extends HttpException { | ||
constructor(message: string, errors?: any) { | ||
super( | ||
{ | ||
message, | ||
errors, | ||
}, | ||
HttpStatus.BAD_REQUEST, | ||
); | ||
} | ||
} | ||
|
||
export class SessionException extends HttpException { | ||
constructor(message: string = 'Session expired') { | ||
super( | ||
{ | ||
message, | ||
}, | ||
HttpStatus.FORBIDDEN, | ||
); | ||
} | ||
} | ||
|
||
export class DatabaseException extends HttpException { | ||
constructor( | ||
message: string, | ||
statusCode: HttpStatus = HttpStatus.INTERNAL_SERVER_ERROR, | ||
) { | ||
super( | ||
{ | ||
message, | ||
}, | ||
statusCode, | ||
); | ||
} | ||
} | ||
|
||
export class BlockchainException extends HttpException { | ||
constructor(message: string) { | ||
super( | ||
{ | ||
message, | ||
}, | ||
HttpStatus.UNPROCESSABLE_ENTITY, | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
import { | ||
ExceptionFilter, | ||
Catch, | ||
ArgumentsHost, | ||
HttpException, | ||
HttpStatus, | ||
Logger, | ||
} from '@nestjs/common'; | ||
import { Request, Response } from 'express'; | ||
import { randomUUID } from 'crypto'; | ||
import { ErrorResponse } from '../interfaces/error-response.interface'; | ||
|
||
@Catch() | ||
export class AllExceptionsFilter implements ExceptionFilter { | ||
private readonly logger = new Logger(AllExceptionsFilter.name); | ||
|
||
catch(exception: unknown, host: ArgumentsHost) { | ||
const ctx = host.switchToHttp(); | ||
const response = ctx.getResponse<Response>(); | ||
const request = ctx.getRequest<Request>(); | ||
|
||
const requestId = randomUUID(); | ||
|
||
let statusCode = HttpStatus.INTERNAL_SERVER_ERROR; | ||
let message = 'Internal server error'; | ||
let error = 'Internal Server Error'; | ||
|
||
if (exception instanceof HttpException) { | ||
statusCode = exception.getStatus(); | ||
const exceptionResponse = exception.getResponse(); | ||
|
||
if (typeof exceptionResponse === 'string') { | ||
message = exceptionResponse; | ||
} else if ( | ||
typeof exceptionResponse === 'object' && | ||
exceptionResponse !== null | ||
) { | ||
message = (exceptionResponse as any).message || message; | ||
error = | ||
(exceptionResponse as any).error || | ||
this.getErrorNameFromStatus(statusCode); | ||
} | ||
} | ||
|
||
const errorResponse: ErrorResponse = { | ||
statusCode, | ||
message, | ||
error, | ||
timestamp: new Date().toISOString(), | ||
path: request.url, | ||
requestId, | ||
}; | ||
|
||
this.logger.error( | ||
`${requestId} - ${request.method} ${request.url} - ${statusCode} - ${message}`, | ||
exception instanceof Error ? exception.stack : '', | ||
); | ||
|
||
response.status(statusCode).json(errorResponse); | ||
} | ||
|
||
private getErrorNameFromStatus(status: number): string { | ||
switch (status) { | ||
case HttpStatus.BAD_REQUEST: | ||
return 'Bad Request'; | ||
case HttpStatus.UNAUTHORIZED: | ||
return 'Unauthorized'; | ||
case HttpStatus.FORBIDDEN: | ||
return 'Forbidden'; | ||
case HttpStatus.NOT_FOUND: | ||
return 'Not Found'; | ||
case HttpStatus.UNPROCESSABLE_ENTITY: | ||
return 'Unprocessable Entity'; | ||
default: | ||
return 'Internal Server Error'; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
import { | ||
ExceptionFilter, | ||
Catch, | ||
ArgumentsHost, | ||
UnauthorizedException, | ||
ForbiddenException, | ||
Logger, | ||
} from '@nestjs/common'; | ||
import { Request, Response } from 'express'; | ||
import { randomUUID } from 'crypto'; | ||
import { ErrorResponse } from '../interfaces/error-response.interface'; | ||
|
||
@Catch(UnauthorizedException, ForbiddenException) | ||
export class AuthExceptionFilter implements ExceptionFilter { | ||
private readonly logger = new Logger(AuthExceptionFilter.name); | ||
|
||
catch( | ||
exception: UnauthorizedException | ForbiddenException, | ||
host: ArgumentsHost, | ||
) { | ||
const ctx = host.switchToHttp(); | ||
const response = ctx.getResponse<Response>(); | ||
const request = ctx.getRequest<Request>(); | ||
|
||
const requestId = randomUUID(); | ||
const statusCode = exception.getStatus(); | ||
|
||
let message: string; | ||
let error: string; | ||
|
||
if (exception instanceof UnauthorizedException) { | ||
message = 'Authentication required'; | ||
error = 'Unauthorized'; | ||
} else { | ||
message = 'Access denied'; | ||
error = 'Forbidden'; | ||
} | ||
|
||
// Override with custom message if provided | ||
const exceptionResponse = exception.getResponse(); | ||
if (typeof exceptionResponse === 'object' && exceptionResponse !== null) { | ||
if ((exceptionResponse as any).message) { | ||
message = (exceptionResponse as any).message; | ||
} | ||
} | ||
|
||
const errorResponse: ErrorResponse = { | ||
statusCode, | ||
message, | ||
error, | ||
timestamp: new Date().toISOString(), | ||
path: request.url, | ||
requestId, | ||
}; | ||
|
||
this.logger.error( | ||
`${requestId} - ${error}: ${request.method} ${request.url} - ${message}`, | ||
); | ||
|
||
response.status(statusCode).json(errorResponse); | ||
} | ||
} |
Oops, something went wrong.