mirror of https://github.com/ghostfolio/ghostfolio
3 changed files with 90 additions and 0 deletions
@ -0,0 +1,3 @@ |
|||||
|
import { SetMetadata } from '@nestjs/common'; |
||||
|
export const HAS_PERMISSION_KEY = 'has_permission'; |
||||
|
export const HasPermission = (permission: string) => SetMetadata(HAS_PERMISSION_KEY, permission); |
@ -0,0 +1,57 @@ |
|||||
|
import { Test, TestingModule } from '@nestjs/testing'; |
||||
|
import { HasPermissionsGuard } from './has-permissions.guard'; |
||||
|
import { Reflector } from '@nestjs/core'; |
||||
|
import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context-host'; |
||||
|
import { HttpException } from '@nestjs/common'; |
||||
|
|
||||
|
describe('HasPermissionsGuard', () => { |
||||
|
let guard: HasPermissionsGuard; |
||||
|
let reflector: Reflector; |
||||
|
|
||||
|
beforeEach(async () => { |
||||
|
const module: TestingModule = await Test.createTestingModule({ |
||||
|
providers: [ |
||||
|
HasPermissionsGuard, |
||||
|
Reflector, |
||||
|
], |
||||
|
}).compile(); |
||||
|
|
||||
|
guard = module.get<HasPermissionsGuard>(HasPermissionsGuard); |
||||
|
reflector = module.get<Reflector>(Reflector); |
||||
|
}); |
||||
|
|
||||
|
function setupReflectorSpy(returnValue: string) { |
||||
|
jest.spyOn(reflector, 'get').mockReturnValue(returnValue); |
||||
|
} |
||||
|
|
||||
|
function createMockExecutionContext(permissions: string[]) { |
||||
|
return new ExecutionContextHost([ |
||||
|
{ |
||||
|
user: { |
||||
|
permissions, // Set user permissions based on the argument
|
||||
|
}, |
||||
|
}, |
||||
|
]); |
||||
|
} |
||||
|
|
||||
|
it('should deny access if the user does not have any permission', () => { |
||||
|
setupReflectorSpy('required-permission'); |
||||
|
const noPermissions = createMockExecutionContext([]); |
||||
|
|
||||
|
expect(() => guard.canActivate(noPermissions)).toThrow(HttpException); |
||||
|
}); |
||||
|
|
||||
|
it('should deny access if the user has the wrong permission', () => { |
||||
|
setupReflectorSpy('required-permission'); |
||||
|
const wrongPermission = createMockExecutionContext(['wrong-permission']); |
||||
|
|
||||
|
expect(() => guard.canActivate(wrongPermission)).toThrow(HttpException); |
||||
|
}); |
||||
|
|
||||
|
it('should allow access if the user has the required permission', () => { |
||||
|
setupReflectorSpy('required-permission'); |
||||
|
const rightPermission = createMockExecutionContext(['required-permission']); |
||||
|
|
||||
|
expect(guard.canActivate(rightPermission)).toBe(true); |
||||
|
}); |
||||
|
}); |
@ -0,0 +1,30 @@ |
|||||
|
import { Injectable, CanActivate, ExecutionContext, HttpException } from '@nestjs/common'; |
||||
|
import { Reflector } from '@nestjs/core'; |
||||
|
import { StatusCodes, getReasonPhrase } from 'http-status-codes'; |
||||
|
import { HAS_PERMISSION_KEY } from '@ghostfolio/api/decorators/has-permission.decorator'; |
||||
|
import { hasPermission } from '@ghostfolio/common/permissions'; |
||||
|
|
||||
|
@Injectable() |
||||
|
export class HasPermissionsGuard implements CanActivate { |
||||
|
constructor(private reflector: Reflector) { } |
||||
|
|
||||
|
canActivate(context: ExecutionContext): boolean { |
||||
|
const requiredPermission = this.reflector.get<string>(HAS_PERMISSION_KEY, context.getHandler()); |
||||
|
|
||||
|
if (!requiredPermission) { |
||||
|
return true; // No specific permissions required
|
||||
|
} |
||||
|
|
||||
|
const request = context.switchToHttp().getRequest(); |
||||
|
const user = request.user; |
||||
|
|
||||
|
if (!user || !hasPermission(user.permissions, requiredPermission)) { |
||||
|
throw new HttpException( |
||||
|
getReasonPhrase(StatusCodes.FORBIDDEN), |
||||
|
StatusCodes.FORBIDDEN |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
} |
Loading…
Reference in new issue