adminizer 5.0.0-build.9 → 5.1.0-build.25
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/assets/abnfDiagram-VCTEODGH-BTgLIXrb.js +1 -0
- package/assets/add-C8V8qjvm.js +1 -0
- package/assets/add-group-Ezq_Ocot.js +1 -0
- package/assets/add-user-CluNf2QG.js +1 -0
- package/assets/ai-assistant/agent.es.js +28464 -0
- package/assets/app-C96pcs-J.js +138 -0
- package/assets/app-DPmI-MvN.css +1 -0
- package/assets/arc-G6n0TtRf.js +1 -0
- package/assets/architectureDiagram-5GKGNRK7-TQ4_iSAG.js +36 -0
- package/assets/asmcrypto-DZeQH0LC.js +1 -0
- package/assets/blockDiagram-I7D4REHJ-1q050zQd.js +129 -0
- package/assets/c4Diagram-7LVT6UL2-BXn0hW62.js +38 -0
- package/assets/{catalog-dk6a0I1u.js → catalog-Cpx4OH_r.js} +3 -3
- package/assets/channel-JZzXP_E5.js +1 -0
- package/assets/chunk-2Q5K7J3B-BSnocdBz.js +1 -0
- package/assets/chunk-5VM5RSS4-BvfoMSpq.js +15 -0
- package/assets/chunk-F27PBJKO-CMyFcBlX.js +1 -0
- package/assets/chunk-IMKFNOWR--8Y02hwt.js +231 -0
- package/assets/chunk-JWPE2WC7-C18m6v8d.js +1 -0
- package/assets/chunk-POPQ4Y6H-DzoTZe0a.js +1 -0
- package/assets/chunk-SVP7TREG-Ce98IXh7.js +88 -0
- package/assets/chunk-TICWLB2K-BeLoUlaL.js +206 -0
- package/assets/chunk-XXDRQBXY-DCZSZ1aW.js +1 -0
- package/assets/classDiagram-ZZMXUADV-jA4zeKxs.js +1 -0
- package/assets/classDiagram-v2-VYDZK3BY-jA4zeKxs.js +1 -0
- package/assets/controls/ckeditor.es.js +9382 -9320
- package/assets/controls/handsontable.es.js +11971 -11938
- package/assets/controls/jsoneditor.es.js +10442 -10233
- package/assets/controls/toast-ui.es.js +1308 -1279
- package/assets/cose-bilkent-JH36ORCC-Cy9np608.js +1 -0
- package/assets/cynefin-OW5HDTMX-Ba6uP79R.js +166 -0
- package/assets/cynefinDiagram-5FMLGOSQ-DhMBBq7T.js +62 -0
- package/assets/cytoscape.esm-DzSz-X2X.js +321 -0
- package/assets/dagre-GXQ25YYZ-BdAnQUCV.js +4 -0
- package/assets/dashboard-BFVFBIio.js +3 -0
- package/assets/debounce-B3mXI_sx.js +1 -0
- package/assets/defaultLocale-DX6XiGOO.js +1 -0
- package/assets/diagram-S7CK7UJ4-BDYUGbNl.js +30 -0
- package/assets/diagram-UQ7AKVKN-PURK0-X-.js +41 -0
- package/assets/diagram-VSXAHHWV-DxHdFxl9.js +3 -0
- package/assets/diagram-VX7I27RA-Vz8xmeYc.js +24 -0
- package/assets/diagram-Z3DM3KII-CFXUpBMy.js +24 -0
- package/assets/docs-DU3h-HtS.js +1 -0
- package/assets/ebnfDiagram-PWID7BFC-BXNvxMGP.js +1 -0
- package/assets/erDiagram-RLTQ6QDP-DN0GgZEI.js +99 -0
- package/assets/flowDiagram-HODETNUW-DXdnwgBv.js +168 -0
- package/assets/ganttDiagram-EL5Y4UJY-Cv8_dxX3.js +292 -0
- package/assets/gitGraphDiagram-WWUBYQGX-BO8T858E.js +106 -0
- package/assets/history-BmwWpckq.js +1 -0
- package/assets/infoDiagram-27XIBGKW-DMSJ1RNc.js +2 -0
- package/assets/init-Gi6I4Gst.js +1 -0
- package/assets/init-user-BIdB8U_4.js +1 -0
- package/assets/isObject-DL-BuaBl.js +1 -0
- package/assets/ishikawaDiagram-5VMMS53U-_tLJESbl.js +70 -0
- package/assets/journeyDiagram-3NMN7TZE-DMMhdo-_.js +139 -0
- package/assets/kanban-definition-UXKFOSKX-CxQrB4t6.js +89 -0
- package/assets/katex-HP8lGamR.js +257 -0
- package/assets/layout-_oS_xxCJ.js +1 -0
- package/assets/linear-VLYuNK3x.js +1 -0
- package/assets/{list-CcMtS4hD.js → list-YTyJQtiR.js} +2 -2
- package/assets/login-B9GlhjZh.js +1 -0
- package/assets/manifest.json +834 -58
- package/assets/media-manager-Bb1VWlzd.js +6 -0
- package/assets/mermaid.core-BknV2vft.js +310 -0
- package/assets/mindmap-definition-YA3MSWOX-DBjjjDq4.js +96 -0
- package/assets/module-DQ1NK8Xg.js +1 -0
- package/assets/notification-CgNOsXvn.js +20 -0
- package/assets/ordinal-Cboi1Yqb.js +1 -0
- package/assets/pegDiagram-XKGWAZYB-uRznTaO4.js +1 -0
- package/assets/pieDiagram-E7YTZNPT-D1TkCH8I.js +39 -0
- package/assets/quadrantDiagram-AXDQQJYC-BofJ36Jz.js +7 -0
- package/assets/railroadDiagram-O6MQD6OU-e1ZM2IT1.js +1 -0
- package/assets/register-DjTtMayF.js +1 -0
- package/assets/requirementDiagram-BXWQKSXE-DQWArRbb.js +84 -0
- package/assets/ru-CqAtka69.js +1 -0
- package/assets/sankeyDiagram-P5KCCOFB-fVOu2jzi.js +40 -0
- package/assets/sequenceDiagram-WJ2MYXX4-Dj24cqpu.js +162 -0
- package/assets/sizeCapture-INFHLROL-CsApVrVi.js +1 -0
- package/assets/stateDiagram-D77RDMKH-IU2Szlid.js +1 -0
- package/assets/stateDiagram-v2-MP3YSRHH-B-EW3cIC.js +1 -0
- package/assets/swimlanes-42K2YHIH-97rBqTR-.js +1 -0
- package/assets/swimlanesDiagram-VR7AAH4N-CqG2iydK.js +8 -0
- package/assets/timeline-definition-24CTP7MA-DSgRzx1V.js +120 -0
- package/assets/toNumber-DZb-r9sy.js +1 -0
- package/assets/use-appearance-CXz2xWgT.js +1 -0
- package/assets/user-filters-list-BAR4CBN_.js +1 -0
- package/assets/vennDiagram-4TSXK5OY-BqMksFxE.js +34 -0
- package/assets/wardleyDiagram-VM6X3IG4-BMmxRODC.js +78 -0
- package/assets/welcome-CPXT7_4b.js +1 -0
- package/assets/with-app-layout-C6dqetfx.js +31 -0
- package/assets/xychartDiagram-S5SC5T6Z-3xRuF0l4.js +7 -0
- package/controllers/access-rights/permissionOptions.d.ts +2 -0
- package/controllers/access-rights/permissionOptions.js +23 -0
- package/controllers/add.js +2 -2
- package/controllers/addGroup.js +15 -6
- package/controllers/ai/AiAgentController.d.ts +66 -0
- package/controllers/ai/AiAgentController.js +435 -0
- package/controllers/ai/AiAssistantController.d.ts +0 -4
- package/controllers/ai/AiAssistantController.js +4 -78
- package/controllers/catalog/FrontendCatalogAdapter.js +7 -8
- package/controllers/docs/index.d.ts +22 -0
- package/controllers/docs/index.js +126 -0
- package/controllers/edit.js +2 -2
- package/controllers/editGroup.js +15 -6
- package/controllers/filter-fields/savedFilters.js +1 -1
- package/controllers/history-actions/HistoryController.js +7 -7
- package/controllers/inlineUpdate.js +10 -3
- package/controllers/list.js +1 -1
- package/controllers/login.js +1 -1
- package/controllers/notifications/NotificationController.js +14 -8
- package/controllers/view.js +3 -3
- package/controllers/widgets/filterInfo.js +1 -1
- package/helpers/accessRightsHelper.d.ts +63 -2
- package/helpers/accessRightsHelper.js +202 -13
- package/helpers/assetVersionHelper.d.ts +14 -0
- package/helpers/assetVersionHelper.js +42 -0
- package/helpers/configHelper.js +19 -18
- package/helpers/controllerHelper.d.ts +1 -0
- package/helpers/controllerHelper.js +25 -15
- package/helpers/fieldsHelper.d.ts +2 -1
- package/helpers/fieldsHelper.js +11 -5
- package/helpers/inertiaActionsHelper.d.ts +1 -1
- package/helpers/inertiaActionsHelper.js +4 -5
- package/helpers/inertiaAddHelper.d.ts +1 -1
- package/helpers/inertiaAddHelper.js +7 -6
- package/helpers/inertiaGroupHelper.js +10 -2
- package/helpers/inertiaListHelper.d.ts +1 -1
- package/helpers/inertiaListHelper.js +7 -8
- package/helpers/inertiaMenuHelper.d.ts +7 -2
- package/helpers/inertiaMenuHelper.js +12 -27
- package/helpers/menuHelper.d.ts +11 -1
- package/helpers/menuHelper.js +12 -0
- package/helpers/modelResourceHelper.d.ts +9 -0
- package/helpers/modelResourceHelper.js +43 -0
- package/helpers/navigationAccessHelper.d.ts +13 -0
- package/helpers/navigationAccessHelper.js +45 -0
- package/index.d.ts +11 -0
- package/index.js +11 -0
- package/interfaces/adminpanelConfig.d.ts +160 -2
- package/interfaces/queryCriteria.d.ts +13 -0
- package/interfaces/types.d.ts +169 -0
- package/lib/Adminizer.d.ts +8 -0
- package/lib/Adminizer.js +30 -2
- package/lib/DataAccessor.d.ts +100 -3
- package/lib/DataAccessor.js +454 -245
- package/lib/access-graph/AccessGraphResolver.d.ts +136 -0
- package/lib/access-graph/AccessGraphResolver.js +605 -0
- package/lib/access-graph/RecordAccessCache.d.ts +23 -0
- package/lib/access-graph/RecordAccessCache.js +30 -0
- package/lib/access-graph/RecordAccessResolver.d.ts +83 -0
- package/lib/access-graph/RecordAccessResolver.js +295 -0
- package/lib/access-graph/shared.d.ts +60 -0
- package/lib/access-graph/shared.js +121 -0
- package/lib/admin-links/AdminLinkHandler.d.ts +72 -0
- package/lib/admin-links/AdminLinkHandler.js +183 -0
- package/lib/ai-assistant/AbstractAiConversationHistoryService.d.ts +39 -0
- package/lib/ai-assistant/AbstractAiConversationHistoryService.js +98 -0
- package/lib/ai-assistant/AbstractAiModelService.d.ts +139 -2
- package/lib/ai-assistant/AbstractAiModelService.js +154 -1
- package/lib/ai-assistant/AiAgentRunManager.d.ts +35 -0
- package/lib/ai-assistant/AiAgentRunManager.js +109 -0
- package/lib/ai-assistant/AiAssistantAgentSkillHandler.d.ts +61 -0
- package/lib/ai-assistant/AiAssistantAgentSkillHandler.js +90 -0
- package/lib/ai-assistant/AiAssistantHandler.d.ts +1 -0
- package/lib/ai-assistant/AiAssistantHandler.js +4 -0
- package/lib/ai-assistant/AiAssistantUiMethodHandler.d.ts +60 -0
- package/lib/ai-assistant/AiAssistantUiMethodHandler.js +144 -0
- package/lib/ai-assistant/builtinAgentSkills.d.ts +9 -0
- package/lib/ai-assistant/builtinAgentSkills.js +259 -0
- package/lib/ai-assistant/jsonSafe.d.ts +13 -0
- package/lib/ai-assistant/jsonSafe.js +81 -0
- package/lib/app-manager/AdminizerApp.d.ts +40 -1
- package/lib/app-manager/AppManager.js +48 -42
- package/lib/app-manager/ConfigLayerHandler.js +4 -0
- package/lib/docs/AbstractDocumentation.d.ts +100 -0
- package/lib/docs/AbstractDocumentation.js +108 -0
- package/lib/docs/DocumentationHandler.d.ts +100 -0
- package/lib/docs/DocumentationHandler.js +198 -0
- package/lib/docs/FileDocumentation.d.ts +47 -0
- package/lib/docs/FileDocumentation.js +189 -0
- package/lib/docs/docsAgentSkills.d.ts +10 -0
- package/lib/docs/docsAgentSkills.js +93 -0
- package/lib/filters/FilterService.js +4 -3
- package/lib/history-actions/AbstractHistoryAdapter.d.ts +26 -1
- package/lib/history-actions/AbstractHistoryAdapter.js +155 -25
- package/lib/media-manager/helpers/MediaManagerHelper.js +9 -7
- package/lib/model/AbstractModel.d.ts +48 -0
- package/lib/model/AbstractModel.js +14 -4
- package/lib/model/ModelHandler.d.ts +61 -2
- package/lib/model/ModelHandler.js +136 -8
- package/lib/model/adapter/sequelize.d.ts +40 -1
- package/lib/model/adapter/sequelize.js +243 -77
- package/lib/model/adapter/typeorm.d.ts +16 -0
- package/lib/model/adapter/typeorm.js +136 -9
- package/lib/notifications/GeneralNotificationService.js +6 -5
- package/lib/notifications/SystemNotificationService.js +1 -1
- package/lib/ui-i18n/uiTranslationKeys.d.ts +2 -1
- package/lib/ui-i18n/uiTranslationKeys.js +42 -1
- package/lib/widgets/widgetHandler.js +10 -6
- package/models/Group.d.ts +2 -1
- package/package.json +35 -23
- package/policies/authPolicies.js +6 -6
- package/policies/permissionResolvers.d.ts +1 -0
- package/policies/permissionResolvers.js +3 -0
- package/system/Router.js +21 -3
- package/system/bindAccessRights.js +13 -3
- package/system/bindDocs.d.ts +13 -0
- package/system/bindDocs.js +77 -0
- package/system/bindInertia.js +30 -7
- package/system/buildInternalModelAccess.d.ts +9 -0
- package/system/buildInternalModelAccess.js +37 -4
- package/system/defaults.js +7 -1
- package/system/validateSystemModels.js +29 -9
- package/assets/add-CBlLpLr0.js +0 -1
- package/assets/add-group-H0IKIsNm.js +0 -1
- package/assets/add-user-CoTLmxtF.js +0 -1
- package/assets/app-DNXiApCk.css +0 -1
- package/assets/app-layout-xxJpLAvb.js +0 -1
- package/assets/app.js +0 -138
- package/assets/dashboard-DVJS5EPP.js +0 -3
- package/assets/debounce-BMmXVQ06.js +0 -1
- package/assets/history-bWRuzbye.js +0 -1
- package/assets/init-user-8aUohVDq.js +0 -1
- package/assets/login-CO4laf6m.js +0 -1
- package/assets/media-manager-BDbpSSjH.js +0 -6
- package/assets/module-CqO_BKsH.js +0 -1
- package/assets/notification-B5l2wwmi.js +0 -20
- package/assets/register-DBYIMXGQ.js +0 -1
- package/assets/ru-rYKdQbWF.js +0 -1
- package/assets/use-appearance-_EbbjHTh.js +0 -1
- package/assets/user-filters-list-C7kMKN5f.js +0 -1
- package/assets/welcome-6z-7sXnQ.js +0 -1
|
@@ -1,6 +1,29 @@
|
|
|
1
1
|
import { Adminizer } from "../lib/Adminizer.js";
|
|
2
|
+
export function parseGroupPermissionGrant(value) {
|
|
3
|
+
if (!value ||
|
|
4
|
+
typeof value !== "object" ||
|
|
5
|
+
typeof value.tokenId !== "string" ||
|
|
6
|
+
!Array.isArray(value.rights) ||
|
|
7
|
+
!value.rights.every((right) => typeof right === "string" || typeof right === "number")) {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
const parsed = value;
|
|
11
|
+
return {
|
|
12
|
+
tokenId: parsed.tokenId.toLowerCase(),
|
|
13
|
+
rights: Array.from(new Set(parsed.rights.map(String))),
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
/** `<verb>-<model>-model` — the id shape registered by {@link AccessRightsHelper.registerModelTokens}. */
|
|
17
|
+
const MODEL_CRUD_TOKEN_PATTERN = /^(create|read|update|delete)-.+-model$/;
|
|
18
|
+
/**
|
|
19
|
+
* Stack frames of the code that merely forwards a deprecated call: this helper
|
|
20
|
+
* itself and the app-manager runtime bridge. Naming either of them would hide
|
|
21
|
+
* the module we are actually trying to point at.
|
|
22
|
+
*/
|
|
23
|
+
const FORWARDING_FRAME_PATTERN = /[\\/](?:helpers[\\/]accessRightsHelper|app-manager[\\/]AppManager)\.[cm]?[jt]s/;
|
|
2
24
|
export class AccessRightsHelper {
|
|
3
25
|
_tokens = [];
|
|
26
|
+
warnedDeprecations = new Set();
|
|
4
27
|
adminizer;
|
|
5
28
|
constructor(adminizer) {
|
|
6
29
|
this.adminizer = adminizer;
|
|
@@ -10,6 +33,19 @@ export class AccessRightsHelper {
|
|
|
10
33
|
if (!accessRightsToken.id || !accessRightsToken.name || !accessRightsToken.description || !accessRightsToken.department) {
|
|
11
34
|
throw new Error("Adminpanel > Can not register token: Missed one or more required parameters");
|
|
12
35
|
}
|
|
36
|
+
if (accessRightsToken.getOptions && typeof accessRightsToken.getOptions !== "function") {
|
|
37
|
+
throw new Error("Adminpanel > Can not register token: getOptions must be a function");
|
|
38
|
+
}
|
|
39
|
+
if (accessRightsToken.check && typeof accessRightsToken.check !== "function") {
|
|
40
|
+
throw new Error("Adminpanel > Can not register token: check must be a function");
|
|
41
|
+
}
|
|
42
|
+
// Model CRUD tokens are consulted from synchronous paths (DataAccessor field
|
|
43
|
+
// config), where a contextual `check` cannot run and is denied. Such a token would
|
|
44
|
+
// silently hide every field of the model, so it is rejected at registration.
|
|
45
|
+
if (accessRightsToken.check && MODEL_CRUD_TOKEN_PATTERN.test(accessRightsToken.id)) {
|
|
46
|
+
throw new Error(`Adminpanel > Can not register token "${accessRightsToken.id}": model CRUD tokens are ` +
|
|
47
|
+
`evaluated synchronously and cannot carry a contextual "check"`);
|
|
48
|
+
}
|
|
13
49
|
for (let i = 0; i < this._tokens.length; i++) {
|
|
14
50
|
if (this._tokens[i].id === accessRightsToken.id) {
|
|
15
51
|
this._tokens.splice(i, 1);
|
|
@@ -31,6 +67,10 @@ export class AccessRightsHelper {
|
|
|
31
67
|
const normalizedTokenId = tokenId.toLowerCase();
|
|
32
68
|
return this._tokens.some((token) => token.id === normalizedTokenId);
|
|
33
69
|
}
|
|
70
|
+
getToken(tokenId) {
|
|
71
|
+
const normalizedTokenId = tokenId.toLowerCase();
|
|
72
|
+
return this._tokens.find((token) => token.id === normalizedTokenId);
|
|
73
|
+
}
|
|
34
74
|
registerTokens(accessRightsTokens) {
|
|
35
75
|
for (let token of accessRightsTokens) {
|
|
36
76
|
this.registerToken(token);
|
|
@@ -54,7 +94,33 @@ export class AccessRightsHelper {
|
|
|
54
94
|
.map((token) => token.department)
|
|
55
95
|
.filter((item, pos, self) => self.indexOf(item) === pos);
|
|
56
96
|
}
|
|
57
|
-
|
|
97
|
+
/**
|
|
98
|
+
* The full access decision: the token exists, a group of this user carries
|
|
99
|
+
* it, and — for a contextual token — its own `check` accepts the context.
|
|
100
|
+
*
|
|
101
|
+
* This is the only entry point that can honour a contextual `check`, and the
|
|
102
|
+
* one every Adminizer call site uses. It replaces {@link hasPermission},
|
|
103
|
+
* which stays synchronous forever so that modules written against it cannot
|
|
104
|
+
* be broken open by a missing `await`.
|
|
105
|
+
*/
|
|
106
|
+
async checkPermission(tokenId, user, context) {
|
|
107
|
+
if (!this.adminizer.config.auth.enable || user.isAdministrator) {
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
const token = this.getRegisteredToken(tokenId);
|
|
111
|
+
if (!token) {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
if (!this.hasAssignedPermission(token.id, user)) {
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
if (!token.check) {
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
return this.runTokenCheck(token, user, context ?? {});
|
|
121
|
+
}
|
|
122
|
+
/** Grants when {@link checkPermission} grants any of `tokens`. */
|
|
123
|
+
async checkAnyPermission(tokens, user) {
|
|
58
124
|
if (user.isAdministrator) {
|
|
59
125
|
return true;
|
|
60
126
|
}
|
|
@@ -62,30 +128,153 @@ export class AccessRightsHelper {
|
|
|
62
128
|
if (!tokens.length) {
|
|
63
129
|
return true;
|
|
64
130
|
}
|
|
65
|
-
|
|
131
|
+
for (const token of tokens) {
|
|
132
|
+
if (await this.checkPermission(token, user)) {
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return false;
|
|
66
137
|
}
|
|
67
|
-
|
|
68
|
-
|
|
138
|
+
/**
|
|
139
|
+
* Synchronous access decision for call sites that cannot await, such as the
|
|
140
|
+
* field configuration built by `DataAccessor`.
|
|
141
|
+
*
|
|
142
|
+
* A contextual token is denied here: its `check` is asynchronous and there
|
|
143
|
+
* is no context to hand it, so the only safe answer is "no". Model CRUD
|
|
144
|
+
* tokens never declare `check`, so the ordinary panel paths are unaffected.
|
|
145
|
+
*/
|
|
146
|
+
hasStaticPermission(tokenId, user) {
|
|
147
|
+
if (!this.adminizer.config.auth.enable || user.isAdministrator) {
|
|
69
148
|
return true;
|
|
70
149
|
}
|
|
71
|
-
|
|
72
|
-
|
|
150
|
+
const token = this.getRegisteredToken(tokenId);
|
|
151
|
+
if (!token) {
|
|
152
|
+
return false;
|
|
73
153
|
}
|
|
74
|
-
if (!
|
|
75
|
-
Adminizer.log.warn(`AccessRightsHelper > hasPermission: missing accessRightsToken${context ? ` [${context}]` : ''}`);
|
|
154
|
+
if (!this.hasAssignedPermission(token.id, user)) {
|
|
76
155
|
return false;
|
|
77
156
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
if (!tokenIsValid) {
|
|
81
|
-
Adminizer.log.error("Adminpanel > Token is not valid", tokenId, user.login);
|
|
157
|
+
if (token.check) {
|
|
158
|
+
Adminizer.log.warn("AccessRightsHelper > hasStaticPermission: contextual token denied in a synchronous check", token.id);
|
|
82
159
|
return false;
|
|
83
160
|
}
|
|
161
|
+
return true;
|
|
162
|
+
}
|
|
163
|
+
/** Fail-closed synchronous variant of {@link checkAnyPermission}. */
|
|
164
|
+
enoughStaticPermissions(tokens, user) {
|
|
165
|
+
if (user.isAdministrator) {
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
168
|
+
if (!tokens.length) {
|
|
169
|
+
return true;
|
|
170
|
+
}
|
|
171
|
+
return tokens.some((token) => this.hasStaticPermission(token, user));
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* @deprecated Use {@link checkPermission}, which can honour a contextual
|
|
175
|
+
* token's `check`. This method stays synchronous and fails closed.
|
|
176
|
+
*
|
|
177
|
+
* Kept synchronous deliberately, and never to be made `async`. Modules and
|
|
178
|
+
* apps outside this repository call it as `if (hasPermission(...))`, and we
|
|
179
|
+
* can neither find nor warn them at compile time. An `async` version returns
|
|
180
|
+
* a pending promise there, which is truthy — every such check would grant
|
|
181
|
+
* access to everyone. So this signature is frozen at `boolean`: the worst a
|
|
182
|
+
* stale caller gets is a denial it can see, never a silent grant.
|
|
183
|
+
*/
|
|
184
|
+
hasPermission(tokenId, user) {
|
|
185
|
+
this.warnDeprecated("hasPermission", "checkPermission");
|
|
186
|
+
return this.hasStaticPermission(tokenId, user);
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* @deprecated Use {@link checkAnyPermission}. Synchronous and fail-closed
|
|
190
|
+
* for the same reason as {@link hasPermission}.
|
|
191
|
+
*/
|
|
192
|
+
enoughPermissions(tokens, user) {
|
|
193
|
+
this.warnDeprecated("enoughPermissions", "checkAnyPermission");
|
|
194
|
+
return this.enoughStaticPermissions(tokens, user);
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Warns once per call site, with the caller's frame so the module still
|
|
198
|
+
* using it can actually be found.
|
|
199
|
+
*
|
|
200
|
+
* Keyed on the frame rather than the method name: an app reaching the
|
|
201
|
+
* deprecated method through the runtime bridge used to spend the single
|
|
202
|
+
* warning of the whole process on a frame naming `AppManager` instead of
|
|
203
|
+
* itself, after which no other stale caller could ever surface.
|
|
204
|
+
*/
|
|
205
|
+
warnDeprecated(method, replacement) {
|
|
206
|
+
const callerFrame = this.resolveCallerFrame();
|
|
207
|
+
const warned = `${method}@${callerFrame ?? "unknown"}`;
|
|
208
|
+
if (this.warnedDeprecations.has(warned)) {
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
this.warnedDeprecations.add(warned);
|
|
212
|
+
Adminizer.log.warn(`AccessRightsHelper > ${method} is deprecated and cannot evaluate contextual tokens ` +
|
|
213
|
+
`(they are denied). Use the asynchronous ${replacement} instead.`, callerFrame ?? "");
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* The first stack frame outside the forwarding layer — this helper and the
|
|
217
|
+
* app-manager bridge — which is the module that actually made the call.
|
|
218
|
+
*/
|
|
219
|
+
resolveCallerFrame() {
|
|
220
|
+
return new Error().stack
|
|
221
|
+
?.split("\n")
|
|
222
|
+
.slice(1)
|
|
223
|
+
.map((frame) => frame.trim())
|
|
224
|
+
.find((frame) => frame.startsWith("at ") && !FORWARDING_FRAME_PATTERN.test(frame));
|
|
225
|
+
}
|
|
226
|
+
/** Returns trusted option IDs granted by all groups for a contextual token. */
|
|
227
|
+
getPermissionRights(tokenId, user) {
|
|
228
|
+
if (!this.adminizer.config.auth.enable) {
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
if (user.isAdministrator) {
|
|
232
|
+
return null;
|
|
233
|
+
}
|
|
234
|
+
const token = this.getRegisteredToken(tokenId);
|
|
235
|
+
if (!token) {
|
|
236
|
+
return [];
|
|
237
|
+
}
|
|
84
238
|
if (!user.groups) {
|
|
85
239
|
Adminizer.log.error('User has no groups');
|
|
240
|
+
return [];
|
|
241
|
+
}
|
|
242
|
+
const rights = user.groups.flatMap((group) => group.tokens ?? [])
|
|
243
|
+
.map(parseGroupPermissionGrant)
|
|
244
|
+
.filter((grant) => grant?.tokenId === token.id)
|
|
245
|
+
.flatMap((grant) => grant.rights);
|
|
246
|
+
return Array.from(new Set(rights));
|
|
247
|
+
}
|
|
248
|
+
getRegisteredToken(tokenId) {
|
|
249
|
+
if (!tokenId) {
|
|
250
|
+
Adminizer.log.warn("AccessRightsHelper > hasPermission: missing accessRightsToken");
|
|
251
|
+
return undefined;
|
|
252
|
+
}
|
|
253
|
+
const token = this.getToken(tokenId);
|
|
254
|
+
if (!token) {
|
|
255
|
+
Adminizer.log.error("Adminpanel > Token is not valid", tokenId);
|
|
256
|
+
}
|
|
257
|
+
return token;
|
|
258
|
+
}
|
|
259
|
+
hasAssignedPermission(tokenId, user) {
|
|
260
|
+
if (!this.adminizer.config.auth.enable || user.isAdministrator) {
|
|
261
|
+
return true;
|
|
262
|
+
}
|
|
263
|
+
if (!user.groups) {
|
|
264
|
+
Adminizer.log.error("User has no groups");
|
|
265
|
+
return false;
|
|
266
|
+
}
|
|
267
|
+
return user.groups.some((group) => group.tokens?.some((groupToken) => groupToken === tokenId || parseGroupPermissionGrant(groupToken)?.tokenId === tokenId));
|
|
268
|
+
}
|
|
269
|
+
async runTokenCheck(token, user, context) {
|
|
270
|
+
try {
|
|
271
|
+
const rights = this.getPermissionRights(token.id, user) ?? [];
|
|
272
|
+
return await token.check(user, { ...context, rights });
|
|
273
|
+
}
|
|
274
|
+
catch (error) {
|
|
275
|
+
Adminizer.log.error("AccessRightsHelper > token check failed", token.id, error);
|
|
86
276
|
return false;
|
|
87
277
|
}
|
|
88
|
-
return user.groups.some((group) => group.tokens?.includes(tokenId));
|
|
89
278
|
}
|
|
90
279
|
}
|
|
91
280
|
export class GroupsAccessRightsHelper {
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adminizer package version. Some built entries keep stable file names because
|
|
3
|
+
* they are imported by a hard-coded path and so cannot be looked up in the
|
|
4
|
+
* manifest (agent.es.js, controls/*.es.js and controls/*.css); for those this
|
|
5
|
+
* version is appended as a query string to invalidate browser caches on release.
|
|
6
|
+
*
|
|
7
|
+
* Only ever put this on a *self-contained* bundle. An entry whose split chunks
|
|
8
|
+
* import it back by name (app.js) must be content-hashed instead — a query
|
|
9
|
+
* string there makes the browser instantiate the module twice.
|
|
10
|
+
*/
|
|
11
|
+
export declare function getAssetVersion(): string;
|
|
12
|
+
/** Appends ?v=<version> to a production asset URL; dev URLs pass through untouched. */
|
|
13
|
+
export declare function withAssetVersion(url: string): string;
|
|
14
|
+
export declare function withAssetVersion(url: string | undefined): string | undefined;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
let cachedVersion = null;
|
|
4
|
+
/**
|
|
5
|
+
* Adminizer package version. Some built entries keep stable file names because
|
|
6
|
+
* they are imported by a hard-coded path and so cannot be looked up in the
|
|
7
|
+
* manifest (agent.es.js, controls/*.es.js and controls/*.css); for those this
|
|
8
|
+
* version is appended as a query string to invalidate browser caches on release.
|
|
9
|
+
*
|
|
10
|
+
* Only ever put this on a *self-contained* bundle. An entry whose split chunks
|
|
11
|
+
* import it back by name (app.js) must be content-hashed instead — a query
|
|
12
|
+
* string there makes the browser instantiate the module twice.
|
|
13
|
+
*/
|
|
14
|
+
export function getAssetVersion() {
|
|
15
|
+
if (cachedVersion !== null) {
|
|
16
|
+
return cachedVersion;
|
|
17
|
+
}
|
|
18
|
+
let version = "";
|
|
19
|
+
// Compiled sources sit one level below the package root in the published
|
|
20
|
+
// package (helpers/) and two below it in the repo (dist/helpers/).
|
|
21
|
+
for (const candidate of ["../package.json", "../../package.json"]) {
|
|
22
|
+
try {
|
|
23
|
+
const pkg = JSON.parse(fs.readFileSync(path.resolve(import.meta.dirname, candidate), "utf-8"));
|
|
24
|
+
if (pkg?.name === "adminizer" && typeof pkg.version === "string") {
|
|
25
|
+
version = pkg.version;
|
|
26
|
+
break;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
// Missing or unreadable package.json: try the next candidate.
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
cachedVersion = version;
|
|
34
|
+
return version;
|
|
35
|
+
}
|
|
36
|
+
export function withAssetVersion(url) {
|
|
37
|
+
const version = getAssetVersion();
|
|
38
|
+
if (!url || !version || process.env.ADMINIZER_ENV === "dev") {
|
|
39
|
+
return url;
|
|
40
|
+
}
|
|
41
|
+
return `${url}${url.includes("?") ? "&" : "?"}v=${version}`;
|
|
42
|
+
}
|
package/helpers/configHelper.js
CHANGED
|
@@ -34,23 +34,17 @@ export class ConfigHelper {
|
|
|
34
34
|
if (!modelName) {
|
|
35
35
|
throw new Error("Model name is not defined");
|
|
36
36
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
if (typeof config.models[modelResourceName] !== "boolean") {
|
|
44
|
-
modelConfig = config.models[modelResourceName];
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
});
|
|
37
|
+
const resourceName = this.adminizer.modelHandler.getResourceRecord(modelName)?.name
|
|
38
|
+
?? this.adminizer.modelHandler.resolveResourceByHostModel(modelName);
|
|
39
|
+
const modelConfig = resourceName && typeof this.adminizer.config.models[resourceName] !== "boolean"
|
|
40
|
+
? this.adminizer.config.models[resourceName]
|
|
41
|
+
: undefined;
|
|
42
|
+
const model = resourceName ? this.adminizer.modelHandler.getResource(resourceName) : undefined;
|
|
49
43
|
if (modelConfig && modelConfig.identifierField) {
|
|
50
44
|
return modelConfig.identifierField;
|
|
51
45
|
}
|
|
52
|
-
else if (
|
|
53
|
-
return
|
|
46
|
+
else if (model?.primaryKey) {
|
|
47
|
+
return model.primaryKey;
|
|
54
48
|
}
|
|
55
49
|
else {
|
|
56
50
|
throw new Error("ConfigHelper > Identifier field was not found");
|
|
@@ -87,12 +81,13 @@ export class ConfigHelper {
|
|
|
87
81
|
let displayField;
|
|
88
82
|
try {
|
|
89
83
|
const associatedModelName = config.type === "association"
|
|
90
|
-
? modelField.model
|
|
91
|
-
: modelField.collection
|
|
84
|
+
? modelField.model
|
|
85
|
+
: modelField.collection;
|
|
92
86
|
if (!associatedModelName) {
|
|
93
87
|
throw new Error(`No model/collection defined for association field: ${key}`);
|
|
94
88
|
}
|
|
95
|
-
const
|
|
89
|
+
const resourceName = adminizer.modelHandler.resolveAssociationResource(associatedModelName, modelField.resourceName);
|
|
90
|
+
const associatedModel = resourceName ? adminizer.modelHandler.getResource(resourceName) : undefined;
|
|
96
91
|
if (!associatedModel) {
|
|
97
92
|
throw new Error(`Can not add relations to unloaded models; Config: ${JSON.stringify(config, null, 2)}`);
|
|
98
93
|
}
|
|
@@ -139,7 +134,13 @@ export class ConfigHelper {
|
|
|
139
134
|
additionalLinks: [
|
|
140
135
|
...builtinLinks,
|
|
141
136
|
...(config.navbar?.additionalLinks || [])
|
|
142
|
-
]
|
|
137
|
+
],
|
|
138
|
+
// Merged per section name so an app declaring its own sections
|
|
139
|
+
// keeps the built-in ones, while still being able to override them.
|
|
140
|
+
sections: {
|
|
141
|
+
...defaultConfig.navbar?.sections,
|
|
142
|
+
...config.navbar?.sections
|
|
143
|
+
}
|
|
143
144
|
}
|
|
144
145
|
};
|
|
145
146
|
// Normalize auth config if it's a boolean
|
|
@@ -89,19 +89,27 @@ export class ControllerHelper {
|
|
|
89
89
|
* @returns {?string}
|
|
90
90
|
*/
|
|
91
91
|
static findModelResourceName(req) {
|
|
92
|
-
|
|
93
|
-
|
|
92
|
+
const models = req.adminizer.config.models;
|
|
93
|
+
const requestedName = req.params.modelResourceName
|
|
94
|
+
?? req.params.model
|
|
95
|
+
?? req.originalUrl.split('/')[3];
|
|
96
|
+
return this.resolveConfiguredResourceName(models, requestedName);
|
|
97
|
+
}
|
|
98
|
+
static resolveConfiguredResourceName(models, requestedName) {
|
|
99
|
+
if (!models || !requestedName) {
|
|
100
|
+
throw new Error(`Model "${requestedName}" not found`);
|
|
94
101
|
}
|
|
95
|
-
if (
|
|
96
|
-
return
|
|
102
|
+
if (Object.prototype.hasOwnProperty.call(models, requestedName)) {
|
|
103
|
+
return requestedName;
|
|
97
104
|
}
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
105
|
+
const candidates = Object.keys(models).filter((key) => key.toLowerCase() === requestedName.toLowerCase());
|
|
106
|
+
if (candidates.length === 1) {
|
|
107
|
+
return candidates[0];
|
|
108
|
+
}
|
|
109
|
+
if (candidates.length > 1) {
|
|
110
|
+
throw new Error(`Model resource "${requestedName}" is ambiguous: ${candidates.join(", ")}`);
|
|
103
111
|
}
|
|
104
|
-
|
|
112
|
+
throw new Error(`Model "${requestedName}" not found`);
|
|
105
113
|
}
|
|
106
114
|
/**
|
|
107
115
|
* Searches for config from admin panel
|
|
@@ -116,12 +124,14 @@ export class ControllerHelper {
|
|
|
116
124
|
Adminizer.log.error('No models configuration found');
|
|
117
125
|
return null;
|
|
118
126
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
127
|
+
try {
|
|
128
|
+
const foundKey = this.resolveConfiguredResourceName(models, modelResourceName);
|
|
129
|
+
return this._normalizeModelConfig(foundKey, models[foundKey]);
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
Adminizer.log.error(error);
|
|
122
133
|
return null;
|
|
123
134
|
}
|
|
124
|
-
return this._normalizeModelConfig(foundKey, models[foundKey]);
|
|
125
135
|
}
|
|
126
136
|
/**
|
|
127
137
|
* Will get action config from configuration file depending to given action
|
|
@@ -197,7 +207,7 @@ export class ControllerHelper {
|
|
|
197
207
|
modelResource.config = this.findModelConfig(req, modelResourceName);
|
|
198
208
|
// Find and add the model itself to the ModelResource
|
|
199
209
|
if (this._isValidModelConfig(modelResource.config)) {
|
|
200
|
-
modelResource.model = req.adminizer.modelHandler.
|
|
210
|
+
modelResource.model = req.adminizer.modelHandler.getResource(modelResourceName);
|
|
201
211
|
}
|
|
202
212
|
// Return the completed ModelResource object
|
|
203
213
|
return modelResource;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ActionType, BaseFieldConfig, FieldsTypes, ModelConfig } from "../interfaces/adminpanelConfig.js";
|
|
2
2
|
import { Attribute } from "../lib/model/AbstractModel.js";
|
|
3
|
+
import { RecordAccessCache } from "../lib/access-graph/RecordAccessCache.js";
|
|
3
4
|
export type Field = {
|
|
4
5
|
config: BaseFieldConfig & {
|
|
5
6
|
/** @deprecated record should not be in config anymore */
|
|
@@ -30,5 +31,5 @@ export declare class FieldsHelper {
|
|
|
30
31
|
* @param action
|
|
31
32
|
* @deprecated this method has overload association tree on each request that is not optimize
|
|
32
33
|
*/
|
|
33
|
-
static loadAssociations(req: ReqType, fields: Fields, action?: ActionType): Promise<Fields>;
|
|
34
|
+
static loadAssociations(req: ReqType, fields: Fields, action?: ActionType, recordAccessCache?: RecordAccessCache): Promise<Fields>;
|
|
34
35
|
}
|
package/helpers/fieldsHelper.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { DataAccessor } from "../lib/DataAccessor.js";
|
|
2
|
+
import { RecordAccessCache } from "../lib/access-graph/RecordAccessCache.js";
|
|
2
3
|
import { Adminizer } from "../lib/Adminizer.js";
|
|
3
4
|
export class FieldsHelper {
|
|
4
5
|
/**
|
|
@@ -11,7 +12,11 @@ export class FieldsHelper {
|
|
|
11
12
|
* @deprecated this method has overload association tree on each request that is not optimize
|
|
12
13
|
*/
|
|
13
14
|
// TODO: make separate request for all dropdown relations by api call
|
|
14
|
-
static async loadAssociations(req, fields, action) {
|
|
15
|
+
static async loadAssociations(req, fields, action, recordAccessCache = new RecordAccessCache()) {
|
|
16
|
+
// `getFieldsConfig()` returns undefined when the user has no rights on the model at all
|
|
17
|
+
if (!fields) {
|
|
18
|
+
return {};
|
|
19
|
+
}
|
|
15
20
|
/**
|
|
16
21
|
* Load all associated records for given field key
|
|
17
22
|
*
|
|
@@ -30,9 +35,9 @@ export class FieldsHelper {
|
|
|
30
35
|
Adminizer.log.error('No model found for field: ', fields[key]);
|
|
31
36
|
return;
|
|
32
37
|
}
|
|
33
|
-
const resolvedModelName = req.adminizer.modelHandler.
|
|
34
|
-
let Model = req.adminizer.modelHandler.
|
|
35
|
-
if (!Model) {
|
|
38
|
+
const resolvedModelName = req.adminizer.modelHandler.resolveAssociationResource(modelName, fields[key].model.resourceName);
|
|
39
|
+
let Model = resolvedModelName ? req.adminizer.modelHandler.getResource(resolvedModelName) : undefined;
|
|
40
|
+
if (!Model || !resolvedModelName) {
|
|
36
41
|
return;
|
|
37
42
|
}
|
|
38
43
|
let list;
|
|
@@ -46,7 +51,8 @@ export class FieldsHelper {
|
|
|
46
51
|
model: Model,
|
|
47
52
|
uri: `${req.adminizer.config.routePrefix}/model/${resolvedModelName}`
|
|
48
53
|
};
|
|
49
|
-
|
|
54
|
+
// One cache for every dropdown of the page: the record-access lookups are identical
|
|
55
|
+
let dataAccessor = new DataAccessor(req.adminizer, req.user, modelResource, "view", recordAccessCache);
|
|
50
56
|
list = await Model.find({}, dataAccessor);
|
|
51
57
|
}
|
|
52
58
|
catch (e) {
|
|
@@ -7,4 +7,4 @@ export interface Actions {
|
|
|
7
7
|
title: string;
|
|
8
8
|
icon: string;
|
|
9
9
|
}
|
|
10
|
-
export default function inertiaActionsHelper(actionType: ActionType, modelResource: ModelResource, req: ReqType): Actions[]
|
|
10
|
+
export default function inertiaActionsHelper(actionType: ActionType, modelResource: ModelResource, req: ReqType): Promise<Actions[]>;
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
export default function inertiaActionsHelper(actionType, modelResource, req) {
|
|
1
|
+
export default async function inertiaActionsHelper(actionType, modelResource, req) {
|
|
2
2
|
let resActions = [];
|
|
3
3
|
if (req.adminizer.menuHelper.hasGlobalActions(modelResource.config, actionType)) {
|
|
4
4
|
const actions = req.adminizer.menuHelper.getGlobalActions(modelResource.config, actionType);
|
|
5
5
|
if (actions && actions.length > 0) {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
if (req.adminizer.accessRightsHelper.hasPermission(action.accessRightsToken, req.user, context)) {
|
|
6
|
+
for (const action of actions) {
|
|
7
|
+
if (await req.adminizer.accessRightsHelper.checkPermission(action.accessRightsToken, req.user)) {
|
|
9
8
|
resActions.push({
|
|
10
9
|
link: action.link,
|
|
11
10
|
id: action.id,
|
|
@@ -14,7 +13,7 @@ export default function inertiaActionsHelper(actionType, modelResource, req) {
|
|
|
14
13
|
icon: action.icon
|
|
15
14
|
});
|
|
16
15
|
}
|
|
17
|
-
}
|
|
16
|
+
}
|
|
18
17
|
}
|
|
19
18
|
}
|
|
20
19
|
return resActions;
|
|
@@ -23,7 +23,7 @@ interface FieldProps extends Record<string | number | symbol, unknown> {
|
|
|
23
23
|
postLink: string;
|
|
24
24
|
model: string;
|
|
25
25
|
}
|
|
26
|
-
export default function inertiaAddHelper(req: ReqType, modelResource: ModelResource, fields: Fields, record?: Record<string, string | boolean | number | string[]>, view?: boolean): FieldProps
|
|
26
|
+
export default function inertiaAddHelper(req: ReqType, modelResource: ModelResource, fields: Fields, record?: Record<string, string | boolean | number | string[]>, view?: boolean): Promise<FieldProps>;
|
|
27
27
|
export declare function getControlsOptions(fieldConfig: Field["config"], req: ReqType, type: ControlType, defaultControlName: string, context?: string): {
|
|
28
28
|
name: string;
|
|
29
29
|
config: any;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import inertiaActionsHelper from "./inertiaActionsHelper.js";
|
|
2
2
|
import { isObject } from "./JsUtils.js";
|
|
3
|
+
import { withAssetVersion } from "./assetVersionHelper.js";
|
|
3
4
|
import { Adminizer } from "../lib/Adminizer.js";
|
|
4
5
|
const missingControlWarnings = new Set();
|
|
5
6
|
/**
|
|
@@ -14,7 +15,7 @@ function getWeekNumber(date) {
|
|
|
14
15
|
const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
|
|
15
16
|
return Math.ceil((((d.getTime() - yearStart.getTime()) / 86400000) + 1) / 7);
|
|
16
17
|
}
|
|
17
|
-
export default function inertiaAddHelper(req, modelResource, fields, record, view = false) {
|
|
18
|
+
export default async function inertiaAddHelper(req, modelResource, fields, record, view = false) {
|
|
18
19
|
const actionType = 'add';
|
|
19
20
|
let props = {
|
|
20
21
|
edit: !!record,
|
|
@@ -36,7 +37,7 @@ export default function inertiaAddHelper(req, modelResource, fields, record, vie
|
|
|
36
37
|
postLink: record ? `${modelResource.uri}/edit/${record.id}` : `${modelResource.uri}/add`,
|
|
37
38
|
model: modelResource.name.toLocaleLowerCase()
|
|
38
39
|
};
|
|
39
|
-
props.actions = inertiaActionsHelper(actionType, modelResource, req);
|
|
40
|
+
props.actions = await inertiaActionsHelper(actionType, modelResource, req);
|
|
40
41
|
const config = req.adminizer.configHelper.getConfig();
|
|
41
42
|
for (const key of Object.keys(fields)) {
|
|
42
43
|
if ((!config.showORMtime) && (key === 'createdAt' || key === 'updatedAt'))
|
|
@@ -62,7 +63,7 @@ export default function inertiaAddHelper(req, modelResource, fields, record, vie
|
|
|
62
63
|
let canCreateRelated = false;
|
|
63
64
|
const controlContext = `${modelResource.name}.${key}`;
|
|
64
65
|
//@ts-ignore TODO: fix field type
|
|
65
|
-
if (
|
|
66
|
+
if (req.adminizer.configHelper.isId(field, modelResource.name)) {
|
|
66
67
|
disabled = true;
|
|
67
68
|
}
|
|
68
69
|
if (['string', 'password', 'date', 'datetime', 'time', 'integer', 'number', 'float', 'email', 'month', 'week', 'range'].includes(type)) {
|
|
@@ -118,7 +119,7 @@ export default function inertiaAddHelper(req, modelResource, fields, record, vie
|
|
|
118
119
|
? req.adminizer.modelHandler.resolveModelName(rawRelatedModel)
|
|
119
120
|
: undefined;
|
|
120
121
|
if (relatedModel && req.user) {
|
|
121
|
-
canCreateRelated = req.adminizer.accessRightsHelper.
|
|
122
|
+
canCreateRelated = await req.adminizer.accessRightsHelper.checkPermission(`create-${relatedModel}-model`, req.user);
|
|
122
123
|
}
|
|
123
124
|
else if (relatedModel && !req.adminizer.config.auth?.enable) {
|
|
124
125
|
canCreateRelated = true;
|
|
@@ -195,8 +196,8 @@ export function getControlsOptions(fieldConfig, req, type, defaultControlName, c
|
|
|
195
196
|
...(control?.getConfig() || {}), // Base config of the editor
|
|
196
197
|
...(shouldUseFieldConfig ? fieldOptions?.config || {} : {}),
|
|
197
198
|
},
|
|
198
|
-
path: control.getJsPath(),
|
|
199
|
-
cssPath: control.getCssPath(),
|
|
199
|
+
path: withAssetVersion(control.getJsPath()),
|
|
200
|
+
cssPath: withAssetVersion(control.getCssPath()),
|
|
200
201
|
};
|
|
201
202
|
// If items are provided, use them instead of the CKEditor defaults.
|
|
202
203
|
if (type === 'wysiwyg'
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { parseGroupPermissionGrant } from "./accessRightsHelper.js";
|
|
1
2
|
export function inertiaGroupHelper(modelResource, req, users, groupedTokens, group, view = false) {
|
|
2
3
|
let props = {
|
|
3
4
|
edit: !!group,
|
|
@@ -55,8 +56,15 @@ export function inertiaGroupHelper(modelResource, req, users, groupedTokens, gro
|
|
|
55
56
|
label: token.name,
|
|
56
57
|
tooltip: token.description,
|
|
57
58
|
name: `token-checkbox-${token.id}`,
|
|
58
|
-
value: group ? (group.tokens
|
|
59
|
-
type: 'checkbox'
|
|
59
|
+
value: group ? Boolean(group.tokens?.some((value) => value === token.id || parseGroupPermissionGrant(value)?.tokenId === token.id)) : false,
|
|
60
|
+
type: 'checkbox',
|
|
61
|
+
options: token.getOptions ? {
|
|
62
|
+
permissionOptions: {
|
|
63
|
+
rights: group?.tokens
|
|
64
|
+
?.map(parseGroupPermissionGrant)
|
|
65
|
+
.find((value) => value?.tokenId === token.id)?.rights ?? [],
|
|
66
|
+
},
|
|
67
|
+
} : undefined,
|
|
60
68
|
});
|
|
61
69
|
}
|
|
62
70
|
props.groupedTokens.push({
|
|
@@ -28,5 +28,5 @@ interface listProps extends Record<string | number | symbol, unknown> {
|
|
|
28
28
|
defaultPageSize: number;
|
|
29
29
|
pageSizeOptions: number[];
|
|
30
30
|
}
|
|
31
|
-
export declare function inertiaListHelper(modelResource: ModelResource, req: ReqType, fields: Fields, activeFilterName?: string): listProps
|
|
31
|
+
export declare function inertiaListHelper(modelResource: ModelResource, req: ReqType, fields: Fields, activeFilterName?: string): Promise<listProps>;
|
|
32
32
|
export {};
|