adminforth 1.1.13 → 1.1.15

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.
Files changed (47) hide show
  1. package/auth.ts +17 -3
  2. package/dist/auth.js +32 -18
  3. package/dist/index.js +114 -58
  4. package/dist/modules/codeInjector.js +0 -1
  5. package/dist/modules/utils.js +2 -12
  6. package/dist/plugins/AccessControl/index.js +66 -0
  7. package/dist/plugins/AccessControl/types.js +1 -0
  8. package/dist/plugins/ForeignInlineListPlugin/index.js +1 -1
  9. package/dist/plugins/plugins/ForeignInlineListPlugin/custom/InlineList.vue +248 -0
  10. package/dist/servers/express.js +2 -2
  11. package/dist/spa/spa/src/App.vue +7 -3
  12. package/dist/spa/spa/src/components/Toast.vue +5 -4
  13. package/dist/spa/spa/src/components/ValueRenderer.vue +1 -1
  14. package/dist/spa/spa/src/composables/useStores.ts +2 -1
  15. package/dist/spa/spa/src/router/index.ts +13 -1
  16. package/dist/spa/spa/src/stores/core.ts +13 -11
  17. package/dist/spa/spa/src/stores/user.ts +54 -0
  18. package/dist/spa/spa/src/utils.ts +9 -4
  19. package/dist/spa/spa/src/views/CreateView.vue +7 -0
  20. package/dist/spa/spa/src/views/EditView.vue +8 -1
  21. package/dist/spa/spa/src/views/ListView.vue +10 -1
  22. package/dist/spa/spa/src/views/LoginView.vue +4 -0
  23. package/dist/types/AdminForthConfig.js +9 -0
  24. package/dist/types/FrontendAPI.js +4 -4
  25. package/index.ts +106 -42
  26. package/modules/codeInjector.ts +0 -1
  27. package/modules/utils.ts +2 -10
  28. package/package.json +3 -2
  29. package/plugins/AccessControl/index.ts +83 -0
  30. package/plugins/AccessControl/types.ts +14 -0
  31. package/plugins/ForeignInlineListPlugin/custom/InlineList.vue +11 -0
  32. package/plugins/ForeignInlineListPlugin/index.ts +2 -1
  33. package/servers/express.ts +2 -2
  34. package/spa/src/App.vue +7 -3
  35. package/spa/src/components/Toast.vue +5 -4
  36. package/spa/src/components/ValueRenderer.vue +1 -1
  37. package/spa/src/composables/useStores.ts +2 -1
  38. package/spa/src/router/index.ts +13 -1
  39. package/spa/src/stores/core.ts +13 -11
  40. package/spa/src/stores/user.ts +54 -0
  41. package/spa/src/utils.ts +9 -4
  42. package/spa/src/views/CreateView.vue +7 -0
  43. package/spa/src/views/EditView.vue +8 -1
  44. package/spa/src/views/ListView.vue +10 -1
  45. package/spa/src/views/LoginView.vue +4 -0
  46. package/types/AdminForthConfig.ts +73 -21
  47. package/types/FrontendAPI.ts +11 -5
package/auth.ts CHANGED
@@ -2,6 +2,7 @@
2
2
  import jwt from 'jsonwebtoken';
3
3
 
4
4
  import crypto from 'crypto';
5
+ import AdminForth from './index.js';
5
6
 
6
7
  // Function to generate a password hash using PBKDF2
7
8
  function calcPasswordHash(password, salt, iterations = 100000, keyLength = 64, digest = 'sha512') {
@@ -19,6 +20,11 @@ function generateSalt(length = 16) {
19
20
  }
20
21
 
21
22
  class AdminForthAuth {
23
+ adminforth: AdminForth;
24
+
25
+ constructor(adminforth) {
26
+ this.adminforth = adminforth;
27
+ }
22
28
 
23
29
  issueJWT(payload) {
24
30
  // read ADMINFORH_SECRET from environment if not drop error
@@ -32,16 +38,16 @@ class AdminForthAuth {
32
38
  return jwt.sign(payload, secret, { expiresIn });
33
39
  }
34
40
 
35
- verify(jwtToken) {
41
+ async verify(jwtToken) {
36
42
  // read ADMINFORH_SECRET from environment if not drop error
37
43
  const secret = process.env.ADMINFORTH_SECRET;
38
44
  if (!secret) {
39
45
  throw new Error('ADMINFORTH_SECRET environment not set');
40
46
  }
47
+ let decoded;
41
48
  try {
42
49
  // verify JWT token
43
- const decoded = jwt.verify(jwtToken, secret);
44
- return decoded;
50
+ decoded = jwt.verify(jwtToken, secret);
45
51
  } catch (err) {
46
52
  if (err.name === 'TokenExpiredError') {
47
53
  console.error('Token expired:', err.message);
@@ -52,6 +58,14 @@ class AdminForthAuth {
52
58
  }
53
59
  return null;
54
60
  }
61
+ const { pk } = decoded;
62
+ if (pk === null) {
63
+ decoded.isRoot = true;
64
+ } else {
65
+ const dbUser = await this.adminforth.getUserByPk(pk);
66
+ decoded.dbUser = dbUser;
67
+ }
68
+ return decoded;
55
69
  }
56
70
 
57
71
  static async generatePasswordHash(password) {
package/dist/auth.js CHANGED
@@ -24,6 +24,9 @@ function generateSalt(length = 16) {
24
24
  return crypto.randomBytes(length).toString('hex');
25
25
  }
26
26
  class AdminForthAuth {
27
+ constructor(adminforth) {
28
+ this.adminforth = adminforth;
29
+ }
27
30
  issueJWT(payload) {
28
31
  // read ADMINFORH_SECRET from environment if not drop error
29
32
  const secret = process.env.ADMINFORTH_SECRET;
@@ -35,28 +38,39 @@ class AdminForthAuth {
35
38
  return jwt.sign(payload, secret, { expiresIn });
36
39
  }
37
40
  verify(jwtToken) {
38
- // read ADMINFORH_SECRET from environment if not drop error
39
- const secret = process.env.ADMINFORTH_SECRET;
40
- if (!secret) {
41
- throw new Error('ADMINFORTH_SECRET environment not set');
42
- }
43
- try {
44
- // verify JWT token
45
- const decoded = jwt.verify(jwtToken, secret);
46
- return decoded;
47
- }
48
- catch (err) {
49
- if (err.name === 'TokenExpiredError') {
50
- console.error('Token expired:', err.message);
41
+ return __awaiter(this, void 0, void 0, function* () {
42
+ // read ADMINFORH_SECRET from environment if not drop error
43
+ const secret = process.env.ADMINFORTH_SECRET;
44
+ if (!secret) {
45
+ throw new Error('ADMINFORTH_SECRET environment not set');
46
+ }
47
+ let decoded;
48
+ try {
49
+ // verify JWT token
50
+ decoded = jwt.verify(jwtToken, secret);
51
+ }
52
+ catch (err) {
53
+ if (err.name === 'TokenExpiredError') {
54
+ console.error('Token expired:', err.message);
55
+ }
56
+ else if (err.name === 'JsonWebTokenError') {
57
+ console.error('Token error:', err.message);
58
+ }
59
+ else {
60
+ console.error('Failed to verify JWT token', err);
61
+ }
62
+ return null;
51
63
  }
52
- else if (err.name === 'JsonWebTokenError') {
53
- console.error('Token error:', err.message);
64
+ const { pk } = decoded;
65
+ if (pk === null) {
66
+ decoded.isRoot = true;
54
67
  }
55
68
  else {
56
- console.error('Failed to verify JWT token', err);
69
+ const dbUser = yield this.adminforth.getUserByPk(pk);
70
+ decoded.dbUser = dbUser;
57
71
  }
58
- return null;
59
- }
72
+ return decoded;
73
+ });
60
74
  }
61
75
  static generatePasswordHash(password) {
62
76
  return __awaiter(this, void 0, void 0, function* () {
package/dist/index.js CHANGED
@@ -13,7 +13,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
13
13
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
14
14
  };
15
15
  var _a, _AdminForth_defaultConfig;
16
- import Auth from './auth.js';
16
+ import AdminForthAuth from './auth.js';
17
17
  import MongoConnector from './dataConnectors/mongo.js';
18
18
  import PostgresConnector from './dataConnectors/postgres.js';
19
19
  import SQLiteConnector from './dataConnectors/sqlite.js';
@@ -22,9 +22,8 @@ import { guessLabelFromName } from './modules/utils.js';
22
22
  import ExpressServer from './servers/express.js';
23
23
  import { v1 as uuid } from 'uuid';
24
24
  import fs from 'fs';
25
- import { ADMINFORTH_VERSION } from './modules/utils.js';
25
+ import { ADMINFORTH_VERSION, listify } from './modules/utils.js';
26
26
  import { AdminForthFilterOperators, AdminForthDataTypes, AdminForthResourcePages } from './types/AdminForthConfig.js';
27
- import { getFunctionList } from './modules/utils.js';
28
27
  import path from 'path';
29
28
  //get array from enum AdminForthResourcePages
30
29
  const DEFAULT_ALLOWED_ACTIONS = { create: true, edit: true, show: true, delete: true };
@@ -40,7 +39,7 @@ class AdminForth {
40
39
  this.activatePlugins();
41
40
  this.validateConfig(); // revalidate after plugins
42
41
  this.express = new ExpressServer(this);
43
- this.auth = new Auth();
42
+ this.auth = new AdminForthAuth(this);
44
43
  this.connectors = {};
45
44
  this.statuses = {};
46
45
  console.log(`🚀 AdminForth v${ADMINFORTH_VERSION} starting up`);
@@ -159,6 +158,7 @@ class AdminForth {
159
158
  res.columns = [];
160
159
  }
161
160
  res.columns.forEach((col) => {
161
+ var _b, _c, _d, _e;
162
162
  col.label = col.label || guessLabelFromName(col.name);
163
163
  //define default sortable
164
164
  if (!Object.keys(col).includes('sortable')) {
@@ -193,6 +193,20 @@ class AdminForth {
193
193
  errors.push(`Resource "${res.resourceId}" column "${col.name}" has invalid showIn value "${wrongShowIn}", allowed values are ${Object.keys(AdminForthResourcePages).join(', ')}`);
194
194
  }
195
195
  col.showIn = col.showIn || Object.values(AdminForthResourcePages);
196
+ if (col.foreignResource) {
197
+ const befHook = (_c = (_b = col.foreignResource.hooks) === null || _b === void 0 ? void 0 : _b.dropdownList) === null || _c === void 0 ? void 0 : _c.beforeDatasourceRequest;
198
+ if (befHook) {
199
+ if (!Array.isArray(befHook)) {
200
+ col.foreignResource.hooks.dropdownList.beforeDatasourceRequest = [befHook];
201
+ }
202
+ }
203
+ const aftHook = (_e = (_d = col.foreignResource.hooks) === null || _d === void 0 ? void 0 : _d.dropdownList) === null || _e === void 0 ? void 0 : _e.afterDatasourceResponse;
204
+ if (aftHook) {
205
+ if (!Array.isArray(aftHook)) {
206
+ col.foreignResource.hooks.dropdownList.afterDatasourceResponse = [aftHook];
207
+ }
208
+ }
209
+ }
196
210
  });
197
211
  if (!res.options) {
198
212
  res.options = { bulkActions: [], allowedActions: {} };
@@ -254,6 +268,37 @@ class AdminForth {
254
268
  else {
255
269
  res.options.allowedActions = DEFAULT_ALLOWED_ACTIONS;
256
270
  }
271
+ // transform all hooks Functions to array of functions
272
+ if (res.hooks) {
273
+ for (const value of [res.hooks.show, res.hooks.list]) {
274
+ if (value) {
275
+ if (value.beforeDatasourceRequest) {
276
+ if (!Array.isArray(value.beforeDatasourceRequest)) {
277
+ value.beforeDatasourceRequest = [value.beforeDatasourceRequest];
278
+ }
279
+ }
280
+ if (value.afterDatasourceResponse) {
281
+ if (!Array.isArray(value.afterDatasourceResponse)) {
282
+ value.afterDatasourceResponse = [value.afterDatasourceResponse];
283
+ }
284
+ }
285
+ }
286
+ }
287
+ for (const value of [res.hooks.create, res.hooks.edit, res.hooks.delete]) {
288
+ if (value) {
289
+ if (value.beforeSave) {
290
+ if (!Array.isArray(value.beforeSave)) {
291
+ value.beforeSave = [value.beforeSave];
292
+ }
293
+ }
294
+ if (value.afterSave) {
295
+ if (!Array.isArray(value.afterSave)) {
296
+ value.afterSave = [value.afterSave];
297
+ }
298
+ }
299
+ }
300
+ }
301
+ }
257
302
  });
258
303
  if (!this.config.menu) {
259
304
  errors.push('No config.menu defined');
@@ -315,7 +360,6 @@ class AdminForth {
315
360
  for (const resource of this.config.resources) {
316
361
  for (const column of resource.columns) {
317
362
  if (column.components) {
318
- console.log('🔧🔧🔧 Validating components for resource', column.components);
319
363
  for (const [key, comp] of Object.entries(column.components)) {
320
364
  let ignoreExistsCheck = false;
321
365
  if (this.codeInjector.allComponentNames[comp.file]) {
@@ -393,6 +437,24 @@ class AdminForth {
393
437
  this.codeInjector.bundleNow({ hotReload, verbose });
394
438
  });
395
439
  }
440
+ getUserByPk(pk) {
441
+ return __awaiter(this, void 0, void 0, function* () {
442
+ const resource = this.config.resources.find((res) => res.resourceId === this.config.auth.resourceId);
443
+ if (!resource) {
444
+ throw new Error('No auth resource found');
445
+ }
446
+ const users = yield this.connectors[resource.dataSource].getData({
447
+ resource,
448
+ filters: [
449
+ { field: resource.columns.find((col) => col.primaryKey).name, operator: AdminForthFilterOperators.EQ, value: pk },
450
+ ],
451
+ limit: 1,
452
+ offset: 0,
453
+ sort: [],
454
+ });
455
+ return users.data[0] || null;
456
+ });
457
+ }
396
458
  setupEndpoints(server) {
397
459
  server.endpoint({
398
460
  noAuth: true,
@@ -436,7 +498,7 @@ class AdminForth {
436
498
  }
437
499
  const passwordHash = userRecord[this.config.auth.passwordHashField];
438
500
  console.log('User record', userRecord, passwordHash); // why does it has no hash?
439
- const valid = yield Auth.verifyPassword(password, passwordHash);
501
+ const valid = yield AdminForthAuth.verifyPassword(password, passwordHash);
440
502
  if (valid) {
441
503
  token = this.auth.issueJWT({
442
504
  username, pk: userRecord[userResource.columns.find((col) => col.primaryKey).name]
@@ -450,11 +512,18 @@ class AdminForth {
450
512
  return { ok: true };
451
513
  }),
452
514
  });
515
+ server.endpoint({
516
+ method: 'POST',
517
+ path: '/check_auth',
518
+ handler: (_d) => __awaiter(this, [_d], void 0, function* ({ adminUser }) {
519
+ return { ok: true };
520
+ }),
521
+ });
453
522
  server.endpoint({
454
523
  noAuth: true,
455
524
  method: 'POST',
456
525
  path: '/logout',
457
- handler: (_d) => __awaiter(this, [_d], void 0, function* ({ response }) {
526
+ handler: (_e) => __awaiter(this, [_e], void 0, function* ({ response }) {
458
527
  response.setHeader('Set-Cookie', `adminforth_jwt=; Path=${this.config.baseUrl || '/'}; HttpOnly; SameSite=Strict; Expires=Thu, 01 Jan 1970 00:00:00 GMT`);
459
528
  return { ok: true };
460
529
  }),
@@ -463,8 +532,8 @@ class AdminForth {
463
532
  noAuth: true,
464
533
  method: 'GET',
465
534
  path: '/get_public_config',
466
- handler: (_e) => __awaiter(this, [_e], void 0, function* ({ body }) {
467
- var _f;
535
+ handler: (_f) => __awaiter(this, [_f], void 0, function* ({ body }) {
536
+ var _g;
468
537
  // find resource
469
538
  if (!this.config.auth) {
470
539
  throw new Error('No config.auth defined');
@@ -476,37 +545,24 @@ class AdminForth {
476
545
  brandName: this.config.customization.brandName,
477
546
  usernameFieldName: usernameColumn.label,
478
547
  loginBackgroundImage: this.config.auth.loginBackgroundImage,
479
- title: (_f = this.config.customization) === null || _f === void 0 ? void 0 : _f.title,
548
+ title: (_g = this.config.customization) === null || _g === void 0 ? void 0 : _g.title,
480
549
  };
481
550
  }),
482
551
  });
483
552
  server.endpoint({
484
553
  method: 'GET',
485
554
  path: '/get_base_config',
486
- handler: (_g) => __awaiter(this, [_g], void 0, function* ({ input, adminUser, cookies }) {
487
- var _h, _j;
488
- const cookieParsed = this.auth.verify(cookies['adminforth_jwt']);
555
+ handler: (_h) => __awaiter(this, [_h], void 0, function* ({ input, adminUser, cookies }) {
556
+ var _j, _k;
489
557
  let username = '';
490
558
  let userFullName = '';
491
- if (cookieParsed['pk'] == null) {
559
+ if (adminUser.isRoot) {
492
560
  username = this.config.rootUser.username;
493
561
  }
494
562
  else {
495
- const userResource = this.config.resources.find((res) => res.resourceId === this.config.auth.resourceId);
496
- const user = yield this.connectors[userResource.dataSource].getData({
497
- resource: userResource,
498
- filters: [
499
- { field: userResource.columns.find((col) => col.primaryKey).name, operator: AdminForthFilterOperators.EQ, value: cookieParsed['pk'] },
500
- ],
501
- limit: 1,
502
- offset: 0,
503
- sort: [],
504
- });
505
- if (!user.data.length) {
506
- return { error: 'Unauthorized' };
507
- }
508
- username = user.data[0][this.config.auth.usernameField];
509
- userFullName = user.data[0][this.config.auth.userFullNameField];
563
+ const dbUser = adminUser.dbUser;
564
+ username = dbUser[this.config.auth.usernameField];
565
+ userFullName = dbUser[this.config.auth.userFullNameField];
510
566
  }
511
567
  const userData = {
512
568
  [this.config.auth.usernameField]: username,
@@ -526,8 +582,8 @@ class AdminForth {
526
582
  deleteConfirmation: this.config.deleteConfirmation,
527
583
  auth: this.config.auth,
528
584
  usernameField: this.config.auth.usernameField,
529
- title: (_h = this.config.customization) === null || _h === void 0 ? void 0 : _h.title,
530
- emptyFieldPlaceholder: (_j = this.config.customization) === null || _j === void 0 ? void 0 : _j.emptyFieldPlaceholder,
585
+ title: (_j = this.config.customization) === null || _j === void 0 ? void 0 : _j.title,
586
+ emptyFieldPlaceholder: (_k = this.config.customization) === null || _k === void 0 ? void 0 : _k.emptyFieldPlaceholder,
531
587
  },
532
588
  adminUser,
533
589
  version: ADMINFORTH_VERSION,
@@ -537,7 +593,7 @@ class AdminForth {
537
593
  server.endpoint({
538
594
  method: 'POST',
539
595
  path: '/get_resource',
540
- handler: (_k) => __awaiter(this, [_k], void 0, function* ({ body }) {
596
+ handler: (_l) => __awaiter(this, [_l], void 0, function* ({ body }) {
541
597
  const { resourceId } = body;
542
598
  if (!this.statuses.dbDiscover) {
543
599
  return { error: 'Database discovery not started' };
@@ -556,8 +612,8 @@ class AdminForth {
556
612
  server.endpoint({
557
613
  method: 'POST',
558
614
  path: '/get_resource_data',
559
- handler: (_l) => __awaiter(this, [_l], void 0, function* ({ body, adminUser }) {
560
- var _m, _o, _p, _q;
615
+ handler: (_m) => __awaiter(this, [_m], void 0, function* ({ body, adminUser }) {
616
+ var _o, _p, _q, _r;
561
617
  const { resourceId, source } = body;
562
618
  if (['show', 'list'].includes(source) === false) {
563
619
  return { error: 'Invalid source, should be list or show' };
@@ -572,7 +628,7 @@ class AdminForth {
572
628
  if (!resource) {
573
629
  return { error: `Resource ${resourceId} not found` };
574
630
  }
575
- for (const hook of getFunctionList((_o = (_m = resource.hooks) === null || _m === void 0 ? void 0 : _m[source]) === null || _o === void 0 ? void 0 : _o.beforeDatasourceRequest)) {
631
+ for (const hook of listify((_p = (_o = resource.hooks) === null || _o === void 0 ? void 0 : _o[source]) === null || _p === void 0 ? void 0 : _p.beforeDatasourceRequest)) {
576
632
  const resp = yield hook({ resource, query: body, adminUser });
577
633
  if (!resp || (!resp.ok && !resp.error)) {
578
634
  throw new Error(`Hook must return object with {ok: true} or { error: 'Error' } `);
@@ -639,7 +695,7 @@ class AdminForth {
639
695
  item[col.name] = targetDataMap[item[col.name]];
640
696
  });
641
697
  })));
642
- for (const hook of getFunctionList((_q = (_p = resource.hooks) === null || _p === void 0 ? void 0 : _p[source]) === null || _q === void 0 ? void 0 : _q.afterDatasourceResponse)) {
698
+ for (const hook of listify((_r = (_q = resource.hooks) === null || _q === void 0 ? void 0 : _q[source]) === null || _r === void 0 ? void 0 : _r.afterDatasourceResponse)) {
643
699
  const resp = yield hook({ resource, response: data.data, adminUser });
644
700
  if (!resp || (!resp.ok && !resp.error)) {
645
701
  throw new Error(`Hook must return object with {ok: true} or { error: 'Error' } `);
@@ -665,8 +721,8 @@ class AdminForth {
665
721
  server.endpoint({
666
722
  method: 'POST',
667
723
  path: '/get_resource_foreign_data',
668
- handler: (_r) => __awaiter(this, [_r], void 0, function* ({ body, adminUser }) {
669
- var _s, _t, _u, _v;
724
+ handler: (_s) => __awaiter(this, [_s], void 0, function* ({ body, adminUser }) {
725
+ var _t, _u, _v, _w;
670
726
  const { resourceId, column } = body;
671
727
  if (!this.statuses.dbDiscover) {
672
728
  return { error: 'Database discovery not started' };
@@ -687,8 +743,8 @@ class AdminForth {
687
743
  }
688
744
  const targetResourceId = columnConfig.foreignResource.resourceId;
689
745
  const targetResource = this.config.resources.find((res) => res.resourceId == targetResourceId);
690
- for (const hook of getFunctionList((_t = (_s = columnConfig.foreignResource.hooks) === null || _s === void 0 ? void 0 : _s.dropdownList) === null || _t === void 0 ? void 0 : _t.beforeDatasourceRequest)) {
691
- const resp = yield hook({ query: body, adminUser });
746
+ for (const hook of listify((_u = (_t = columnConfig.foreignResource.hooks) === null || _t === void 0 ? void 0 : _t.dropdownList) === null || _u === void 0 ? void 0 : _u.beforeDatasourceRequest)) {
747
+ const resp = yield hook({ query: body, adminUser, resource: targetResource });
692
748
  if (!resp || (!resp.ok && !resp.error)) {
693
749
  throw new Error(`Hook must return object with {ok: true} or { error: 'Error' } `);
694
750
  }
@@ -716,8 +772,8 @@ class AdminForth {
716
772
  const response = {
717
773
  items
718
774
  };
719
- for (const hook of getFunctionList((_v = (_u = columnConfig.foreignResource.hooks) === null || _u === void 0 ? void 0 : _u.dropdownList) === null || _v === void 0 ? void 0 : _v.afterDatasourceResponse)) {
720
- const resp = yield hook({ response, adminUser });
775
+ for (const hook of listify((_w = (_v = columnConfig.foreignResource.hooks) === null || _v === void 0 ? void 0 : _v.dropdownList) === null || _w === void 0 ? void 0 : _w.afterDatasourceResponse)) {
776
+ const resp = yield hook({ response, adminUser, resource: targetResource });
721
777
  if (!resp || (!resp.ok && !resp.error)) {
722
778
  throw new Error(`Hook must return object with {ok: true} or { error: 'Error' } `);
723
779
  }
@@ -731,7 +787,7 @@ class AdminForth {
731
787
  server.endpoint({
732
788
  method: 'POST',
733
789
  path: '/get_min_max_for_columns',
734
- handler: (_w) => __awaiter(this, [_w], void 0, function* ({ body }) {
790
+ handler: (_x) => __awaiter(this, [_x], void 0, function* ({ body }) {
735
791
  const { resourceId } = body;
736
792
  if (!this.statuses.dbDiscover) {
737
793
  return { error: 'Database discovery not started' };
@@ -760,8 +816,8 @@ class AdminForth {
760
816
  server.endpoint({
761
817
  method: 'POST',
762
818
  path: '/create_record',
763
- handler: (_x) => __awaiter(this, [_x], void 0, function* ({ body, adminUser }) {
764
- var _y, _z, _0, _1, _2;
819
+ handler: (_y) => __awaiter(this, [_y], void 0, function* ({ body, adminUser }) {
820
+ var _z, _0, _1, _2, _3;
765
821
  console.log('create_record', body, this.config.resources);
766
822
  const resource = this.config.resources.find((res) => res.resourceId == body['resourceId']);
767
823
  if (!resource) {
@@ -769,7 +825,7 @@ class AdminForth {
769
825
  }
770
826
  const record = body['record'];
771
827
  // execute hook if needed
772
- for (const hook of getFunctionList((_z = (_y = resource.hooks) === null || _y === void 0 ? void 0 : _y.create) === null || _z === void 0 ? void 0 : _z.beforeSave)) {
828
+ for (const hook of listify((_0 = (_z = resource.hooks) === null || _z === void 0 ? void 0 : _z.create) === null || _0 === void 0 ? void 0 : _0.beforeSave)) {
773
829
  const resp = yield hook({ resource, record, adminUser });
774
830
  if (!resp || (!resp.ok && !resp.error)) {
775
831
  throw new Error(`Hook beforeSave must return object with {ok: true} or { error: 'Error' } `);
@@ -786,7 +842,7 @@ class AdminForth {
786
842
  });
787
843
  }
788
844
  }
789
- if (((_0 = column.required) === null || _0 === void 0 ? void 0 : _0.create) && body['record'][column.name] === undefined) {
845
+ if (((_1 = column.required) === null || _1 === void 0 ? void 0 : _1.create) && body['record'][column.name] === undefined) {
790
846
  return { error: `Column '${column.name}' is required` };
791
847
  }
792
848
  if (column.isUnique) {
@@ -811,7 +867,7 @@ class AdminForth {
811
867
  const connector = this.connectors[resource.dataSource];
812
868
  yield connector.createRecord({ resource, record });
813
869
  // execute hook if needed
814
- for (const hook of getFunctionList((_2 = (_1 = resource.hooks) === null || _1 === void 0 ? void 0 : _1.create) === null || _2 === void 0 ? void 0 : _2.afterSave)) {
870
+ for (const hook of listify((_3 = (_2 = resource.hooks) === null || _2 === void 0 ? void 0 : _2.create) === null || _3 === void 0 ? void 0 : _3.afterSave)) {
815
871
  const resp = yield hook({ resource, record, adminUser });
816
872
  if (!resp || (!resp.ok && !resp.error)) {
817
873
  throw new Error(`Hook afterSave must return object with {ok: true} or { error: 'Error' } `);
@@ -828,8 +884,8 @@ class AdminForth {
828
884
  server.endpoint({
829
885
  method: 'POST',
830
886
  path: '/update_record',
831
- handler: (_3) => __awaiter(this, [_3], void 0, function* ({ body, adminUser }) {
832
- var _4, _5, _6, _7;
887
+ handler: (_4) => __awaiter(this, [_4], void 0, function* ({ body, adminUser }) {
888
+ var _5, _6, _7, _8;
833
889
  const resource = this.config.resources.find((res) => res.resourceId == body['resourceId']);
834
890
  if (!resource) {
835
891
  return { error: `Resource '${body['resourceId']}' not found` };
@@ -843,7 +899,7 @@ class AdminForth {
843
899
  }
844
900
  const record = body['record'];
845
901
  // execute hook if needed
846
- for (const hook of getFunctionList((_5 = (_4 = resource.hooks) === null || _4 === void 0 ? void 0 : _4.edit) === null || _5 === void 0 ? void 0 : _5.beforeSave)) {
902
+ for (const hook of listify((_6 = (_5 = resource.hooks) === null || _5 === void 0 ? void 0 : _5.edit) === null || _6 === void 0 ? void 0 : _6.beforeSave)) {
847
903
  const resp = yield hook({ resource, record, adminUser });
848
904
  if (!resp || (!resp.ok && !resp.error)) {
849
905
  throw new Error(`Hook beforeSave must return object with {ok: true} or { error: 'Error' } `);
@@ -871,7 +927,7 @@ class AdminForth {
871
927
  yield connector.updateRecord({ resource, recordId, record, newValues });
872
928
  }
873
929
  // execute hook if needed
874
- for (const hook of getFunctionList((_7 = (_6 = resource.hooks) === null || _6 === void 0 ? void 0 : _6.edit) === null || _7 === void 0 ? void 0 : _7.afterSave)) {
930
+ for (const hook of listify((_8 = (_7 = resource.hooks) === null || _7 === void 0 ? void 0 : _7.edit) === null || _8 === void 0 ? void 0 : _8.afterSave)) {
875
931
  const resp = yield hook({ resource, record, adminUser });
876
932
  if (!resp || (!resp.ok && !resp.error)) {
877
933
  throw new Error(`Hook afterSave must return object with {ok: true} or { error: 'Error' } `);
@@ -888,8 +944,8 @@ class AdminForth {
888
944
  server.endpoint({
889
945
  method: 'POST',
890
946
  path: '/delete_record',
891
- handler: (_8) => __awaiter(this, [_8], void 0, function* ({ body, adminUser }) {
892
- var _9, _10, _11, _12;
947
+ handler: (_9) => __awaiter(this, [_9], void 0, function* ({ body, adminUser }) {
948
+ var _10, _11, _12, _13;
893
949
  const resource = this.config.resources.find((res) => res.resourceId == body['resourceId']);
894
950
  const record = yield this.connectors[resource.dataSource].getRecordByPrimaryKey(resource, body['primaryKey']);
895
951
  if (!resource) {
@@ -902,7 +958,7 @@ class AdminForth {
902
958
  return { error: `Resource '${resource.resourceId}' does not allow delete action` };
903
959
  }
904
960
  // execute hook if needed
905
- for (const hook of getFunctionList((_10 = (_9 = resource.hooks) === null || _9 === void 0 ? void 0 : _9.delete) === null || _10 === void 0 ? void 0 : _10.beforeSave)) {
961
+ for (const hook of listify((_11 = (_10 = resource.hooks) === null || _10 === void 0 ? void 0 : _10.delete) === null || _11 === void 0 ? void 0 : _11.beforeSave)) {
906
962
  const resp = yield hook({ resource, record, adminUser });
907
963
  if (!resp || (!resp.ok && !resp.error)) {
908
964
  throw new Error(`Hook beforeSave must return object with {ok: true} or { error: 'Error' } `);
@@ -914,7 +970,7 @@ class AdminForth {
914
970
  const connector = this.connectors[resource.dataSource];
915
971
  yield connector.deleteRecord({ resource, recordId: body['primaryKey'] });
916
972
  // execute hook if needed
917
- for (const hook of getFunctionList((_12 = (_11 = resource.hooks) === null || _11 === void 0 ? void 0 : _11.delete) === null || _12 === void 0 ? void 0 : _12.afterSave)) {
973
+ for (const hook of listify((_13 = (_12 = resource.hooks) === null || _12 === void 0 ? void 0 : _12.delete) === null || _13 === void 0 ? void 0 : _13.afterSave)) {
918
974
  const resp = yield hook({ resource, record, adminUser });
919
975
  if (!resp || (!resp.ok && !resp.error)) {
920
976
  throw new Error(`Hook afterSave must return object with {ok: true} or { error: 'Error' } `);
@@ -931,7 +987,7 @@ class AdminForth {
931
987
  server.endpoint({
932
988
  method: 'POST',
933
989
  path: '/start_bulk_action',
934
- handler: (_13) => __awaiter(this, [_13], void 0, function* ({ body }) {
990
+ handler: (_14) => __awaiter(this, [_14], void 0, function* ({ body }) {
935
991
  const { resourceId, actionId, recordIds } = body;
936
992
  const resource = this.config.resources.find((res) => res.resourceId == resourceId);
937
993
  if (!resource) {
@@ -962,7 +1018,7 @@ _a = AdminForth, _AdminForth_defaultConfig = new WeakMap();
962
1018
  AdminForth.Types = AdminForthDataTypes;
963
1019
  AdminForth.Utils = {
964
1020
  generatePasswordHash: (password) => __awaiter(void 0, void 0, void 0, function* () {
965
- return yield Auth.generatePasswordHash(password);
1021
+ return yield AdminForthAuth.generatePasswordHash(password);
966
1022
  })
967
1023
  };
968
1024
  export default AdminForth;
@@ -254,7 +254,6 @@ class CodeInjector {
254
254
  });
255
255
  });
256
256
  });
257
- console.log('🔧 🔧 Injecting code into Vue sources...', customResourceComponents);
258
257
  customResourceComponents.forEach((filePath) => {
259
258
  const componentName = getComponentNameFromPath(filePath);
260
259
  this.allComponentNames[filePath] = componentName;
@@ -28,16 +28,6 @@ export const ADMINFORTH_VERSION = package_json.version;
28
28
  export function getComponentNameFromPath(filePath) {
29
29
  return filePath.replace(/@/g, '').replace(/\./g, '').replace(/\//g, '');
30
30
  }
31
- export function getFunctionList(param) {
32
- if (param) {
33
- if (Array.isArray(param)) {
34
- return param;
35
- }
36
- else {
37
- return [param];
38
- }
39
- }
40
- else {
41
- return [];
42
- }
31
+ export function listify(param) {
32
+ return param || [];
43
33
  }
@@ -0,0 +1,66 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { AllowedActionsEnum } from "../../types/AdminForthConfig.js";
11
+ import AdminForthPlugin from "../base.js";
12
+ class AccessControlPlugin extends AdminForthPlugin {
13
+ constructor(options) {
14
+ super(options, import.meta.url);
15
+ this.options = options;
16
+ }
17
+ modifyResourceConfig(adminforth, resourceConfig) {
18
+ super.modifyResourceConfig(adminforth, resourceConfig);
19
+ this.adminforth = adminforth;
20
+ if (!resourceConfig.hooks) {
21
+ resourceConfig.hooks = {};
22
+ }
23
+ const checkAccess = (adminUser, action, meta) => __awaiter(this, void 0, void 0, function* () {
24
+ const hasAccessOrError = yield this.options.hasAccess(adminUser, action, meta);
25
+ if (hasAccessOrError === true) {
26
+ return { ok: true };
27
+ }
28
+ else {
29
+ return { ok: false, error: hasAccessOrError || AccessControlPlugin.defaultError };
30
+ }
31
+ });
32
+ const bindHookCheck = (action, hookName) => {
33
+ if (!resourceConfig.hooks[action]) {
34
+ resourceConfig.hooks[action] = {};
35
+ }
36
+ if (!resourceConfig.hooks[action][hookName]) {
37
+ resourceConfig.hooks[action][hookName] = [];
38
+ }
39
+ if (hookName === 'beforeDatasourceRequest') {
40
+ resourceConfig.hooks[action][hookName].unshift((_a) => __awaiter(this, [_a], void 0, function* ({ adminUser, query }) {
41
+ return checkAccess(adminUser, action, { query });
42
+ }));
43
+ }
44
+ else {
45
+ resourceConfig.hooks[action][hookName].unshift((_b) => __awaiter(this, [_b], void 0, function* ({ adminUser, record }) {
46
+ return checkAccess(adminUser, action, { record });
47
+ }));
48
+ }
49
+ };
50
+ // List check
51
+ bindHookCheck(AllowedActionsEnum.list, 'beforeDatasourceRequest');
52
+ // Show check
53
+ bindHookCheck(AllowedActionsEnum.show, 'beforeDatasourceRequest');
54
+ // Edit check
55
+ bindHookCheck(AllowedActionsEnum.edit, 'beforeDatasourceRequest');
56
+ // create check
57
+ bindHookCheck(AllowedActionsEnum.create, 'beforeSave');
58
+ // edit check
59
+ bindHookCheck(AllowedActionsEnum.edit, 'beforeSave');
60
+ // delete check
61
+ bindHookCheck(AllowedActionsEnum.delete, 'beforeSave');
62
+ console.log('resourceConfig', resourceConfig);
63
+ }
64
+ }
65
+ AccessControlPlugin.defaultError = 'Sorry, you do not have access to this resource.';
66
+ export default AccessControlPlugin;
@@ -0,0 +1 @@
1
+ export {};
@@ -24,7 +24,7 @@ export default class ForeignInlineListPlugin extends AdminForthPlugin {
24
24
  return { error: `Resource ${this.options.foreignResourceId} not found` };
25
25
  }
26
26
  // exclude "plugins" key
27
- const resourceCopy = Object.assign(Object.assign({}, resource), { plugins: undefined });
27
+ const resourceCopy = JSON.parse(JSON.stringify(Object.assign(Object.assign({}, resource), { plugins: undefined })));
28
28
  if (this.options.modifyTableResourceConfig) {
29
29
  this.options.modifyTableResourceConfig(resourceCopy);
30
30
  }