roles-guard.ts 2.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162
  1. import { ExecutionContext, Injectable, ReflectMetadata } from '@nestjs/common';
  2. import { Reflector } from '@nestjs/core';
  3. import { ExtractJwt, Strategy } from 'passport-jwt';
  4. import { Permission } from 'shared/generated-types';
  5. import { idsAreEqual } from '../../common/utils';
  6. import { ConfigService } from '../../config/config.service';
  7. import { User } from '../../entity/user/user.entity';
  8. import { RequestContextService } from './request-context.service';
  9. export const PERMISSIONS_METADATA_KEY = '__permissions__';
  10. /**
  11. * Attatches metadata to the resolver defining which permissions are required to execute the
  12. * operation.
  13. *
  14. * @example
  15. * ```
  16. * @Allow(Permission.SuperAdmin)
  17. * @Query()
  18. * getAdministrators() {
  19. * // ...
  20. * }
  21. * ```
  22. */
  23. export const Allow = (...permissions: Permission[]) => ReflectMetadata(PERMISSIONS_METADATA_KEY, permissions);
  24. @Injectable()
  25. export class RolesGuard {
  26. constructor(
  27. private readonly reflector: Reflector,
  28. private requestContextService: RequestContextService,
  29. private configService: ConfigService,
  30. ) {}
  31. canActivate(context: ExecutionContext): boolean {
  32. const permissions = this.reflector.get<string[]>(PERMISSIONS_METADATA_KEY, context.getHandler());
  33. if (this.configService.disableAuth || !permissions) {
  34. return true;
  35. }
  36. const req = context.getArgByIndex(2).req;
  37. const ctx = this.requestContextService.fromRequest(req);
  38. const user: User = req.user;
  39. if (!user) {
  40. return false;
  41. }
  42. const permissionsOnChannel = user.roles
  43. .filter(role => role.channels.find(c => idsAreEqual(c.id, ctx.channel.id)))
  44. .reduce((output, role) => [...output, ...role.permissions], [] as Permission[]);
  45. return arraysIntersect(permissions, permissionsOnChannel);
  46. }
  47. }
  48. /**
  49. * Returns true if any element of arr1 appears in arr2.
  50. */
  51. function arraysIntersect<T>(arr1: T[], arr2: T[]): boolean {
  52. return arr1.reduce((intersects, role) => {
  53. return intersects || arr2.includes(role);
  54. }, false);
  55. }