1
0

JobContext.ts 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. import { forEachIn } from "@common/utils/forEachIn";
  2. import { SessionSchema } from "@/modules/DataModule/models/sessions/schema";
  3. import Job, { JobOptions } from "@/Job";
  4. import { Log } from "@/LogBook";
  5. import DataModule from "@/modules/DataModule";
  6. import { UserModel } from "@/modules/DataModule/models/users/schema";
  7. import { JobDerived } from "./types/JobDerived";
  8. import assertJobDerived from "./utils/assertJobDerived";
  9. import {
  10. GetMultipleModelPermissionsResult,
  11. GetSingleModelPermissionsResult
  12. } from "./modules/DataModule/models/users/jobs/GetModelPermissions";
  13. import { GetPermissionsResult } from "./modules/DataModule/models/users/jobs/GetPermissions";
  14. const permissionRegex =
  15. // eslint-disable-next-line max-len
  16. /^(?<moduleName>[a-z]+)\.(?<modelOrJobName>[A-z]+)\.(?<jobName>[A-z]+)(?::(?:(?<modelId>[A-z0-9]{24})(?:\.(?<extraAfterModelId>[A-z]+))?|(?<extraAfterColon>[A-z]+)))?$/;
  17. export default class JobContext {
  18. public readonly job: Job;
  19. private _session?: SessionSchema;
  20. private readonly _socketId?: string;
  21. private readonly _callbackRef?: string;
  22. public constructor(
  23. job: Job,
  24. options?: {
  25. session?: SessionSchema;
  26. socketId?: string;
  27. callbackRef?: string;
  28. }
  29. ) {
  30. this.job = job;
  31. this._session = options?.session;
  32. this._socketId = options?.socketId;
  33. this._callbackRef = options?.callbackRef;
  34. }
  35. /**
  36. * Log a message in the context of the current job, which automatically sets the category and data
  37. *
  38. * @param log - Log message or object
  39. */
  40. public log(log: string | Omit<Log, "timestamp" | "category">) {
  41. return this.job.log(log);
  42. }
  43. public getSession() {
  44. return this._session;
  45. }
  46. public setSession(session?: SessionSchema) {
  47. this._session = session;
  48. }
  49. public getSocketId() {
  50. return this._socketId;
  51. }
  52. public getCallbackRef() {
  53. return this._callbackRef;
  54. }
  55. public executeJob(
  56. // eslint-disable-next-line @typescript-eslint/ban-types
  57. JobClass: Function,
  58. payload?: unknown,
  59. options?: JobOptions
  60. ) {
  61. assertJobDerived(JobClass);
  62. return new (JobClass as JobDerived)(payload, {
  63. session: this._session,
  64. socketId: this._socketId,
  65. ...(options ?? {})
  66. }).execute();
  67. }
  68. public async getUser() {
  69. if (!this._session?.userId)
  70. throw new Error("No user found for session");
  71. const User = await DataModule.getModel<UserModel>("users");
  72. const user = await User.findById(this._session.userId);
  73. if (!user) throw new Error("No user found for session");
  74. return user;
  75. }
  76. public async assertLoggedIn() {
  77. if (!this._session?.userId)
  78. throw new Error("No user found for session");
  79. }
  80. public async assertPermission(permission: string) {
  81. let hasPermission = false;
  82. const {
  83. moduleName,
  84. modelOrJobName,
  85. jobName,
  86. modelId,
  87. extraAfterModelId,
  88. extraAfterColon
  89. } = permissionRegex.exec(permission)?.groups ?? {};
  90. const extra = extraAfterModelId || extraAfterColon;
  91. if (moduleName === "data" && modelOrJobName && jobName) {
  92. const GetModelPermissions = DataModule.getJob(
  93. "users.getModelPermissions"
  94. );
  95. const permissions = (await this.executeJob(GetModelPermissions, {
  96. modelName: modelOrJobName,
  97. modelId
  98. })) as GetSingleModelPermissionsResult;
  99. let modelPermission = `data.${modelOrJobName}.${jobName}`;
  100. if (extra) modelPermission += `.${extra}`;
  101. hasPermission = permissions[modelPermission];
  102. } else {
  103. const GetPermissions = DataModule.getJob("users.getPermissions");
  104. const permissions = (await this.executeJob(
  105. GetPermissions
  106. )) as GetPermissionsResult;
  107. hasPermission = permissions[permission];
  108. }
  109. if (!hasPermission)
  110. throw new Error(
  111. `Insufficient permissions for permission ${permission}`
  112. );
  113. }
  114. public async assertPermissions(permissions: string[]) {
  115. const hasPermission: { [permission: string]: boolean } = {};
  116. permissions.forEach(permission => {
  117. hasPermission[permission] = false;
  118. });
  119. const permissionData = permissions.map(permission => {
  120. const {
  121. moduleName,
  122. modelOrJobName,
  123. jobName,
  124. modelId,
  125. extraAfterModelId,
  126. extraAfterColon
  127. } = permissionRegex.exec(permission)?.groups ?? {};
  128. const extra = extraAfterModelId || extraAfterColon;
  129. return {
  130. permission,
  131. moduleName,
  132. modelOrJobName,
  133. jobName,
  134. modelId,
  135. extra
  136. };
  137. });
  138. const dataPermissions = permissionData.filter(
  139. ({ moduleName, modelOrJobName, jobName }) =>
  140. moduleName === "data" && modelOrJobName && jobName
  141. );
  142. const otherPermissions = permissionData.filter(
  143. ({ moduleName, modelOrJobName, jobName }) =>
  144. !(moduleName === "data" && modelOrJobName && jobName)
  145. );
  146. if (otherPermissions.length > 0) {
  147. const GetPermissions = DataModule.getJob("users.getPermissions");
  148. const permissions = (await this.executeJob(
  149. GetPermissions
  150. )) as GetPermissionsResult;
  151. otherPermissions.forEach(({ permission }) => {
  152. hasPermission[permission] = permissions[permission];
  153. });
  154. }
  155. if (dataPermissions.length > 0) {
  156. const dataPermissionsPerModel: any = {};
  157. dataPermissions.forEach(dataPermission => {
  158. const { modelOrJobName } = dataPermission;
  159. if (!Array.isArray(dataPermissionsPerModel[modelOrJobName]))
  160. dataPermissionsPerModel[modelOrJobName] = [];
  161. dataPermissionsPerModel[modelOrJobName].push(dataPermission);
  162. });
  163. const modelNames = Object.keys(dataPermissionsPerModel);
  164. const GetModelPermissions = DataModule.getJob(
  165. "users.getModelPermissions"
  166. );
  167. await forEachIn(modelNames, async modelName => {
  168. const dataPermissionsForThisModel =
  169. dataPermissionsPerModel[modelName];
  170. const modelIds = dataPermissionsForThisModel.map(
  171. ({ modelId }: { modelId: string }) => modelId
  172. );
  173. const permissions = (await this.executeJob(
  174. GetModelPermissions,
  175. {
  176. modelName,
  177. modelIds
  178. }
  179. )) as GetMultipleModelPermissionsResult;
  180. dataPermissionsForThisModel.forEach(
  181. ({
  182. modelOrJobName,
  183. jobName,
  184. modelId,
  185. extra,
  186. permission
  187. }: {
  188. modelOrJobName: string;
  189. jobName: string;
  190. modelId: string;
  191. extra?: string;
  192. permission: string;
  193. }) => {
  194. let modelPermission = `data.${modelOrJobName}.${jobName}`;
  195. if (extra) modelPermission += `.${extra}`;
  196. const permissionsForModelId = permissions[
  197. modelId
  198. ] as Record<string, boolean>;
  199. hasPermission[permission] =
  200. permissionsForModelId[modelPermission];
  201. }
  202. );
  203. });
  204. }
  205. if (
  206. Object.values(hasPermission).some(hasPermission => !hasPermission)
  207. ) {
  208. const missingPermissions = Object.entries(hasPermission)
  209. .filter(([, hasPermission]) => !hasPermission)
  210. .map(([permission]) => permission);
  211. throw new Error(
  212. `Insufficient permissions for permission(s) ${missingPermissions.join(
  213. ", "
  214. )}`
  215. );
  216. }
  217. }
  218. }