mirror of https://github.com/ghostfolio/ghostfolio
underwater
1 year ago
committed by
GitHub
4 changed files with 99 additions and 0 deletions
@ -0,0 +1,6 @@ |
|||
import { SetMetadata } from '@nestjs/common'; |
|||
export const HAS_PERMISSION_KEY = 'has_permission'; |
|||
|
|||
export function HasPermission(permission: string) { |
|||
return SetMetadata(HAS_PERMISSION_KEY, permission); |
|||
} |
@ -0,0 +1,55 @@ |
|||
import { HttpException } from '@nestjs/common'; |
|||
import { Reflector } from '@nestjs/core'; |
|||
import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context-host'; |
|||
import { Test, TestingModule } from '@nestjs/testing'; |
|||
|
|||
import { HasPermissionGuard } from './has-permission.guard'; |
|||
|
|||
describe('HasPermissionGuard', () => { |
|||
let guard: HasPermissionGuard; |
|||
let reflector: Reflector; |
|||
|
|||
beforeEach(async () => { |
|||
const module: TestingModule = await Test.createTestingModule({ |
|||
providers: [HasPermissionGuard, Reflector] |
|||
}).compile(); |
|||
|
|||
guard = module.get<HasPermissionGuard>(HasPermissionGuard); |
|||
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,37 @@ |
|||
import { HAS_PERMISSION_KEY } from '@ghostfolio/api/decorators/has-permission.decorator'; |
|||
import { hasPermission } from '@ghostfolio/common/permissions'; |
|||
import { |
|||
CanActivate, |
|||
ExecutionContext, |
|||
HttpException, |
|||
Injectable |
|||
} from '@nestjs/common'; |
|||
import { Reflector } from '@nestjs/core'; |
|||
import { StatusCodes, getReasonPhrase } from 'http-status-codes'; |
|||
|
|||
@Injectable() |
|||
export class HasPermissionGuard implements CanActivate { |
|||
public constructor(private reflector: Reflector) {} |
|||
|
|||
public canActivate(context: ExecutionContext): boolean { |
|||
const requiredPermission = this.reflector.get<string>( |
|||
HAS_PERMISSION_KEY, |
|||
context.getHandler() |
|||
); |
|||
|
|||
if (!requiredPermission) { |
|||
return true; // No specific permissions required
|
|||
} |
|||
|
|||
const { user } = context.switchToHttp().getRequest(); |
|||
|
|||
if (!user || !hasPermission(user.permissions, requiredPermission)) { |
|||
throw new HttpException( |
|||
getReasonPhrase(StatusCodes.FORBIDDEN), |
|||
StatusCodes.FORBIDDEN |
|||
); |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
} |
Loading…
Reference in new issue