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.
- package/auth.ts +17 -3
- package/dist/auth.js +32 -18
- package/dist/index.js +114 -58
- package/dist/modules/codeInjector.js +0 -1
- package/dist/modules/utils.js +2 -12
- package/dist/plugins/AccessControl/index.js +66 -0
- package/dist/plugins/AccessControl/types.js +1 -0
- package/dist/plugins/ForeignInlineListPlugin/index.js +1 -1
- package/dist/plugins/plugins/ForeignInlineListPlugin/custom/InlineList.vue +248 -0
- package/dist/servers/express.js +2 -2
- package/dist/spa/spa/src/App.vue +7 -3
- package/dist/spa/spa/src/components/Toast.vue +5 -4
- package/dist/spa/spa/src/components/ValueRenderer.vue +1 -1
- package/dist/spa/spa/src/composables/useStores.ts +2 -1
- package/dist/spa/spa/src/router/index.ts +13 -1
- package/dist/spa/spa/src/stores/core.ts +13 -11
- package/dist/spa/spa/src/stores/user.ts +54 -0
- package/dist/spa/spa/src/utils.ts +9 -4
- package/dist/spa/spa/src/views/CreateView.vue +7 -0
- package/dist/spa/spa/src/views/EditView.vue +8 -1
- package/dist/spa/spa/src/views/ListView.vue +10 -1
- package/dist/spa/spa/src/views/LoginView.vue +4 -0
- package/dist/types/AdminForthConfig.js +9 -0
- package/dist/types/FrontendAPI.js +4 -4
- package/index.ts +106 -42
- package/modules/codeInjector.ts +0 -1
- package/modules/utils.ts +2 -10
- package/package.json +3 -2
- package/plugins/AccessControl/index.ts +83 -0
- package/plugins/AccessControl/types.ts +14 -0
- package/plugins/ForeignInlineListPlugin/custom/InlineList.vue +11 -0
- package/plugins/ForeignInlineListPlugin/index.ts +2 -1
- package/servers/express.ts +2 -2
- package/spa/src/App.vue +7 -3
- package/spa/src/components/Toast.vue +5 -4
- package/spa/src/components/ValueRenderer.vue +1 -1
- package/spa/src/composables/useStores.ts +2 -1
- package/spa/src/router/index.ts +13 -1
- package/spa/src/stores/core.ts +13 -11
- package/spa/src/stores/user.ts +54 -0
- package/spa/src/utils.ts +9 -4
- package/spa/src/views/CreateView.vue +7 -0
- package/spa/src/views/EditView.vue +8 -1
- package/spa/src/views/ListView.vue +10 -1
- package/spa/src/views/LoginView.vue +4 -0
- package/types/AdminForthConfig.ts +73 -21
- 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
|
-
|
|
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
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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
|
-
|
|
53
|
-
|
|
64
|
+
const { pk } = decoded;
|
|
65
|
+
if (pk === null) {
|
|
66
|
+
decoded.isRoot = true;
|
|
54
67
|
}
|
|
55
68
|
else {
|
|
56
|
-
|
|
69
|
+
const dbUser = yield this.adminforth.getUserByPk(pk);
|
|
70
|
+
decoded.dbUser = dbUser;
|
|
57
71
|
}
|
|
58
|
-
return
|
|
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
|
|
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
|
|
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
|
|
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: (
|
|
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: (
|
|
467
|
-
var
|
|
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: (
|
|
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: (
|
|
487
|
-
var
|
|
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 (
|
|
559
|
+
if (adminUser.isRoot) {
|
|
492
560
|
username = this.config.rootUser.username;
|
|
493
561
|
}
|
|
494
562
|
else {
|
|
495
|
-
const
|
|
496
|
-
|
|
497
|
-
|
|
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: (
|
|
530
|
-
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: (
|
|
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: (
|
|
560
|
-
var
|
|
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
|
|
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
|
|
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: (
|
|
669
|
-
var
|
|
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
|
|
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
|
|
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: (
|
|
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: (
|
|
764
|
-
var
|
|
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
|
|
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 (((
|
|
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
|
|
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: (
|
|
832
|
-
var
|
|
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
|
|
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
|
|
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: (
|
|
892
|
-
var
|
|
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
|
|
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
|
|
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: (
|
|
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
|
|
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;
|
package/dist/modules/utils.js
CHANGED
|
@@ -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
|
|
32
|
-
|
|
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
|
}
|