GetPermissions.ts 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. import { HydratedDocument } from "mongoose";
  2. import { forEachIn } from "@common/utils/forEachIn";
  3. import CacheModule from "@/modules/CacheModule";
  4. import DataModuleJob from "@/modules/DataModule/DataModuleJob";
  5. import { UserSchema } from "../schema";
  6. import ModuleManager from "@/ModuleManager";
  7. import Job from "@/Job";
  8. import Event from "@/modules/EventsModule/Event";
  9. import { UserRole } from "../UserRole";
  10. export type GetPermissionsResult = Record<string, boolean>;
  11. /**
  12. * This jobs returns the static/pre-defined permissions for the current user/guest based on the user's role.
  13. * Permissions are cached. No cache invalidation machanism has been implemented yet, but it expires naturally after 6 minutes.
  14. */
  15. export default class GetPermissions extends DataModuleJob {
  16. protected static _modelName = "users";
  17. protected static _hasPermission = true;
  18. // _authorize calls GetPermissions and GetModelPermissions, so to avoid ending up in an infinite loop, just override it
  19. protected override async _authorize() {}
  20. protected async _execute(): Promise<GetPermissionsResult> {
  21. const user = await this._context.getUser().catch(() => null);
  22. const cacheKey = user
  23. ? `user-permissions.${user._id}`
  24. : `user-permissions.guest`;
  25. const cachedPermissions = await CacheModule.get(cacheKey);
  26. if (cachedPermissions) return cachedPermissions;
  27. const permissions = await this._getPermissions(user);
  28. await CacheModule.set(cacheKey, permissions, 360);
  29. return permissions;
  30. }
  31. protected async _getPermissions(user: HydratedDocument<UserSchema> | null) {
  32. const jobs = this._getAllJobs();
  33. const events = this._getAllEvents();
  34. const jobNames = Object.keys(jobs);
  35. const eventNames = Object.keys(events);
  36. const permissions: GetPermissionsResult =
  37. this._getFrontendViewPermissions(user);
  38. await forEachIn(jobNames, async jobName => {
  39. const job = jobs[jobName];
  40. const hasPermission = await job.hasPermission(user);
  41. if (hasPermission) {
  42. permissions[jobName] = true;
  43. }
  44. });
  45. await forEachIn(eventNames, async eventName => {
  46. const event = events[eventName];
  47. const hasPermission = await event.hasPermission(user);
  48. if (hasPermission) {
  49. permissions[eventName] = true;
  50. }
  51. });
  52. return permissions;
  53. }
  54. protected _getFrontendViewPermissions(
  55. user: HydratedDocument<UserSchema> | null
  56. ): Record<string, boolean> {
  57. if (!user) return {};
  58. const moderatorPermissions = {
  59. "admin.view": true,
  60. "admin.view.import": true,
  61. "admin.view.news": true,
  62. "admin.view.playlists": true,
  63. "admin.view.punishments": true,
  64. "admin.view.reports": true,
  65. "admin.view.songs": true,
  66. "admin.view.stations": true,
  67. "admin.view.users": true,
  68. "admin.view.youtubeVideos": true
  69. };
  70. if (user.role === UserRole.MODERATOR) return moderatorPermissions;
  71. const adminPermissions = {
  72. ...moderatorPermissions,
  73. "admin.view.dataRequests": true,
  74. "admin.view.statistics": true,
  75. "admin.view.youtube": true
  76. };
  77. if (user.role === UserRole.ADMIN) return adminPermissions;
  78. return {};
  79. }
  80. protected _getAllJobs(): Record<string, typeof Job> {
  81. const modules = Object.entries(ModuleManager.getModules() ?? {});
  82. let jobs: (string | typeof Job)[][] = [];
  83. modules.forEach(([moduleName, module]) => {
  84. const moduleJobs = Object.entries(module.getJobs()).map(
  85. ([jobName, job]) => [`${moduleName}.${jobName}`, job]
  86. );
  87. jobs = [...jobs, ...moduleJobs];
  88. });
  89. return Object.fromEntries(jobs);
  90. }
  91. protected _getAllEvents(): Record<string, typeof Event> {
  92. const modules = Object.entries(ModuleManager.getModules() ?? {});
  93. let events: (string | typeof Event)[][] = [];
  94. modules.forEach(([moduleName, module]) => {
  95. const moduleEvents = Object.entries(module.getEvents()).map(
  96. ([eventName, event]) => [
  97. `event.${moduleName}.${eventName}`,
  98. event
  99. ]
  100. );
  101. events = [...events, ...moduleEvents];
  102. });
  103. return Object.fromEntries(events);
  104. }
  105. }