adminforth 1.1.13 → 1.1.14
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 +99 -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/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 +50 -0
- package/dist/spa/spa/src/utils.ts +8 -3
- 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/types/AdminForthConfig.js +9 -0
- package/dist/types/FrontendAPI.js +4 -4
- package/index.ts +91 -42
- package/modules/codeInjector.ts +0 -1
- package/modules/utils.ts +2 -10
- package/package.json +1 -1
- 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 +50 -0
- package/spa/src/utils.ts +8 -3
- 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/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`);
|
|
@@ -254,6 +253,37 @@ class AdminForth {
|
|
|
254
253
|
else {
|
|
255
254
|
res.options.allowedActions = DEFAULT_ALLOWED_ACTIONS;
|
|
256
255
|
}
|
|
256
|
+
// transform all hooks Functions to array of functions
|
|
257
|
+
if (res.hooks) {
|
|
258
|
+
for (const value of [res.hooks.show, res.hooks.list]) {
|
|
259
|
+
if (value) {
|
|
260
|
+
if (value.beforeDatasourceRequest) {
|
|
261
|
+
if (!Array.isArray(value.beforeDatasourceRequest)) {
|
|
262
|
+
value.beforeDatasourceRequest = [value.beforeDatasourceRequest];
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
if (value.afterDatasourceResponse) {
|
|
266
|
+
if (!Array.isArray(value.afterDatasourceResponse)) {
|
|
267
|
+
value.afterDatasourceResponse = [value.afterDatasourceResponse];
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
for (const value of [res.hooks.create, res.hooks.edit, res.hooks.delete]) {
|
|
273
|
+
if (value) {
|
|
274
|
+
if (value.beforeSave) {
|
|
275
|
+
if (!Array.isArray(value.beforeSave)) {
|
|
276
|
+
value.beforeSave = [value.beforeSave];
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
if (value.afterSave) {
|
|
280
|
+
if (!Array.isArray(value.afterSave)) {
|
|
281
|
+
value.afterSave = [value.afterSave];
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
257
287
|
});
|
|
258
288
|
if (!this.config.menu) {
|
|
259
289
|
errors.push('No config.menu defined');
|
|
@@ -315,7 +345,6 @@ class AdminForth {
|
|
|
315
345
|
for (const resource of this.config.resources) {
|
|
316
346
|
for (const column of resource.columns) {
|
|
317
347
|
if (column.components) {
|
|
318
|
-
console.log('🔧🔧🔧 Validating components for resource', column.components);
|
|
319
348
|
for (const [key, comp] of Object.entries(column.components)) {
|
|
320
349
|
let ignoreExistsCheck = false;
|
|
321
350
|
if (this.codeInjector.allComponentNames[comp.file]) {
|
|
@@ -393,6 +422,24 @@ class AdminForth {
|
|
|
393
422
|
this.codeInjector.bundleNow({ hotReload, verbose });
|
|
394
423
|
});
|
|
395
424
|
}
|
|
425
|
+
getUserByPk(pk) {
|
|
426
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
427
|
+
const resource = this.config.resources.find((res) => res.resourceId === this.config.auth.resourceId);
|
|
428
|
+
if (!resource) {
|
|
429
|
+
throw new Error('No auth resource found');
|
|
430
|
+
}
|
|
431
|
+
const users = yield this.connectors[resource.dataSource].getData({
|
|
432
|
+
resource,
|
|
433
|
+
filters: [
|
|
434
|
+
{ field: resource.columns.find((col) => col.primaryKey).name, operator: AdminForthFilterOperators.EQ, value: pk },
|
|
435
|
+
],
|
|
436
|
+
limit: 1,
|
|
437
|
+
offset: 0,
|
|
438
|
+
sort: [],
|
|
439
|
+
});
|
|
440
|
+
return users.data[0] || null;
|
|
441
|
+
});
|
|
442
|
+
}
|
|
396
443
|
setupEndpoints(server) {
|
|
397
444
|
server.endpoint({
|
|
398
445
|
noAuth: true,
|
|
@@ -436,7 +483,7 @@ class AdminForth {
|
|
|
436
483
|
}
|
|
437
484
|
const passwordHash = userRecord[this.config.auth.passwordHashField];
|
|
438
485
|
console.log('User record', userRecord, passwordHash); // why does it has no hash?
|
|
439
|
-
const valid = yield
|
|
486
|
+
const valid = yield AdminForthAuth.verifyPassword(password, passwordHash);
|
|
440
487
|
if (valid) {
|
|
441
488
|
token = this.auth.issueJWT({
|
|
442
489
|
username, pk: userRecord[userResource.columns.find((col) => col.primaryKey).name]
|
|
@@ -450,11 +497,18 @@ class AdminForth {
|
|
|
450
497
|
return { ok: true };
|
|
451
498
|
}),
|
|
452
499
|
});
|
|
500
|
+
server.endpoint({
|
|
501
|
+
method: 'POST',
|
|
502
|
+
path: '/check_auth',
|
|
503
|
+
handler: (_d) => __awaiter(this, [_d], void 0, function* ({ adminUser }) {
|
|
504
|
+
return { ok: true };
|
|
505
|
+
}),
|
|
506
|
+
});
|
|
453
507
|
server.endpoint({
|
|
454
508
|
noAuth: true,
|
|
455
509
|
method: 'POST',
|
|
456
510
|
path: '/logout',
|
|
457
|
-
handler: (
|
|
511
|
+
handler: (_e) => __awaiter(this, [_e], void 0, function* ({ response }) {
|
|
458
512
|
response.setHeader('Set-Cookie', `adminforth_jwt=; Path=${this.config.baseUrl || '/'}; HttpOnly; SameSite=Strict; Expires=Thu, 01 Jan 1970 00:00:00 GMT`);
|
|
459
513
|
return { ok: true };
|
|
460
514
|
}),
|
|
@@ -463,8 +517,8 @@ class AdminForth {
|
|
|
463
517
|
noAuth: true,
|
|
464
518
|
method: 'GET',
|
|
465
519
|
path: '/get_public_config',
|
|
466
|
-
handler: (
|
|
467
|
-
var
|
|
520
|
+
handler: (_f) => __awaiter(this, [_f], void 0, function* ({ body }) {
|
|
521
|
+
var _g;
|
|
468
522
|
// find resource
|
|
469
523
|
if (!this.config.auth) {
|
|
470
524
|
throw new Error('No config.auth defined');
|
|
@@ -476,37 +530,24 @@ class AdminForth {
|
|
|
476
530
|
brandName: this.config.customization.brandName,
|
|
477
531
|
usernameFieldName: usernameColumn.label,
|
|
478
532
|
loginBackgroundImage: this.config.auth.loginBackgroundImage,
|
|
479
|
-
title: (
|
|
533
|
+
title: (_g = this.config.customization) === null || _g === void 0 ? void 0 : _g.title,
|
|
480
534
|
};
|
|
481
535
|
}),
|
|
482
536
|
});
|
|
483
537
|
server.endpoint({
|
|
484
538
|
method: 'GET',
|
|
485
539
|
path: '/get_base_config',
|
|
486
|
-
handler: (
|
|
487
|
-
var
|
|
488
|
-
const cookieParsed = this.auth.verify(cookies['adminforth_jwt']);
|
|
540
|
+
handler: (_h) => __awaiter(this, [_h], void 0, function* ({ input, adminUser, cookies }) {
|
|
541
|
+
var _j, _k;
|
|
489
542
|
let username = '';
|
|
490
543
|
let userFullName = '';
|
|
491
|
-
if (
|
|
544
|
+
if (adminUser.isRoot) {
|
|
492
545
|
username = this.config.rootUser.username;
|
|
493
546
|
}
|
|
494
547
|
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];
|
|
548
|
+
const dbUser = adminUser.dbUser;
|
|
549
|
+
username = dbUser[this.config.auth.usernameField];
|
|
550
|
+
userFullName = dbUser[this.config.auth.userFullNameField];
|
|
510
551
|
}
|
|
511
552
|
const userData = {
|
|
512
553
|
[this.config.auth.usernameField]: username,
|
|
@@ -526,8 +567,8 @@ class AdminForth {
|
|
|
526
567
|
deleteConfirmation: this.config.deleteConfirmation,
|
|
527
568
|
auth: this.config.auth,
|
|
528
569
|
usernameField: this.config.auth.usernameField,
|
|
529
|
-
title: (
|
|
530
|
-
emptyFieldPlaceholder: (
|
|
570
|
+
title: (_j = this.config.customization) === null || _j === void 0 ? void 0 : _j.title,
|
|
571
|
+
emptyFieldPlaceholder: (_k = this.config.customization) === null || _k === void 0 ? void 0 : _k.emptyFieldPlaceholder,
|
|
531
572
|
},
|
|
532
573
|
adminUser,
|
|
533
574
|
version: ADMINFORTH_VERSION,
|
|
@@ -537,7 +578,7 @@ class AdminForth {
|
|
|
537
578
|
server.endpoint({
|
|
538
579
|
method: 'POST',
|
|
539
580
|
path: '/get_resource',
|
|
540
|
-
handler: (
|
|
581
|
+
handler: (_l) => __awaiter(this, [_l], void 0, function* ({ body }) {
|
|
541
582
|
const { resourceId } = body;
|
|
542
583
|
if (!this.statuses.dbDiscover) {
|
|
543
584
|
return { error: 'Database discovery not started' };
|
|
@@ -556,8 +597,8 @@ class AdminForth {
|
|
|
556
597
|
server.endpoint({
|
|
557
598
|
method: 'POST',
|
|
558
599
|
path: '/get_resource_data',
|
|
559
|
-
handler: (
|
|
560
|
-
var
|
|
600
|
+
handler: (_m) => __awaiter(this, [_m], void 0, function* ({ body, adminUser }) {
|
|
601
|
+
var _o, _p, _q, _r;
|
|
561
602
|
const { resourceId, source } = body;
|
|
562
603
|
if (['show', 'list'].includes(source) === false) {
|
|
563
604
|
return { error: 'Invalid source, should be list or show' };
|
|
@@ -572,7 +613,7 @@ class AdminForth {
|
|
|
572
613
|
if (!resource) {
|
|
573
614
|
return { error: `Resource ${resourceId} not found` };
|
|
574
615
|
}
|
|
575
|
-
for (const hook of
|
|
616
|
+
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
617
|
const resp = yield hook({ resource, query: body, adminUser });
|
|
577
618
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
578
619
|
throw new Error(`Hook must return object with {ok: true} or { error: 'Error' } `);
|
|
@@ -639,7 +680,7 @@ class AdminForth {
|
|
|
639
680
|
item[col.name] = targetDataMap[item[col.name]];
|
|
640
681
|
});
|
|
641
682
|
})));
|
|
642
|
-
for (const hook of
|
|
683
|
+
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
684
|
const resp = yield hook({ resource, response: data.data, adminUser });
|
|
644
685
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
645
686
|
throw new Error(`Hook must return object with {ok: true} or { error: 'Error' } `);
|
|
@@ -665,8 +706,8 @@ class AdminForth {
|
|
|
665
706
|
server.endpoint({
|
|
666
707
|
method: 'POST',
|
|
667
708
|
path: '/get_resource_foreign_data',
|
|
668
|
-
handler: (
|
|
669
|
-
var
|
|
709
|
+
handler: (_s) => __awaiter(this, [_s], void 0, function* ({ body, adminUser }) {
|
|
710
|
+
var _t, _u, _v, _w;
|
|
670
711
|
const { resourceId, column } = body;
|
|
671
712
|
if (!this.statuses.dbDiscover) {
|
|
672
713
|
return { error: 'Database discovery not started' };
|
|
@@ -687,8 +728,8 @@ class AdminForth {
|
|
|
687
728
|
}
|
|
688
729
|
const targetResourceId = columnConfig.foreignResource.resourceId;
|
|
689
730
|
const targetResource = this.config.resources.find((res) => res.resourceId == targetResourceId);
|
|
690
|
-
for (const hook of
|
|
691
|
-
const resp = yield hook({ query: body, adminUser });
|
|
731
|
+
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)) {
|
|
732
|
+
const resp = yield hook({ query: body, adminUser, resource: targetResource });
|
|
692
733
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
693
734
|
throw new Error(`Hook must return object with {ok: true} or { error: 'Error' } `);
|
|
694
735
|
}
|
|
@@ -716,8 +757,8 @@ class AdminForth {
|
|
|
716
757
|
const response = {
|
|
717
758
|
items
|
|
718
759
|
};
|
|
719
|
-
for (const hook of
|
|
720
|
-
const resp = yield hook({ response, adminUser });
|
|
760
|
+
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)) {
|
|
761
|
+
const resp = yield hook({ response, adminUser, resource: targetResource });
|
|
721
762
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
722
763
|
throw new Error(`Hook must return object with {ok: true} or { error: 'Error' } `);
|
|
723
764
|
}
|
|
@@ -731,7 +772,7 @@ class AdminForth {
|
|
|
731
772
|
server.endpoint({
|
|
732
773
|
method: 'POST',
|
|
733
774
|
path: '/get_min_max_for_columns',
|
|
734
|
-
handler: (
|
|
775
|
+
handler: (_x) => __awaiter(this, [_x], void 0, function* ({ body }) {
|
|
735
776
|
const { resourceId } = body;
|
|
736
777
|
if (!this.statuses.dbDiscover) {
|
|
737
778
|
return { error: 'Database discovery not started' };
|
|
@@ -760,8 +801,8 @@ class AdminForth {
|
|
|
760
801
|
server.endpoint({
|
|
761
802
|
method: 'POST',
|
|
762
803
|
path: '/create_record',
|
|
763
|
-
handler: (
|
|
764
|
-
var
|
|
804
|
+
handler: (_y) => __awaiter(this, [_y], void 0, function* ({ body, adminUser }) {
|
|
805
|
+
var _z, _0, _1, _2, _3;
|
|
765
806
|
console.log('create_record', body, this.config.resources);
|
|
766
807
|
const resource = this.config.resources.find((res) => res.resourceId == body['resourceId']);
|
|
767
808
|
if (!resource) {
|
|
@@ -769,7 +810,7 @@ class AdminForth {
|
|
|
769
810
|
}
|
|
770
811
|
const record = body['record'];
|
|
771
812
|
// execute hook if needed
|
|
772
|
-
for (const hook of
|
|
813
|
+
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
814
|
const resp = yield hook({ resource, record, adminUser });
|
|
774
815
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
775
816
|
throw new Error(`Hook beforeSave must return object with {ok: true} or { error: 'Error' } `);
|
|
@@ -786,7 +827,7 @@ class AdminForth {
|
|
|
786
827
|
});
|
|
787
828
|
}
|
|
788
829
|
}
|
|
789
|
-
if (((
|
|
830
|
+
if (((_1 = column.required) === null || _1 === void 0 ? void 0 : _1.create) && body['record'][column.name] === undefined) {
|
|
790
831
|
return { error: `Column '${column.name}' is required` };
|
|
791
832
|
}
|
|
792
833
|
if (column.isUnique) {
|
|
@@ -811,7 +852,7 @@ class AdminForth {
|
|
|
811
852
|
const connector = this.connectors[resource.dataSource];
|
|
812
853
|
yield connector.createRecord({ resource, record });
|
|
813
854
|
// execute hook if needed
|
|
814
|
-
for (const hook of
|
|
855
|
+
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
856
|
const resp = yield hook({ resource, record, adminUser });
|
|
816
857
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
817
858
|
throw new Error(`Hook afterSave must return object with {ok: true} or { error: 'Error' } `);
|
|
@@ -828,8 +869,8 @@ class AdminForth {
|
|
|
828
869
|
server.endpoint({
|
|
829
870
|
method: 'POST',
|
|
830
871
|
path: '/update_record',
|
|
831
|
-
handler: (
|
|
832
|
-
var
|
|
872
|
+
handler: (_4) => __awaiter(this, [_4], void 0, function* ({ body, adminUser }) {
|
|
873
|
+
var _5, _6, _7, _8;
|
|
833
874
|
const resource = this.config.resources.find((res) => res.resourceId == body['resourceId']);
|
|
834
875
|
if (!resource) {
|
|
835
876
|
return { error: `Resource '${body['resourceId']}' not found` };
|
|
@@ -843,7 +884,7 @@ class AdminForth {
|
|
|
843
884
|
}
|
|
844
885
|
const record = body['record'];
|
|
845
886
|
// execute hook if needed
|
|
846
|
-
for (const hook of
|
|
887
|
+
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
888
|
const resp = yield hook({ resource, record, adminUser });
|
|
848
889
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
849
890
|
throw new Error(`Hook beforeSave must return object with {ok: true} or { error: 'Error' } `);
|
|
@@ -871,7 +912,7 @@ class AdminForth {
|
|
|
871
912
|
yield connector.updateRecord({ resource, recordId, record, newValues });
|
|
872
913
|
}
|
|
873
914
|
// execute hook if needed
|
|
874
|
-
for (const hook of
|
|
915
|
+
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
916
|
const resp = yield hook({ resource, record, adminUser });
|
|
876
917
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
877
918
|
throw new Error(`Hook afterSave must return object with {ok: true} or { error: 'Error' } `);
|
|
@@ -888,8 +929,8 @@ class AdminForth {
|
|
|
888
929
|
server.endpoint({
|
|
889
930
|
method: 'POST',
|
|
890
931
|
path: '/delete_record',
|
|
891
|
-
handler: (
|
|
892
|
-
var
|
|
932
|
+
handler: (_9) => __awaiter(this, [_9], void 0, function* ({ body, adminUser }) {
|
|
933
|
+
var _10, _11, _12, _13;
|
|
893
934
|
const resource = this.config.resources.find((res) => res.resourceId == body['resourceId']);
|
|
894
935
|
const record = yield this.connectors[resource.dataSource].getRecordByPrimaryKey(resource, body['primaryKey']);
|
|
895
936
|
if (!resource) {
|
|
@@ -902,7 +943,7 @@ class AdminForth {
|
|
|
902
943
|
return { error: `Resource '${resource.resourceId}' does not allow delete action` };
|
|
903
944
|
}
|
|
904
945
|
// execute hook if needed
|
|
905
|
-
for (const hook of
|
|
946
|
+
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
947
|
const resp = yield hook({ resource, record, adminUser });
|
|
907
948
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
908
949
|
throw new Error(`Hook beforeSave must return object with {ok: true} or { error: 'Error' } `);
|
|
@@ -914,7 +955,7 @@ class AdminForth {
|
|
|
914
955
|
const connector = this.connectors[resource.dataSource];
|
|
915
956
|
yield connector.deleteRecord({ resource, recordId: body['primaryKey'] });
|
|
916
957
|
// execute hook if needed
|
|
917
|
-
for (const hook of
|
|
958
|
+
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
959
|
const resp = yield hook({ resource, record, adminUser });
|
|
919
960
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
920
961
|
throw new Error(`Hook afterSave must return object with {ok: true} or { error: 'Error' } `);
|
|
@@ -931,7 +972,7 @@ class AdminForth {
|
|
|
931
972
|
server.endpoint({
|
|
932
973
|
method: 'POST',
|
|
933
974
|
path: '/start_bulk_action',
|
|
934
|
-
handler: (
|
|
975
|
+
handler: (_14) => __awaiter(this, [_14], void 0, function* ({ body }) {
|
|
935
976
|
const { resourceId, actionId, recordIds } = body;
|
|
936
977
|
const resource = this.config.resources.find((res) => res.resourceId == resourceId);
|
|
937
978
|
if (!resource) {
|
|
@@ -962,7 +1003,7 @@ _a = AdminForth, _AdminForth_defaultConfig = new WeakMap();
|
|
|
962
1003
|
AdminForth.Types = AdminForthDataTypes;
|
|
963
1004
|
AdminForth.Utils = {
|
|
964
1005
|
generatePasswordHash: (password) => __awaiter(void 0, void 0, void 0, function* () {
|
|
965
|
-
return yield
|
|
1006
|
+
return yield AdminForthAuth.generatePasswordHash(password);
|
|
966
1007
|
})
|
|
967
1008
|
};
|
|
968
1009
|
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
|
}
|
package/dist/servers/express.js
CHANGED
|
@@ -149,7 +149,7 @@ class ExpressServer {
|
|
|
149
149
|
res.status(401).send('Unauthorized by AdminForth');
|
|
150
150
|
return;
|
|
151
151
|
}
|
|
152
|
-
const adminforthUser = this.adminforth.auth.verify(jwt);
|
|
152
|
+
const adminforthUser = yield this.adminforth.auth.verify(jwt);
|
|
153
153
|
if (!adminforthUser) {
|
|
154
154
|
res.status(401).send('Unauthorized by AdminForth');
|
|
155
155
|
}
|
|
@@ -191,7 +191,7 @@ class ExpressServer {
|
|
|
191
191
|
this.message = message;
|
|
192
192
|
}
|
|
193
193
|
};
|
|
194
|
-
const input = { body, query, headers, cookies, response, _raw_express_req: req, _raw_express_res: res };
|
|
194
|
+
const input = { body, query, headers, cookies, adminUser, response, _raw_express_req: req, _raw_express_res: res };
|
|
195
195
|
let output;
|
|
196
196
|
try {
|
|
197
197
|
output = yield handler(input);
|