@adminforth/crud-approve-plugin 1.0.17 → 1.0.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build.log ADDED
@@ -0,0 +1,15 @@
1
+
2
+ > @adminforth/crud-approve-plugin@1.0.0 build
3
+ > tsc && rsync -av --exclude 'node_modules' custom dist/
4
+
5
+ sending incremental file list
6
+ custom/
7
+ custom/ListPageDiffView.vue
8
+ custom/ShowPageDiffView.vue
9
+ custom/package-lock.json
10
+ custom/package.json
11
+ custom/pnpm-lock.yaml
12
+ custom/tsconfig.json
13
+
14
+ sent 32,550 bytes received 134 bytes 65,368.00 bytes/sec
15
+ total size is 32,047 speedup is 0.98
@@ -43,8 +43,8 @@ async function sendApproveRequest(approved) {
43
43
 
44
44
  function initDiffFile() {
45
45
  const file = generateDiffFile(
46
- 'diff.json', oldContent.slice(2, -1),
47
- 'diff.json', newContent.slice(2, -1),
46
+ 'diff.json', oldContent,
47
+ 'diff.json', newContent,
48
48
  'json', 'json'
49
49
  );
50
50
  file.initTheme(theme.value === 'dark' ? 'dark' : 'light');
@@ -77,4 +77,3 @@ watch([theme], ([t]) => {
77
77
  <Button style="background-color: red; color: white;" @click="sendApproveRequest(false)" :loader="false" class="w-full">Reject</Button>
78
78
  </div>
79
79
  </template>
80
-
@@ -20,16 +20,10 @@ const diffFile = ref();
20
20
 
21
21
 
22
22
  async function sendApproveRequest(approved) {
23
- let code = '123456'
24
- if (approved) {
25
- code = await (window).adminforthTwoFaModal?.get2FaConfirmationResult?.("Approve Action Confirmation");
26
- }
27
-
28
23
  const data = await callAdminForthApi({
29
24
  path: `/plugin/crud-approve/update-status`,
30
25
  method: 'POST',
31
26
  body: {
32
- meta: { confirmationResult: code },
33
27
  connectorId: props.resource.connectorId,
34
28
  resourceId: props.resource.resourceId,
35
29
  action: props.record[props.meta.resourceColumns.actionColumnName],
@@ -49,8 +43,8 @@ async function sendApproveRequest(approved) {
49
43
 
50
44
  function initDiffFile() {
51
45
  const file = generateDiffFile(
52
- 'diff.json', oldContent.slice(2, -1),
53
- 'diff.json', newContent.slice(2, -1),
46
+ 'diff.json', oldContent,
47
+ 'diff.json', newContent,
54
48
  'json', 'json'
55
49
  );
56
50
  file.initTheme(theme.value === 'dark' ? 'dark' : 'light');
@@ -83,4 +77,3 @@ watch([theme], ([t]) => {
83
77
  <Button style="background-color: red; color: white;" @click="sendApproveRequest(false)" :loader="false" class="w-full">Reject</Button>
84
78
  </div>
85
79
  </template>
86
-
@@ -22,16 +22,10 @@ const newContent = JSON.stringify(props.record[props.meta.resourceColumns.dataCo
22
22
  const diffFile = ref();
23
23
 
24
24
  async function sendApproveRequest(approved) {
25
- let code = '123456'
26
- if (approved) {
27
- code = await (window).adminforthTwoFaModal?.get2FaConfirmationResult?.("Approve Action Confirmation");
28
- }
29
-
30
25
  const data = await callAdminForthApi({
31
26
  path: `/plugin/crud-approve/update-status`,
32
27
  method: 'POST',
33
28
  body: {
34
- meta: { confirmationResult: code },
35
29
  connectorId: props.resource.connectorId,
36
30
  resourceId: props.resource.resourceId,
37
31
  action: props.record[props.meta.resourceColumns.actionColumnName],
package/dist/index.js CHANGED
@@ -7,7 +7,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
7
7
  step((generator = generator.apply(thisArg, _arguments || [])).next());
8
8
  });
9
9
  };
10
- import { ActionCheckSource, AdminForthPlugin, AdminForthSortDirections, Filters } from "adminforth";
10
+ import { ActionCheckSource, AdminForthPlugin, AdminForthSortDirections, Filters, interpretResource } from "adminforth";
11
11
  import { AdminForthDataTypes, AllowedActionsEnum } from "adminforth";
12
12
  import { AllowedForReviewActionsEnum, ApprovalStatusEnum } from './types.js';
13
13
  import dayjs from "dayjs";
@@ -179,19 +179,6 @@ export default class CRUDApprovePlugin extends AdminForthPlugin {
179
179
  }
180
180
  }
181
181
  });
182
- this.verifyAuth = (cookies) => __awaiter(this, void 0, void 0, function* () {
183
- let authCookie;
184
- for (const i in cookies) {
185
- if (cookies[i].key === `adminforth_${this.adminforth.config.customization.brandNameSlug}_jwt`) {
186
- authCookie = cookies[i].value;
187
- }
188
- }
189
- const authRes = yield this.adminforth.auth.verify(authCookie, 'auth', true);
190
- if ('error' in authRes) {
191
- return { error: authRes.error, authRes: null };
192
- }
193
- return { error: null, authRes: authRes };
194
- });
195
182
  this.createRecord = (resource, diffData, adminUser) => __awaiter(this, void 0, void 0, function* () {
196
183
  const connector = this.adminforth.connectors[resource.dataSource];
197
184
  //@ts-ignore
@@ -289,16 +276,8 @@ export default class CRUDApprovePlugin extends AdminForthPlugin {
289
276
  server.endpoint({
290
277
  method: 'POST',
291
278
  path: `/plugin/crud-approve/update-status`,
292
- noAuth: true,
293
- handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, response, cookies }) {
294
- const authRes = yield this.verifyAuth(cookies);
295
- if (authRes.error) {
296
- //@ts-ignore
297
- response.status = 403;
298
- return { error: authRes.error };
299
- }
300
- const adminUser = authRes.authRes;
301
- const { resourceId, diffId, recordId, action, approved, code } = body;
279
+ handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, response, cookies, adminUser }) {
280
+ const { diffId, approved, code } = body;
302
281
  const diffRecord = yield this.adminforth.resource(this.diffResource.resourceId).get(Filters.EQ(this.options.resourceColumns.idColumnName, diffId));
303
282
  if (!diffRecord) {
304
283
  //@ts-ignore
@@ -310,9 +289,40 @@ export default class CRUDApprovePlugin extends AdminForthPlugin {
310
289
  response.status = 400;
311
290
  return { error: 'Diff record is not pending' };
312
291
  }
292
+ const action = diffRecord[this.options.resourceColumns.actionColumnName];
293
+ const recordId = diffRecord[this.options.resourceColumns.recordIdColumnName];
294
+ const targetResource = this.adminforth.config.resources.find((res) => res.resourceId == diffRecord[this.options.resourceColumns.resourceIdColumnName]);
295
+ const diffResource = this.adminforth.config.resources.find((res) => res.resourceId === this.diffResource.resourceId);
296
+ const { allowedActions } = yield interpretResource(adminUser, diffResource, { requestBody: body, pk: diffId, record: diffRecord }, ActionCheckSource.ShowRequest, this.adminforth);
297
+ const showAllowed = allowedActions[AllowedActionsEnum.show];
298
+ if (showAllowed !== true) {
299
+ //@ts-ignore
300
+ response.status = 403;
301
+ return {
302
+ error: typeof showAllowed === 'string'
303
+ ? showAllowed
304
+ : 'You do not have permission to approve or reject this change request'
305
+ };
306
+ }
307
+ // separation of duties: author of the request is not a valid reviewer for it
308
+ const requesterId = diffRecord[this.options.resourceColumns.userIdColumnName];
309
+ if (!this.options.allowSelfApproval
310
+ && requesterId !== undefined && requesterId !== null
311
+ && `${requesterId}` === `${adminUser.pk}`) {
312
+ //@ts-ignore
313
+ response.status = 403;
314
+ return { error: 'You can not approve or reject your own change request' };
315
+ }
313
316
  let beforeSaveResp;
314
317
  if (approved === true) {
315
- const resource = this.adminforth.config.resources.find((res) => res.resourceId == diffRecord[this.options.resourceColumns.resourceIdColumnName]);
318
+ if (!targetResource) {
319
+ //@ts-ignore
320
+ response.status = 404;
321
+ return {
322
+ error: `Resource ${diffRecord[this.options.resourceColumns.resourceIdColumnName]} not found`
323
+ };
324
+ }
325
+ const resource = targetResource;
316
326
  const diffData = diffRecord[this.options.resourceColumns.dataColumnName];
317
327
  const extra = diffRecord[this.options.resourceColumns.extraColumnName] || {};
318
328
  extra.body = body;
@@ -377,7 +387,7 @@ export default class CRUDApprovePlugin extends AdminForthPlugin {
377
387
  adminUser: adminUser, oldRecord: diffRecord,
378
388
  updates: {
379
389
  [this.options.resourceColumns.statusColumnName]: approved ? ApprovalStatusEnum.approved : ApprovalStatusEnum.rejected,
380
- [this.options.resourceColumns.responserIdColumnName]: authRes.authRes.pk,
390
+ [this.options.resourceColumns.responserIdColumnName]: adminUser.pk,
381
391
  },
382
392
  extra: {
383
393
  //@ts-ignore
package/index.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { ActionCheckSource, AdminForthPlugin, AdminForthSortDirections, Filters, IAdminForthDataSourceConnectorBase, IHttpServer } from "adminforth";
1
+ import { ActionCheckSource, AdminForthPlugin, AdminForthSortDirections, Filters, IAdminForthDataSourceConnectorBase, IHttpServer, interpretResource } from "adminforth";
2
2
  import { IAdminForth, AdminForthDataTypes, AdminForthResource, AllowedActionsEnum, HttpExtra, AdminUser } from "adminforth";
3
3
  import { AllowedForReviewActionsEnum, ApprovalStatusEnum, type PluginOptions } from './types.js';
4
4
 
@@ -250,21 +250,7 @@ export default class CRUDApprovePlugin extends AdminForthPlugin {
250
250
  }
251
251
  }
252
252
  }
253
-
254
- verifyAuth = async (cookies: Array<{key: string, value: string}>) => {
255
- let authCookie;
256
- for (const i in cookies) {
257
- if (cookies[i].key === `adminforth_${this.adminforth.config.customization.brandNameSlug}_jwt`) {
258
- authCookie = cookies[i].value;
259
- }
260
- }
261
- const authRes = await this.adminforth.auth.verify(authCookie, 'auth', true);
262
- if ('error' in authRes) {
263
- return { error: authRes.error, authRes: null };
264
- }
265
- return { error: null, authRes: authRes };
266
- }
267
-
253
+
268
254
  createRecord = async (resource: AdminForthResource, diffData: any, adminUser: AdminUser) => {
269
255
  const connector = this.adminforth.connectors[resource.dataSource];
270
256
  //@ts-ignore
@@ -325,17 +311,9 @@ export default class CRUDApprovePlugin extends AdminForthPlugin {
325
311
  server.endpoint({
326
312
  method: 'POST',
327
313
  path: `/plugin/crud-approve/update-status`,
328
- noAuth: true,
329
- handler: async ({ body, response, cookies }) => {
330
- const authRes = await this.verifyAuth(cookies);
331
- if (authRes.error) {
332
- //@ts-ignore
333
- response.status = 403;
334
- return { error: authRes.error };
335
- }
336
- const adminUser = authRes.authRes;
314
+ handler: async ({ body, response, cookies, adminUser }) => {
337
315
 
338
- const { resourceId, diffId, recordId, action, approved, code } = body;
316
+ const { diffId, approved, code } = body;
339
317
  const diffRecord = await this.adminforth.resource(this.diffResource.resourceId).get(
340
318
  Filters.EQ(this.options.resourceColumns.idColumnName, diffId),
341
319
  )
@@ -350,11 +328,54 @@ export default class CRUDApprovePlugin extends AdminForthPlugin {
350
328
  response.status = 400;
351
329
  return { error: 'Diff record is not pending' };
352
330
  }
331
+
332
+ const action = diffRecord[this.options.resourceColumns.actionColumnName];
333
+ const recordId = diffRecord[this.options.resourceColumns.recordIdColumnName];
334
+ const targetResource = this.adminforth.config.resources.find(
335
+ (res) => res.resourceId == diffRecord[this.options.resourceColumns.resourceIdColumnName]
336
+ );
337
+
338
+ const diffResource = this.adminforth.config.resources.find(
339
+ (res) => res.resourceId === this.diffResource.resourceId
340
+ );
341
+ const { allowedActions } = await interpretResource(
342
+ adminUser,
343
+ diffResource,
344
+ { requestBody: body, pk: diffId, record: diffRecord },
345
+ ActionCheckSource.ShowRequest,
346
+ this.adminforth
347
+ );
348
+ const showAllowed = allowedActions[AllowedActionsEnum.show] as boolean | string | undefined;
349
+ if (showAllowed !== true) {
350
+ //@ts-ignore
351
+ response.status = 403;
352
+ return {
353
+ error: typeof showAllowed === 'string'
354
+ ? showAllowed
355
+ : 'You do not have permission to approve or reject this change request'
356
+ };
357
+ }
358
+
359
+ // separation of duties: author of the request is not a valid reviewer for it
360
+ const requesterId = diffRecord[this.options.resourceColumns.userIdColumnName];
361
+ if (!this.options.allowSelfApproval
362
+ && requesterId !== undefined && requesterId !== null
363
+ && `${requesterId}` === `${adminUser.pk}`) {
364
+ //@ts-ignore
365
+ response.status = 403;
366
+ return { error: 'You can not approve or reject your own change request' };
367
+ }
368
+
353
369
  let beforeSaveResp;
354
370
  if (approved === true) {
355
- const resource = this.adminforth.config.resources.find(
356
- (res) => res.resourceId == diffRecord[this.options.resourceColumns.resourceIdColumnName]
357
- );
371
+ if (!targetResource) {
372
+ //@ts-ignore
373
+ response.status = 404;
374
+ return {
375
+ error: `Resource ${diffRecord[this.options.resourceColumns.resourceIdColumnName]} not found`
376
+ };
377
+ }
378
+ const resource = targetResource;
358
379
  const diffData = diffRecord[this.options.resourceColumns.dataColumnName];
359
380
  const extra = diffRecord[this.options.resourceColumns.extraColumnName] || {};
360
381
  extra.body = body;
@@ -439,7 +460,7 @@ export default class CRUDApprovePlugin extends AdminForthPlugin {
439
460
  adminUser: adminUser, oldRecord: diffRecord,
440
461
  updates: {
441
462
  [this.options.resourceColumns.statusColumnName]: approved ? ApprovalStatusEnum.approved : ApprovalStatusEnum.rejected,
442
- [this.options.resourceColumns.responserIdColumnName]: authRes.authRes.pk,
463
+ [this.options.resourceColumns.responserIdColumnName]: adminUser.pk,
443
464
  },
444
465
  extra: {
445
466
  //@ts-ignore
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adminforth/crud-approve-plugin",
3
- "version": "1.0.17",
3
+ "version": "1.0.19",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
package/types.ts CHANGED
@@ -16,6 +16,12 @@ export interface PluginOptions {
16
16
  createdAtColumnName: string;
17
17
  extraColumnName: string;
18
18
  }
19
+
20
+ /**
21
+ * By default the user who created the approval request can't approve or reject it himself
22
+ * (separation of duties / four-eyes principle). Set to true to disable this check.
23
+ */
24
+ allowSelfApproval?: boolean;
19
25
  }
20
26
 
21
27
  export enum AllowedForReviewActionsEnum {