@acorex-platform/module-security-management-client 0.0.18 → 0.0.24
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/fesm2022/acorex-platform-module-security-management-client-acorex-platform-module-security-management-client-jSDkyMMZ.mjs +2088 -0
- package/fesm2022/acorex-platform-module-security-management-client-acorex-platform-module-security-management-client-jSDkyMMZ.mjs.map +1 -0
- package/fesm2022/acorex-platform-module-security-management-client-contracts.mjs +108 -1576
- package/fesm2022/acorex-platform-module-security-management-client-contracts.mjs.map +1 -1
- package/fesm2022/acorex-platform-module-security-management-client-index-DOHWBEzM.mjs +462 -0
- package/fesm2022/acorex-platform-module-security-management-client-index-DOHWBEzM.mjs.map +1 -0
- package/fesm2022/{acorex-platform-module-security-management-client-send-password-reset-email.command-DzfUvFk2.mjs → acorex-platform-module-security-management-client-send-password-reset-email.command-CoQgWdya.mjs} +9 -9
- package/fesm2022/acorex-platform-module-security-management-client-send-password-reset-email.command-CoQgWdya.mjs.map +1 -0
- package/fesm2022/{acorex-platform-module-security-management-client-terminate-session.command-CKq-4Z4O.mjs → acorex-platform-module-security-management-client-terminate-session.command-dsU7scOK.mjs} +19 -12
- package/fesm2022/acorex-platform-module-security-management-client-terminate-session.command-dsU7scOK.mjs.map +1 -0
- package/fesm2022/acorex-platform-module-security-management-client.mjs +1 -1712
- package/fesm2022/acorex-platform-module-security-management-client.mjs.map +1 -1
- package/package.json +1 -1
- package/types/acorex-platform-module-security-management-client-contracts.d.ts +163 -34
- package/types/acorex-platform-module-security-management-client.d.ts +139 -35
- package/fesm2022/acorex-platform-module-security-management-client-send-password-reset-email.command-DzfUvFk2.mjs.map +0 -1
- package/fesm2022/acorex-platform-module-security-management-client-terminate-session.command-CKq-4Z4O.mjs.map +0 -1
|
@@ -1,1713 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
import { Injectable, NgModule, inject, signal, input, output, computed, ViewEncapsulation, Component, effect, ChangeDetectionStrategy } from '@angular/core';
|
|
3
|
-
import { AXP_EXPRESSION_EVALUATOR_SCOPE_PROVIDER, AXP_MODULE_MANIFEST_PROVIDER, AXP_FEATURE_DEFINITION_PROVIDER, AXP_DATASOURCE_DEFINITION_PROVIDER, AXP_CONTEXT_DEFINITION_PROVIDER, setupPopupFooterKeyboardShortcuts } from '@acorex-platform/framework-client/core';
|
|
4
|
-
import { AXPEntityService, AXP_ENTITY_DEFINITION_LOADER, AXPEntityDefinitionRegistryService, AXPEntityModifyEvent } from '@acorex-platform/framework-client/layout/entity';
|
|
5
|
-
import { RootConfig, AXPSecurityManagementFeatureKeys, AXMSecurityManagementDataSourceKeys, AXM_USER_ACCESS_STATE_DEFINITION_KEY, AXMUserAccessState, AXM_USER_EFFECTIVE_STATE_DEFINITION_KEY, AXMUserEffectiveState, AXPSecurityManagementMenuKeys, AXPSecurityManagementPermissionKeys } from '@acorex-platform/module-security-management-client/contracts';
|
|
6
|
-
import { AXPSessionService, AXP_PERMISSION_DEFINITION_PROVIDER, AXPPermissionDefinitionService } from '@acorex-platform/framework-client/auth';
|
|
7
|
-
import { AXPSearchCommandProvider, createEntitySearchCommand, AXP_MENU_PROVIDER, AXP_SEARCH_PROVIDER, AXP_STATUS_PROVIDERS, AXPSettingsService, AXPRefreshEvent, AXPCommonSettings } from '@acorex-platform/framework-client/common';
|
|
8
|
-
import { provideCommandSetups, AXPCommandService } from '@acorex-platform/framework-client/runtime';
|
|
9
|
-
import { AXPWidgetsList, AXPWidgetsCatalog, AXPStatusProvider, AXPSystemStatusType } from '@acorex-platform/framework-shared/core';
|
|
10
|
-
import { AXPWidgetCoreModule, AXP_WIDGET_DEFINITION_PROVIDER, AXPValueWidgetComponent, AXPColumnWidgetComponent } from '@acorex-platform/framework-client/layout/widget-core';
|
|
11
|
-
import { AXPWidgetGroupEnum } from '@acorex-platform/framework-shared/widget-core';
|
|
12
|
-
import { AXP_NAME_PROPERTY, AXP_DATA_PATH_PROPERTY, AXP_DISABLED_PROPERTY } from '@acorex-platform/framework-client/layout/widgets';
|
|
13
|
-
import * as i3 from '@acorex/cdk/common';
|
|
14
|
-
import { AXDataSource, AXCommonModule } from '@acorex/cdk/common';
|
|
15
|
-
import { AXPCommonMenuKeys } from '@acorex-platform/module-common-client/contracts';
|
|
16
|
-
import * as i1 from '@angular/common';
|
|
17
|
-
import { CommonModule } from '@angular/common';
|
|
18
|
-
import { AXBasePageComponent } from '@acorex/components/page';
|
|
19
|
-
import { AXFormModule } from '@acorex/components/form';
|
|
20
|
-
import * as i4 from '@acorex/components/button';
|
|
21
|
-
import { AXButtonModule } from '@acorex/components/button';
|
|
22
|
-
import { AXTextBoxModule } from '@acorex/components/text-box';
|
|
23
|
-
import * as i6 from '@acorex/components/check-box';
|
|
24
|
-
import { AXCheckBoxModule } from '@acorex/components/check-box';
|
|
25
|
-
import * as i5 from '@acorex/components/label';
|
|
26
|
-
import { AXLabelModule } from '@acorex/components/label';
|
|
27
|
-
import * as i8 from '@acorex/components/decorators';
|
|
28
|
-
import { AXDecoratorModule } from '@acorex/components/decorators';
|
|
29
|
-
import * as i12 from '@acorex/core/translation';
|
|
30
|
-
import { AXTranslationService, AXTranslationModule, resolveMultiLanguageString } from '@acorex/core/translation';
|
|
31
|
-
import * as i7 from '@acorex/components/tabs';
|
|
32
|
-
import { AXTabsModule } from '@acorex/components/tabs';
|
|
33
|
-
import * as i9 from '@acorex/components/badge';
|
|
34
|
-
import { AXBadgeModule } from '@acorex/components/badge';
|
|
35
|
-
import * as i2 from '@angular/forms';
|
|
36
|
-
import { FormsModule } from '@angular/forms';
|
|
37
|
-
import { AXTooltipModule } from '@acorex/components/tooltip';
|
|
38
|
-
import * as i10 from '@acorex/components/skeleton';
|
|
39
|
-
import { AXSkeletonModule } from '@acorex/components/skeleton';
|
|
40
|
-
import * as i11 from '@acorex/components/search-box';
|
|
41
|
-
import { AXSearchBoxModule } from '@acorex/components/search-box';
|
|
42
|
-
import { AXPStateMessageComponent } from '@acorex-platform/framework-client/layout/components';
|
|
43
|
-
import { isEmpty, trim, isString, isArray, castArray } from 'lodash-es';
|
|
44
|
-
import { AXPopupService } from '@acorex/components/popup';
|
|
45
|
-
import { AXToastService } from '@acorex/components/toast';
|
|
46
|
-
import { AXPWorkflowEventService } from '@acorex-platform/framework-client/workflow';
|
|
47
|
-
|
|
48
|
-
//#region ---- Device Sessions Service ----
|
|
49
|
-
/**
|
|
50
|
-
* Terminates device sessions for the current or specified session.
|
|
51
|
-
* Implementations are provided by connectivity layers (e.g. mock or API).
|
|
52
|
-
*/
|
|
53
|
-
class AXMDeviceSessionsService {
|
|
54
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMDeviceSessionsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
55
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMDeviceSessionsService }); }
|
|
56
|
-
}
|
|
57
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMDeviceSessionsService, decorators: [{
|
|
58
|
-
type: Injectable
|
|
59
|
-
}] });
|
|
60
|
-
|
|
61
|
-
//#region ---- Imports ----
|
|
62
|
-
//#endregion
|
|
63
|
-
//#region ---- Manifest Definition ----
|
|
64
|
-
/**
|
|
65
|
-
* Security Management Module Manifest.
|
|
66
|
-
* Defines module metadata, features, and provider references.
|
|
67
|
-
*/
|
|
68
|
-
const SecurityManagementManifest = {
|
|
69
|
-
name: RootConfig.module.name,
|
|
70
|
-
version: '1.0.0',
|
|
71
|
-
title: RootConfig.module.title,
|
|
72
|
-
icon: RootConfig.module.icon,
|
|
73
|
-
i18n: RootConfig.config.i18n,
|
|
74
|
-
required: true,
|
|
75
|
-
// Module dependencies (optional)
|
|
76
|
-
};
|
|
77
|
-
//#endregion
|
|
78
|
-
|
|
79
|
-
class AXMEntityProvider {
|
|
80
|
-
preload() {
|
|
81
|
-
const { entities, module } = RootConfig;
|
|
82
|
-
return [entities.users.name, entities.roles.name, entities.deviceSessions.name].map((entity) => ({
|
|
83
|
-
module: module.name,
|
|
84
|
-
entity,
|
|
85
|
-
}));
|
|
86
|
-
}
|
|
87
|
-
async get(moduleName, entityName) {
|
|
88
|
-
const { entities, module } = RootConfig;
|
|
89
|
-
if (moduleName == module.name) {
|
|
90
|
-
switch (entityName) {
|
|
91
|
-
case RootConfig.entities.users.name:
|
|
92
|
-
return (await import('@acorex-platform/module-security-management-client/contracts')).usersFactory();
|
|
93
|
-
case RootConfig.entities.roles.name:
|
|
94
|
-
return (await import('@acorex-platform/module-security-management-client/contracts')).rolesFactory();
|
|
95
|
-
case RootConfig.entities.profile.name:
|
|
96
|
-
return (await import('@acorex-platform/module-security-management-client/contracts')).profileFactory();
|
|
97
|
-
case RootConfig.entities.deviceSessions.name:
|
|
98
|
-
return (await import('@acorex-platform/module-security-management-client/contracts')).deviceSessionsFactory();
|
|
99
|
-
case RootConfig.entities.policy.name:
|
|
100
|
-
return (await import('@acorex-platform/module-security-management-client/contracts')).policyFactory();
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
return null;
|
|
104
|
-
}
|
|
105
|
-
async list() {
|
|
106
|
-
const m = RootConfig.module.name;
|
|
107
|
-
return Promise.resolve(Object.values(RootConfig.entities).map((e) => ({ name: e.name, module: m })));
|
|
108
|
-
}
|
|
109
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMEntityProvider, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
110
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMEntityProvider }); }
|
|
111
|
-
}
|
|
112
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMEntityProvider, decorators: [{
|
|
113
|
-
type: Injectable
|
|
114
|
-
}] });
|
|
115
|
-
|
|
116
|
-
//#region ---- Imports ----
|
|
117
|
-
//#endregion
|
|
118
|
-
//#region ---- Feature Definition Provider ----
|
|
119
|
-
/**
|
|
120
|
-
* Security Management Module Feature Definition Provider.
|
|
121
|
-
* Provides feature definitions for the Security Management module.
|
|
122
|
-
*/
|
|
123
|
-
class AXMSecurityManagementFeatureDefinitionProvider {
|
|
124
|
-
async provide(context) {
|
|
125
|
-
const i18n = RootConfig.config.i18n;
|
|
126
|
-
// Manage Roles feature
|
|
127
|
-
context.addFeature(AXPSecurityManagementFeatureKeys.ManageRoles, {
|
|
128
|
-
title: `@${i18n}:features.manage-roles.title`,
|
|
129
|
-
description: `@${i18n}:features.manage-roles.description`,
|
|
130
|
-
defaultValue: true,
|
|
131
|
-
type: 'boolean',
|
|
132
|
-
});
|
|
133
|
-
// Manage Users feature
|
|
134
|
-
context.addFeature(AXPSecurityManagementFeatureKeys.ManageUsers, {
|
|
135
|
-
title: `@${i18n}:features.manage-users.title`,
|
|
136
|
-
description: `@${i18n}:features.manage-users.description`,
|
|
137
|
-
defaultValue: true,
|
|
138
|
-
type: 'boolean',
|
|
139
|
-
});
|
|
140
|
-
// Max Total Users feature (widget configuration)
|
|
141
|
-
context.addFeature(AXPSecurityManagementFeatureKeys.MaxTotalUsers, {
|
|
142
|
-
title: `@${i18n}:features.max-total-users.title`,
|
|
143
|
-
description: `@${i18n}:features.max-total-users.description`,
|
|
144
|
-
defaultValue: 100,
|
|
145
|
-
type: 'number',
|
|
146
|
-
interface: {
|
|
147
|
-
type: AXPWidgetsList.Editors.NumberBox,
|
|
148
|
-
options: {
|
|
149
|
-
placeholder: `@${i18n}:features.max-total-users.title`,
|
|
150
|
-
min: 1,
|
|
151
|
-
max: 10000,
|
|
152
|
-
step: 1,
|
|
153
|
-
},
|
|
154
|
-
},
|
|
155
|
-
});
|
|
156
|
-
// Manage Sessions feature
|
|
157
|
-
context.addFeature(AXPSecurityManagementFeatureKeys.ManageSessions, {
|
|
158
|
-
title: `@${i18n}:features.manage-sessions.title`,
|
|
159
|
-
description: `@${i18n}:features.manage-sessions.description`,
|
|
160
|
-
defaultValue: true,
|
|
161
|
-
type: 'boolean',
|
|
162
|
-
});
|
|
163
|
-
// Manage Policies feature
|
|
164
|
-
context.addFeature(AXPSecurityManagementFeatureKeys.ManagePolicies, {
|
|
165
|
-
title: `@${i18n}:features.manage-policies.title`,
|
|
166
|
-
description: `@${i18n}:features.manage-policies.description`,
|
|
167
|
-
defaultValue: true,
|
|
168
|
-
type: 'boolean',
|
|
169
|
-
});
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
//#endregion
|
|
173
|
-
|
|
174
|
-
const AXPPermissionsWidget = {
|
|
175
|
-
name: "permissions-editor",
|
|
176
|
-
title: "Permissions Editor",
|
|
177
|
-
description: 'Widget for managing permissions with full editor interface',
|
|
178
|
-
type: 'view',
|
|
179
|
-
categories: [],
|
|
180
|
-
groups: [AXPWidgetGroupEnum.EntityWidget],
|
|
181
|
-
icon: "fa-light fa-user-shield",
|
|
182
|
-
properties: [
|
|
183
|
-
AXP_NAME_PROPERTY,
|
|
184
|
-
AXP_DATA_PATH_PROPERTY,
|
|
185
|
-
AXP_DISABLED_PROPERTY,
|
|
186
|
-
],
|
|
187
|
-
components: {
|
|
188
|
-
view: {
|
|
189
|
-
component: () => Promise.resolve().then(function () { return permissionsWidgetView_component; }).then((c) => c.AXPPermissionsWidgetViewComponent),
|
|
190
|
-
},
|
|
191
|
-
edit: {
|
|
192
|
-
component: () => Promise.resolve().then(function () { return permissionsWidgetEdit_component; }).then((c) => c.AXPPermissionsWidgetEditComponent),
|
|
193
|
-
},
|
|
194
|
-
column: {
|
|
195
|
-
component: () => Promise.resolve().then(function () { return permissionsWidgetColumn_component; }).then((c) => c.AXPPermissionsWidgetColumnComponent),
|
|
196
|
-
},
|
|
197
|
-
}
|
|
198
|
-
};
|
|
199
|
-
|
|
200
|
-
//#endregion
|
|
201
|
-
//#region ---- Permissions Widgets Provider ----
|
|
202
|
-
const WIDGETS = [AXPPermissionsWidget];
|
|
203
|
-
class AXPPermissionsWidgetsProvider {
|
|
204
|
-
getWidgets() {
|
|
205
|
-
return WIDGETS;
|
|
206
|
-
}
|
|
207
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPPermissionsWidgetsProvider, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
208
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPPermissionsWidgetsProvider }); }
|
|
209
|
-
}
|
|
210
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPPermissionsWidgetsProvider, decorators: [{
|
|
211
|
-
type: Injectable
|
|
212
|
-
}] });
|
|
213
|
-
|
|
214
|
-
class AXMPermissionsModule {
|
|
215
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMPermissionsModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
|
|
216
|
-
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.9", ngImport: i0, type: AXMPermissionsModule, imports: [AXPWidgetCoreModule] }); }
|
|
217
|
-
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMPermissionsModule, providers: [
|
|
218
|
-
{ provide: AXP_WIDGET_DEFINITION_PROVIDER, useClass: AXPPermissionsWidgetsProvider, multi: true },
|
|
219
|
-
provideCommandSetups([
|
|
220
|
-
{
|
|
221
|
-
key: 'assign-permissions',
|
|
222
|
-
command: () => Promise.resolve().then(function () { return assignPermissions_command; }).then((c) => c.AXMAssignPermissionsCommand),
|
|
223
|
-
},
|
|
224
|
-
]),
|
|
225
|
-
], imports: [AXPWidgetCoreModule] }); }
|
|
226
|
-
}
|
|
227
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMPermissionsModule, decorators: [{
|
|
228
|
-
type: NgModule,
|
|
229
|
-
args: [{
|
|
230
|
-
imports: [
|
|
231
|
-
AXPWidgetCoreModule,
|
|
232
|
-
],
|
|
233
|
-
providers: [
|
|
234
|
-
{ provide: AXP_WIDGET_DEFINITION_PROVIDER, useClass: AXPPermissionsWidgetsProvider, multi: true },
|
|
235
|
-
provideCommandSetups([
|
|
236
|
-
{
|
|
237
|
-
key: 'assign-permissions',
|
|
238
|
-
command: () => Promise.resolve().then(function () { return assignPermissions_command; }).then((c) => c.AXMAssignPermissionsCommand),
|
|
239
|
-
},
|
|
240
|
-
]),
|
|
241
|
-
],
|
|
242
|
-
}]
|
|
243
|
-
}] });
|
|
244
|
-
|
|
245
|
-
class AXMSecurityManagementEvaluatorScopeProvider {
|
|
246
|
-
constructor() {
|
|
247
|
-
this.sessionService = inject(AXPSessionService);
|
|
248
|
-
}
|
|
249
|
-
async provide(context) {
|
|
250
|
-
context.addScope('session', {
|
|
251
|
-
currentUser: () => {
|
|
252
|
-
return this.sessionService.user;
|
|
253
|
-
},
|
|
254
|
-
user: () => {
|
|
255
|
-
return this.sessionService.user;
|
|
256
|
-
},
|
|
257
|
-
currentApplication: () => {
|
|
258
|
-
return this.sessionService.application;
|
|
259
|
-
},
|
|
260
|
-
currentTenant: () => {
|
|
261
|
-
return this.sessionService.tenant;
|
|
262
|
-
},
|
|
263
|
-
currentPermissions: () => {
|
|
264
|
-
return this.sessionService.permissions;
|
|
265
|
-
},
|
|
266
|
-
});
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
class AXMUsersDataSourceDefinition {
|
|
271
|
-
constructor() {
|
|
272
|
-
this.entityService = inject(AXPEntityService);
|
|
273
|
-
this.userService = this.entityService.withEntity(RootConfig.module.name, RootConfig.entities.users.name).data();
|
|
274
|
-
}
|
|
275
|
-
items() {
|
|
276
|
-
return Promise.resolve([
|
|
277
|
-
{
|
|
278
|
-
name: AXMSecurityManagementDataSourceKeys.Users,
|
|
279
|
-
title: 'Users',
|
|
280
|
-
source: () => {
|
|
281
|
-
return new AXDataSource({
|
|
282
|
-
key: 'id',
|
|
283
|
-
load: async (_) => {
|
|
284
|
-
try {
|
|
285
|
-
const res = await this.userService.query();
|
|
286
|
-
return {
|
|
287
|
-
items: res.items.map((item) => ({
|
|
288
|
-
...item,
|
|
289
|
-
displayName: item.displayName,
|
|
290
|
-
})),
|
|
291
|
-
total: res.total,
|
|
292
|
-
};
|
|
293
|
-
}
|
|
294
|
-
catch (e) {
|
|
295
|
-
console.log(e);
|
|
296
|
-
throw e;
|
|
297
|
-
}
|
|
298
|
-
},
|
|
299
|
-
byKey: async (key) => {
|
|
300
|
-
return await this.userService.byKey(key);
|
|
301
|
-
},
|
|
302
|
-
pageSize: 100,
|
|
303
|
-
});
|
|
304
|
-
},
|
|
305
|
-
columns: [
|
|
306
|
-
{
|
|
307
|
-
name: 'id',
|
|
308
|
-
title: 'ID',
|
|
309
|
-
datatype: 'uuid',
|
|
310
|
-
type: AXPWidgetsCatalog.text,
|
|
311
|
-
},
|
|
312
|
-
{
|
|
313
|
-
name: 'displayName',
|
|
314
|
-
title: `Display Name`,
|
|
315
|
-
datatype: 'string',
|
|
316
|
-
type: AXPWidgetsCatalog.text,
|
|
317
|
-
},
|
|
318
|
-
{
|
|
319
|
-
name: 'username',
|
|
320
|
-
title: `Username`,
|
|
321
|
-
datatype: 'string',
|
|
322
|
-
type: AXPWidgetsCatalog.text,
|
|
323
|
-
},
|
|
324
|
-
],
|
|
325
|
-
filters: [
|
|
326
|
-
{
|
|
327
|
-
field: 'username',
|
|
328
|
-
title: 'Username',
|
|
329
|
-
operator: {
|
|
330
|
-
type: 'equal',
|
|
331
|
-
},
|
|
332
|
-
widget: {
|
|
333
|
-
type: AXPWidgetsCatalog.text,
|
|
334
|
-
},
|
|
335
|
-
filterType: {
|
|
336
|
-
advance: true,
|
|
337
|
-
inline: true,
|
|
338
|
-
},
|
|
339
|
-
},
|
|
340
|
-
],
|
|
341
|
-
textField: {
|
|
342
|
-
name: 'displayName',
|
|
343
|
-
title: 'Display Name',
|
|
344
|
-
},
|
|
345
|
-
valueField: {
|
|
346
|
-
name: 'id',
|
|
347
|
-
title: 'ID',
|
|
348
|
-
},
|
|
349
|
-
},
|
|
350
|
-
]);
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
//#region ---- Imports ----
|
|
355
|
-
//#endregion
|
|
356
|
-
//#region ---- User access state provider ----
|
|
357
|
-
/**
|
|
358
|
-
* Sign-in readiness from the authentication subsystem (distinct from account lifecycle / `accountState`).
|
|
359
|
-
* Transitions listed here are the only paths offered in the status UI and validated when executing a status change command.
|
|
360
|
-
*/
|
|
361
|
-
class AXMUserAccessStateProvider extends AXPStatusProvider {
|
|
362
|
-
constructor() {
|
|
363
|
-
super(...arguments);
|
|
364
|
-
this.key = AXM_USER_ACCESS_STATE_DEFINITION_KEY;
|
|
365
|
-
this.defaultStatus = AXMUserAccessState.Allowed;
|
|
366
|
-
this.statuses = [
|
|
367
|
-
{
|
|
368
|
-
name: AXMUserAccessState.Allowed,
|
|
369
|
-
title: '@security-management:users.entities.user.access.states.allowed',
|
|
370
|
-
description: '@security-management:users.entities.user.access.descriptions.allowed',
|
|
371
|
-
icon: 'fa-light fa-circle-check',
|
|
372
|
-
color: 'success',
|
|
373
|
-
order: 1,
|
|
374
|
-
isInitial: true,
|
|
375
|
-
isFinal: false,
|
|
376
|
-
},
|
|
377
|
-
{
|
|
378
|
-
name: AXMUserAccessState.Blocked,
|
|
379
|
-
title: '@security-management:users.entities.user.access.states.blocked',
|
|
380
|
-
description: '@security-management:users.entities.user.access.descriptions.blocked',
|
|
381
|
-
icon: 'fa-light fa-ban',
|
|
382
|
-
color: 'danger',
|
|
383
|
-
order: 2,
|
|
384
|
-
isInitial: false,
|
|
385
|
-
isFinal: false,
|
|
386
|
-
},
|
|
387
|
-
{
|
|
388
|
-
name: AXMUserAccessState.Expired,
|
|
389
|
-
title: '@security-management:users.entities.user.access.states.expired',
|
|
390
|
-
description: '@security-management:users.entities.user.access.descriptions.expired',
|
|
391
|
-
icon: 'fa-light fa-clock',
|
|
392
|
-
color: 'danger',
|
|
393
|
-
order: 3,
|
|
394
|
-
isInitial: false,
|
|
395
|
-
isFinal: true,
|
|
396
|
-
generateView: false,
|
|
397
|
-
},
|
|
398
|
-
];
|
|
399
|
-
this.transitions = [
|
|
400
|
-
{
|
|
401
|
-
from: AXMUserAccessState.Allowed,
|
|
402
|
-
to: AXMUserAccessState.Blocked,
|
|
403
|
-
title: '@security-management:users.entities.user.access.transitions.block-sign-in',
|
|
404
|
-
},
|
|
405
|
-
{
|
|
406
|
-
from: AXMUserAccessState.Allowed,
|
|
407
|
-
to: AXMUserAccessState.Expired,
|
|
408
|
-
title: '@security-management:users.entities.user.access.transitions.require-credential-renewal',
|
|
409
|
-
},
|
|
410
|
-
{
|
|
411
|
-
from: AXMUserAccessState.Blocked,
|
|
412
|
-
to: AXMUserAccessState.Allowed,
|
|
413
|
-
title: '@security-management:users.entities.user.access.transitions.unblock-sign-in',
|
|
414
|
-
},
|
|
415
|
-
{
|
|
416
|
-
from: AXMUserAccessState.Expired,
|
|
417
|
-
to: AXMUserAccessState.Allowed,
|
|
418
|
-
title: '@security-management:users.entities.user.access.transitions.restore-after-credential-renewal',
|
|
419
|
-
},
|
|
420
|
-
];
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
//#endregion
|
|
424
|
-
|
|
425
|
-
//#region ---- Imports ----
|
|
426
|
-
//#endregion
|
|
427
|
-
//#region ---- User effective state provider ----
|
|
428
|
-
/**
|
|
429
|
-
* Unified operational state for lists, filters, and page chrome (derived from lifecycle + sign-in gate).
|
|
430
|
-
*/
|
|
431
|
-
class AXMUserEffectiveStateProvider extends AXPStatusProvider {
|
|
432
|
-
constructor() {
|
|
433
|
-
super(...arguments);
|
|
434
|
-
this.key = AXM_USER_EFFECTIVE_STATE_DEFINITION_KEY;
|
|
435
|
-
this.defaultStatus = AXMUserEffectiveState.Active;
|
|
436
|
-
this.statuses = [
|
|
437
|
-
{
|
|
438
|
-
name: AXMUserEffectiveState.Pending,
|
|
439
|
-
title: '@security-management:users.entities.user.effective.states.pending',
|
|
440
|
-
description: '@security-management:users.entities.user.effective.descriptions.pending',
|
|
441
|
-
icon: 'fa-light fa-hourglass-half',
|
|
442
|
-
color: 'warning',
|
|
443
|
-
order: 1,
|
|
444
|
-
generateView: true,
|
|
445
|
-
},
|
|
446
|
-
{
|
|
447
|
-
name: AXMUserEffectiveState.Active,
|
|
448
|
-
title: '@security-management:users.entities.user.effective.states.active',
|
|
449
|
-
description: '@security-management:users.entities.user.effective.descriptions.active',
|
|
450
|
-
icon: 'fa-light fa-circle-check',
|
|
451
|
-
color: 'success',
|
|
452
|
-
order: 2,
|
|
453
|
-
generateView: true,
|
|
454
|
-
},
|
|
455
|
-
{
|
|
456
|
-
name: AXMUserEffectiveState.SignInBlocked,
|
|
457
|
-
title: '@security-management:users.entities.user.effective.states.sign-in-blocked',
|
|
458
|
-
description: '@security-management:users.entities.user.effective.descriptions.sign-in-blocked',
|
|
459
|
-
icon: 'fa-light fa-shield-halved',
|
|
460
|
-
color: 'danger',
|
|
461
|
-
order: 3,
|
|
462
|
-
generateView: true,
|
|
463
|
-
},
|
|
464
|
-
{
|
|
465
|
-
name: AXMUserEffectiveState.CredentialExpired,
|
|
466
|
-
title: '@security-management:users.entities.user.effective.states.credential-expired',
|
|
467
|
-
description: '@security-management:users.entities.user.effective.descriptions.credential-expired',
|
|
468
|
-
icon: 'fa-light fa-key',
|
|
469
|
-
color: 'danger',
|
|
470
|
-
order: 4,
|
|
471
|
-
generateView: true,
|
|
472
|
-
},
|
|
473
|
-
{
|
|
474
|
-
name: AXMUserEffectiveState.Suspended,
|
|
475
|
-
title: '@security-management:users.entities.user.effective.states.suspended',
|
|
476
|
-
description: '@security-management:users.entities.user.effective.descriptions.suspended',
|
|
477
|
-
icon: 'fa-light fa-pause-circle',
|
|
478
|
-
color: 'warning',
|
|
479
|
-
order: 5,
|
|
480
|
-
generateView: true,
|
|
481
|
-
},
|
|
482
|
-
{
|
|
483
|
-
name: AXMUserEffectiveState.Inactive,
|
|
484
|
-
title: '@security-management:users.entities.user.effective.states.inactive',
|
|
485
|
-
description: '@security-management:users.entities.user.effective.descriptions.inactive',
|
|
486
|
-
icon: 'fa-light fa-user-slash',
|
|
487
|
-
color: 'danger',
|
|
488
|
-
order: 6,
|
|
489
|
-
generateView: true,
|
|
490
|
-
},
|
|
491
|
-
{
|
|
492
|
-
name: AXMUserEffectiveState.Archived,
|
|
493
|
-
title: '@security-management:users.entities.user.effective.states.archived',
|
|
494
|
-
description: '@security-management:users.entities.user.effective.descriptions.archived',
|
|
495
|
-
icon: 'fa-light fa-box-archive',
|
|
496
|
-
color: 'danger',
|
|
497
|
-
order: 7,
|
|
498
|
-
generateView: true,
|
|
499
|
-
},
|
|
500
|
-
];
|
|
501
|
-
this.transitions = [];
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
//#endregion
|
|
505
|
-
|
|
506
|
-
//#region ---- Imports ----
|
|
507
|
-
//#endregion
|
|
508
|
-
//#region ---- User account status provider ----
|
|
509
|
-
/**
|
|
510
|
-
* Account-level state for SecurityManagement.User (lifecycle / admin).
|
|
511
|
-
* Sign-in lockout is expressed as `accessState === blocked`, not as `accountState`.
|
|
512
|
-
*/
|
|
513
|
-
class AXMUserStatusProvider extends AXPStatusProvider {
|
|
514
|
-
constructor() {
|
|
515
|
-
super(...arguments);
|
|
516
|
-
this.key = 'security-management.account-state.user';
|
|
517
|
-
this.defaultStatus = AXPSystemStatusType.Active;
|
|
518
|
-
this.statuses = [
|
|
519
|
-
{
|
|
520
|
-
name: AXPSystemStatusType.Pending,
|
|
521
|
-
title: '@security-management:users.entities.user.states.pending',
|
|
522
|
-
description: '@security-management:users.entities.user.account.descriptions.pending',
|
|
523
|
-
icon: 'fa-light fa-envelope-open-text',
|
|
524
|
-
color: 'warning',
|
|
525
|
-
order: 1,
|
|
526
|
-
isInitial: true,
|
|
527
|
-
isFinal: false,
|
|
528
|
-
generateView: true,
|
|
529
|
-
},
|
|
530
|
-
{
|
|
531
|
-
name: AXPSystemStatusType.Active,
|
|
532
|
-
title: '@security-management:users.entities.user.states.active',
|
|
533
|
-
description: '@security-management:users.entities.user.account.descriptions.active',
|
|
534
|
-
icon: 'fa-light fa-user-check',
|
|
535
|
-
color: 'success',
|
|
536
|
-
order: 1,
|
|
537
|
-
isInitial: false,
|
|
538
|
-
isFinal: false,
|
|
539
|
-
generateView: true,
|
|
540
|
-
},
|
|
541
|
-
{
|
|
542
|
-
name: AXPSystemStatusType.Suspended,
|
|
543
|
-
title: '@security-management:users.entities.user.states.suspended',
|
|
544
|
-
description: '@security-management:users.entities.user.account.descriptions.suspended',
|
|
545
|
-
icon: 'fa-light fa-pause-circle',
|
|
546
|
-
color: 'warning',
|
|
547
|
-
order: 5,
|
|
548
|
-
isInitial: false,
|
|
549
|
-
isFinal: false,
|
|
550
|
-
generateView: true,
|
|
551
|
-
},
|
|
552
|
-
{
|
|
553
|
-
name: AXPSystemStatusType.Inactive,
|
|
554
|
-
title: '@security-management:users.entities.user.states.inactive',
|
|
555
|
-
description: '@security-management:users.entities.user.account.descriptions.inactive',
|
|
556
|
-
icon: 'fa-light fa-user-slash',
|
|
557
|
-
color: 'danger',
|
|
558
|
-
order: 2,
|
|
559
|
-
isInitial: false,
|
|
560
|
-
isFinal: false,
|
|
561
|
-
generateView: true,
|
|
562
|
-
},
|
|
563
|
-
{
|
|
564
|
-
name: AXPSystemStatusType.Archived,
|
|
565
|
-
title: '@security-management:users.entities.user.states.archived',
|
|
566
|
-
description: '@security-management:users.entities.user.account.descriptions.archived',
|
|
567
|
-
icon: 'fa-light fa-box-archive',
|
|
568
|
-
color: 'danger',
|
|
569
|
-
order: 5,
|
|
570
|
-
isInitial: false,
|
|
571
|
-
isFinal: true,
|
|
572
|
-
generateView: true,
|
|
573
|
-
},
|
|
574
|
-
];
|
|
575
|
-
this.transitions = [
|
|
576
|
-
{
|
|
577
|
-
from: AXPSystemStatusType.Pending,
|
|
578
|
-
to: AXPSystemStatusType.Active,
|
|
579
|
-
title: '@security-management:users.entities.user.transitions.activate',
|
|
580
|
-
},
|
|
581
|
-
{
|
|
582
|
-
from: AXPSystemStatusType.Pending,
|
|
583
|
-
to: AXPSystemStatusType.Inactive,
|
|
584
|
-
title: '@security-management:users.entities.user.transitions.cancel-invitation',
|
|
585
|
-
},
|
|
586
|
-
{
|
|
587
|
-
from: AXPSystemStatusType.Active,
|
|
588
|
-
to: AXPSystemStatusType.Suspended,
|
|
589
|
-
title: '@security-management:users.entities.user.transitions.suspend',
|
|
590
|
-
},
|
|
591
|
-
{
|
|
592
|
-
from: AXPSystemStatusType.Suspended,
|
|
593
|
-
to: AXPSystemStatusType.Active,
|
|
594
|
-
title: '@security-management:users.entities.user.transitions.restore',
|
|
595
|
-
},
|
|
596
|
-
{
|
|
597
|
-
from: AXPSystemStatusType.Active,
|
|
598
|
-
to: AXPSystemStatusType.Inactive,
|
|
599
|
-
title: '@security-management:users.entities.user.transitions.disable',
|
|
600
|
-
},
|
|
601
|
-
{
|
|
602
|
-
from: AXPSystemStatusType.Inactive,
|
|
603
|
-
to: AXPSystemStatusType.Active,
|
|
604
|
-
title: '@security-management:users.entities.user.transitions.enable',
|
|
605
|
-
},
|
|
606
|
-
{
|
|
607
|
-
from: AXPSystemStatusType.Suspended,
|
|
608
|
-
to: AXPSystemStatusType.Inactive,
|
|
609
|
-
title: '@security-management:users.entities.user.transitions.disable',
|
|
610
|
-
},
|
|
611
|
-
{
|
|
612
|
-
from: AXPSystemStatusType.Active,
|
|
613
|
-
to: AXPSystemStatusType.Archived,
|
|
614
|
-
title: '@security-management:users.entities.user.transitions.archive',
|
|
615
|
-
},
|
|
616
|
-
{
|
|
617
|
-
from: AXPSystemStatusType.Suspended,
|
|
618
|
-
to: AXPSystemStatusType.Archived,
|
|
619
|
-
title: '@security-management:users.entities.user.transitions.archive',
|
|
620
|
-
},
|
|
621
|
-
{
|
|
622
|
-
from: AXPSystemStatusType.Inactive,
|
|
623
|
-
to: AXPSystemStatusType.Archived,
|
|
624
|
-
title: '@security-management:users.entities.user.transitions.archive',
|
|
625
|
-
},
|
|
626
|
-
{
|
|
627
|
-
from: AXPSystemStatusType.Pending,
|
|
628
|
-
to: AXPSystemStatusType.Archived,
|
|
629
|
-
title: '@security-management:users.entities.user.transitions.archive',
|
|
630
|
-
},
|
|
631
|
-
{
|
|
632
|
-
from: AXPSystemStatusType.Archived,
|
|
633
|
-
to: AXPSystemStatusType.Active,
|
|
634
|
-
title: '@security-management:users.entities.user.transitions.restore-archived',
|
|
635
|
-
},
|
|
636
|
-
];
|
|
637
|
-
}
|
|
638
|
-
}
|
|
639
|
-
//#endregion
|
|
640
|
-
|
|
641
|
-
class AXMMenuProvider {
|
|
642
|
-
constructor() {
|
|
643
|
-
this.entityService = inject(AXPEntityService);
|
|
644
|
-
}
|
|
645
|
-
async provide(context) {
|
|
646
|
-
const moduleName = RootConfig.module.name;
|
|
647
|
-
// Check if Security submenu exists under Administration, create it if not
|
|
648
|
-
const securityMenu = context.find(AXPSecurityManagementMenuKeys.Root);
|
|
649
|
-
if (!securityMenu.exists) {
|
|
650
|
-
// Add Security submenu to Administration group
|
|
651
|
-
context.find(AXPCommonMenuKeys.Administration).addItems([
|
|
652
|
-
{
|
|
653
|
-
priority: 1,
|
|
654
|
-
text: '@security-management:module.menus.root.title',
|
|
655
|
-
icon: RootConfig.module.icon,
|
|
656
|
-
name: AXPSecurityManagementMenuKeys.Root,
|
|
657
|
-
type: 'menu',
|
|
658
|
-
children: [],
|
|
659
|
-
policy: {
|
|
660
|
-
features: [RootConfig.module.name],
|
|
661
|
-
},
|
|
662
|
-
},
|
|
663
|
-
]);
|
|
664
|
-
}
|
|
665
|
-
// Add menu items under Security submenu
|
|
666
|
-
context.find(AXPSecurityManagementMenuKeys.Root).addItems([
|
|
667
|
-
{
|
|
668
|
-
priority: 9003,
|
|
669
|
-
text: RootConfig.entities.users.titlePlural,
|
|
670
|
-
icon: RootConfig.entities.users.icon,
|
|
671
|
-
name: AXPSecurityManagementMenuKeys.Users,
|
|
672
|
-
path: this.entityService.createPath(moduleName, RootConfig.entities.users.name),
|
|
673
|
-
policy: {
|
|
674
|
-
features: [moduleName],
|
|
675
|
-
permissions: [AXPSecurityManagementPermissionKeys.User.View],
|
|
676
|
-
},
|
|
677
|
-
},
|
|
678
|
-
{
|
|
679
|
-
priority: 9004,
|
|
680
|
-
text: RootConfig.entities.roles.titlePlural,
|
|
681
|
-
path: this.entityService.createPath(moduleName, RootConfig.entities.roles.name),
|
|
682
|
-
icon: RootConfig.entities.roles.icon,
|
|
683
|
-
name: AXPSecurityManagementMenuKeys.Roles,
|
|
684
|
-
policy: {
|
|
685
|
-
features: [moduleName],
|
|
686
|
-
permissions: [AXPSecurityManagementPermissionKeys.Role.View],
|
|
687
|
-
},
|
|
688
|
-
},
|
|
689
|
-
{
|
|
690
|
-
priority: 9005,
|
|
691
|
-
text: RootConfig.entities.deviceSessions.titlePlural,
|
|
692
|
-
path: this.entityService.createPath(moduleName, RootConfig.entities.deviceSessions.name),
|
|
693
|
-
icon: RootConfig.entities.deviceSessions.icon,
|
|
694
|
-
name: AXPSecurityManagementMenuKeys.DeviceSessions,
|
|
695
|
-
policy: {
|
|
696
|
-
features: [moduleName],
|
|
697
|
-
permissions: [AXPSecurityManagementPermissionKeys.DeviceSession.View],
|
|
698
|
-
},
|
|
699
|
-
},
|
|
700
|
-
// {
|
|
701
|
-
// priority: 9006,
|
|
702
|
-
// text: RootConfig.entities.policy.titlePlural,
|
|
703
|
-
// path: this.entityService.createPath(moduleName, RootConfig.entities.policy.name),
|
|
704
|
-
// icon: RootConfig.entities.policy.icon,
|
|
705
|
-
// name: AXPSecurityManagementMenuKeys.Policy,
|
|
706
|
-
// policy: {
|
|
707
|
-
// permissions: [AXPSecurityManagementPermissionKeys.Policy.View],
|
|
708
|
-
// },
|
|
709
|
-
// },
|
|
710
|
-
]);
|
|
711
|
-
}
|
|
712
|
-
}
|
|
713
|
-
|
|
714
|
-
//#endregion
|
|
715
|
-
//#region ---- Permission Definition Provider ----
|
|
716
|
-
class AXMPermissionDefinitionProvider {
|
|
717
|
-
//#region ---- Define Method ----
|
|
718
|
-
async define(context) {
|
|
719
|
-
const keys = AXPSecurityManagementPermissionKeys;
|
|
720
|
-
// I18n path constants
|
|
721
|
-
const MODULE_PERMISSIONS = '@security-management:module.permissions';
|
|
722
|
-
const USERS_PERMISSIONS = '@security-management:users.permissions';
|
|
723
|
-
const ROLES_PERMISSIONS = '@security-management:roles.permissions';
|
|
724
|
-
const SESSIONS_PERMISSIONS = '@security-management:sessions.permissions';
|
|
725
|
-
const POLICIES_PERMISSIONS = '@security-management:policies.permissions';
|
|
726
|
-
context
|
|
727
|
-
.addGroup(RootConfig.module.name, `${MODULE_PERMISSIONS}.title`, `${MODULE_PERMISSIONS}.description`)
|
|
728
|
-
//
|
|
729
|
-
.addPermission(keys.User.Management, `${USERS_PERMISSIONS}.manage.title`, `${USERS_PERMISSIONS}.manage.description`)
|
|
730
|
-
.addChild(keys.User.View, `${USERS_PERMISSIONS}.view.title`, `${USERS_PERMISSIONS}.view.description`)
|
|
731
|
-
.addChild(keys.User.Create, `${USERS_PERMISSIONS}.create.title`, `${USERS_PERMISSIONS}.create.description`)
|
|
732
|
-
.addChild(keys.User.Edit, `${USERS_PERMISSIONS}.edit.title`, `${USERS_PERMISSIONS}.edit.description`)
|
|
733
|
-
.addChild(keys.User.Delete, `${USERS_PERMISSIONS}.delete.title`, `${USERS_PERMISSIONS}.delete.description`)
|
|
734
|
-
.endPermission()
|
|
735
|
-
//
|
|
736
|
-
.addPermission(keys.Role.Management, `${ROLES_PERMISSIONS}.manage.title`, `${ROLES_PERMISSIONS}.manage.description`)
|
|
737
|
-
.addChild(keys.Role.View, `${ROLES_PERMISSIONS}.view.title`, `${ROLES_PERMISSIONS}.view.description`)
|
|
738
|
-
.addChild(keys.Role.Create, `${ROLES_PERMISSIONS}.create.title`, `${ROLES_PERMISSIONS}.create.description`)
|
|
739
|
-
.addChild(keys.Role.Edit, `${ROLES_PERMISSIONS}.edit.title`, `${ROLES_PERMISSIONS}.edit.description`)
|
|
740
|
-
.addChild(keys.Role.Delete, `${ROLES_PERMISSIONS}.delete.title`, `${ROLES_PERMISSIONS}.delete.description`)
|
|
741
|
-
.endPermission()
|
|
742
|
-
//
|
|
743
|
-
.addPermission(keys.DeviceSession.Management, `${SESSIONS_PERMISSIONS}.manage.title`, `${SESSIONS_PERMISSIONS}.manage.description`)
|
|
744
|
-
.addChild(keys.DeviceSession.View, `${SESSIONS_PERMISSIONS}.view.title`, `${SESSIONS_PERMISSIONS}.view.description`)
|
|
745
|
-
.addChild(keys.DeviceSession.Terminate, `${SESSIONS_PERMISSIONS}.terminate.title`, `${SESSIONS_PERMISSIONS}.terminate.description`)
|
|
746
|
-
.endPermission()
|
|
747
|
-
//
|
|
748
|
-
.addPermission(keys.Policy.Management, `${POLICIES_PERMISSIONS}.manage.title`, `${POLICIES_PERMISSIONS}.manage.description`)
|
|
749
|
-
.addChild(keys.Policy.View, `${POLICIES_PERMISSIONS}.view.title`, `${POLICIES_PERMISSIONS}.view.description`)
|
|
750
|
-
.addChild(keys.Policy.Create, `${POLICIES_PERMISSIONS}.create.title`, `${POLICIES_PERMISSIONS}.create.description`)
|
|
751
|
-
.addChild(keys.Policy.Edit, `${POLICIES_PERMISSIONS}.edit.title`, `${POLICIES_PERMISSIONS}.edit.description`)
|
|
752
|
-
.addChild(keys.Policy.Delete, `${POLICIES_PERMISSIONS}.delete.title`, `${POLICIES_PERMISSIONS}.delete.description`)
|
|
753
|
-
.endPermission()
|
|
754
|
-
//
|
|
755
|
-
.endGroup();
|
|
756
|
-
}
|
|
757
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMPermissionDefinitionProvider, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
758
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMPermissionDefinitionProvider }); }
|
|
759
|
-
}
|
|
760
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMPermissionDefinitionProvider, decorators: [{
|
|
761
|
-
type: Injectable
|
|
762
|
-
}] });
|
|
763
|
-
|
|
764
|
-
// TODO: Refactor to generic provider (do it later)
|
|
765
|
-
//
|
|
766
|
-
// Move this logic to a generic AXPEntityCreateSearchCommandProvider in libs/platform/layout/entity.
|
|
767
|
-
// The generic provider should:
|
|
768
|
-
// - Inject AXPEntityDefinitionRegistryService and AXPSessionService
|
|
769
|
-
// - Override search(text) to build commands dynamically from registry
|
|
770
|
-
// - Filter entities: only aggregate roots (commands?.create exists AND !parentKey)
|
|
771
|
-
// - Filter by permission: sessionService.authorize() for both Create and Management
|
|
772
|
-
// Permission convention: `${module}:Permission:${entity.name}.Create` or `.Management`
|
|
773
|
-
// - Use createEntityCommandOptions() with Entity:Create command (same pattern as below)
|
|
774
|
-
// - Register in entity module as AXP_SEARCH_PROVIDER
|
|
775
|
-
// - Remove this and other module-specific search command providers that only add entity-create commands
|
|
776
|
-
class AXMSearchCommandProvider extends AXPSearchCommandProvider {
|
|
777
|
-
constructor() {
|
|
778
|
-
super(...arguments);
|
|
779
|
-
this.commands = [
|
|
780
|
-
createEntitySearchCommand({
|
|
781
|
-
id: 'create-security-user',
|
|
782
|
-
entityKey: `${RootConfig.module.name}.${RootConfig.entities.users.name}`,
|
|
783
|
-
entityInfo: { name: RootConfig.entities.users.name, module: RootConfig.module.name, title: RootConfig.entities.users.title },
|
|
784
|
-
titleKey: '@security-management:users.permissions.create.title',
|
|
785
|
-
descriptionKey: '@security-management:users.permissions.create.description',
|
|
786
|
-
icon: RootConfig.entities.users.icon,
|
|
787
|
-
}),
|
|
788
|
-
createEntitySearchCommand({
|
|
789
|
-
id: 'create-security-role',
|
|
790
|
-
entityKey: `${RootConfig.module.name}.${RootConfig.entities.roles.name}`,
|
|
791
|
-
entityInfo: { name: RootConfig.entities.roles.name, module: RootConfig.module.name, title: RootConfig.entities.roles.title },
|
|
792
|
-
titleKey: '@security-management:roles.permissions.create.title',
|
|
793
|
-
descriptionKey: '@security-management:roles.permissions.create.description',
|
|
794
|
-
icon: RootConfig.entities.roles.icon,
|
|
795
|
-
}),
|
|
796
|
-
];
|
|
797
|
-
}
|
|
798
|
-
}
|
|
799
|
-
|
|
800
|
-
//#region ---- User context definitions ----
|
|
801
|
-
class AXMSecurityManagementContextDefinitionProvider {
|
|
802
|
-
async items() {
|
|
803
|
-
return [
|
|
804
|
-
{
|
|
805
|
-
name: 'userId',
|
|
806
|
-
title: 'User ID',
|
|
807
|
-
description: 'Identifier of the signed-in user from the current session. Used to scope AI actions and audit to the active account.',
|
|
808
|
-
ai: {
|
|
809
|
-
promptLabel: 'User id',
|
|
810
|
-
usageHint: 'Treat as the active user unless the request explicitly refers to another person.',
|
|
811
|
-
valueKind: 'string',
|
|
812
|
-
order: 10,
|
|
813
|
-
},
|
|
814
|
-
},
|
|
815
|
-
{
|
|
816
|
-
name: 'userDisplayName',
|
|
817
|
-
title: 'User display name',
|
|
818
|
-
description: 'Human-readable name of the signed-in user (name or title from session). Used for personalized responses.',
|
|
819
|
-
ai: {
|
|
820
|
-
promptLabel: 'User display name',
|
|
821
|
-
usageHint: 'Use for polite address; do not invent a different name.',
|
|
822
|
-
valueKind: 'string',
|
|
823
|
-
order: 11,
|
|
824
|
-
},
|
|
825
|
-
},
|
|
826
|
-
];
|
|
827
|
-
}
|
|
828
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMSecurityManagementContextDefinitionProvider, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
829
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMSecurityManagementContextDefinitionProvider }); }
|
|
830
|
-
}
|
|
831
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMSecurityManagementContextDefinitionProvider, decorators: [{
|
|
832
|
-
type: Injectable
|
|
833
|
-
}] });
|
|
834
|
-
|
|
835
|
-
class AXMSecurityManagementModule {
|
|
836
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMSecurityManagementModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
|
|
837
|
-
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.9", ngImport: i0, type: AXMSecurityManagementModule, imports: [AXMPermissionsModule] }); }
|
|
838
|
-
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMSecurityManagementModule, providers: [
|
|
839
|
-
// Module-specific providers (not part of manifest system)
|
|
840
|
-
{
|
|
841
|
-
provide: AXP_EXPRESSION_EVALUATOR_SCOPE_PROVIDER,
|
|
842
|
-
useClass: AXMSecurityManagementEvaluatorScopeProvider,
|
|
843
|
-
multi: true,
|
|
844
|
-
},
|
|
845
|
-
// Module Manifest Provider
|
|
846
|
-
{
|
|
847
|
-
provide: AXP_MODULE_MANIFEST_PROVIDER,
|
|
848
|
-
useValue: SecurityManagementManifest,
|
|
849
|
-
multi: true,
|
|
850
|
-
},
|
|
851
|
-
// Provider references (now using tokens)
|
|
852
|
-
{
|
|
853
|
-
provide: AXP_PERMISSION_DEFINITION_PROVIDER,
|
|
854
|
-
useClass: AXMPermissionDefinitionProvider,
|
|
855
|
-
multi: true,
|
|
856
|
-
},
|
|
857
|
-
{
|
|
858
|
-
provide: AXP_MENU_PROVIDER,
|
|
859
|
-
useClass: AXMMenuProvider,
|
|
860
|
-
multi: true,
|
|
861
|
-
},
|
|
862
|
-
{
|
|
863
|
-
provide: AXP_ENTITY_DEFINITION_LOADER,
|
|
864
|
-
useClass: AXMEntityProvider,
|
|
865
|
-
multi: true,
|
|
866
|
-
},
|
|
867
|
-
{
|
|
868
|
-
provide: AXP_SEARCH_PROVIDER,
|
|
869
|
-
useClass: AXMSearchCommandProvider,
|
|
870
|
-
multi: true,
|
|
871
|
-
},
|
|
872
|
-
{
|
|
873
|
-
provide: AXP_FEATURE_DEFINITION_PROVIDER,
|
|
874
|
-
useClass: AXMSecurityManagementFeatureDefinitionProvider,
|
|
875
|
-
multi: true,
|
|
876
|
-
},
|
|
877
|
-
{
|
|
878
|
-
provide: AXP_STATUS_PROVIDERS,
|
|
879
|
-
useClass: AXMUserStatusProvider,
|
|
880
|
-
multi: true,
|
|
881
|
-
},
|
|
882
|
-
{
|
|
883
|
-
provide: AXP_STATUS_PROVIDERS,
|
|
884
|
-
useClass: AXMUserAccessStateProvider,
|
|
885
|
-
multi: true,
|
|
886
|
-
},
|
|
887
|
-
{
|
|
888
|
-
provide: AXP_STATUS_PROVIDERS,
|
|
889
|
-
useClass: AXMUserEffectiveStateProvider,
|
|
890
|
-
multi: true,
|
|
891
|
-
},
|
|
892
|
-
{
|
|
893
|
-
provide: AXP_DATASOURCE_DEFINITION_PROVIDER,
|
|
894
|
-
useClass: AXMUsersDataSourceDefinition,
|
|
895
|
-
multi: true,
|
|
896
|
-
},
|
|
897
|
-
{
|
|
898
|
-
provide: AXP_CONTEXT_DEFINITION_PROVIDER,
|
|
899
|
-
useClass: AXMSecurityManagementContextDefinitionProvider,
|
|
900
|
-
multi: true,
|
|
901
|
-
},
|
|
902
|
-
provideCommandSetups([
|
|
903
|
-
{
|
|
904
|
-
key: 'Session:Terminate',
|
|
905
|
-
command: () => import('./acorex-platform-module-security-management-client-terminate-session.command-CKq-4Z4O.mjs').then((c) => c.AXPTerminateSessionCommand),
|
|
906
|
-
},
|
|
907
|
-
{
|
|
908
|
-
key: 'SecurityManagement:User:SendPasswordResetEmail',
|
|
909
|
-
command: () => import('./acorex-platform-module-security-management-client-send-password-reset-email.command-DzfUvFk2.mjs').then((c) => c.AXPSendPasswordResetEmailCommand),
|
|
910
|
-
},
|
|
911
|
-
]),
|
|
912
|
-
], imports: [AXMPermissionsModule] }); }
|
|
913
|
-
}
|
|
914
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMSecurityManagementModule, decorators: [{
|
|
915
|
-
type: NgModule,
|
|
916
|
-
args: [{
|
|
917
|
-
imports: [
|
|
918
|
-
AXMPermissionsModule,
|
|
919
|
-
],
|
|
920
|
-
exports: [],
|
|
921
|
-
declarations: [],
|
|
922
|
-
providers: [
|
|
923
|
-
// Module-specific providers (not part of manifest system)
|
|
924
|
-
{
|
|
925
|
-
provide: AXP_EXPRESSION_EVALUATOR_SCOPE_PROVIDER,
|
|
926
|
-
useClass: AXMSecurityManagementEvaluatorScopeProvider,
|
|
927
|
-
multi: true,
|
|
928
|
-
},
|
|
929
|
-
// Module Manifest Provider
|
|
930
|
-
{
|
|
931
|
-
provide: AXP_MODULE_MANIFEST_PROVIDER,
|
|
932
|
-
useValue: SecurityManagementManifest,
|
|
933
|
-
multi: true,
|
|
934
|
-
},
|
|
935
|
-
// Provider references (now using tokens)
|
|
936
|
-
{
|
|
937
|
-
provide: AXP_PERMISSION_DEFINITION_PROVIDER,
|
|
938
|
-
useClass: AXMPermissionDefinitionProvider,
|
|
939
|
-
multi: true,
|
|
940
|
-
},
|
|
941
|
-
{
|
|
942
|
-
provide: AXP_MENU_PROVIDER,
|
|
943
|
-
useClass: AXMMenuProvider,
|
|
944
|
-
multi: true,
|
|
945
|
-
},
|
|
946
|
-
{
|
|
947
|
-
provide: AXP_ENTITY_DEFINITION_LOADER,
|
|
948
|
-
useClass: AXMEntityProvider,
|
|
949
|
-
multi: true,
|
|
950
|
-
},
|
|
951
|
-
{
|
|
952
|
-
provide: AXP_SEARCH_PROVIDER,
|
|
953
|
-
useClass: AXMSearchCommandProvider,
|
|
954
|
-
multi: true,
|
|
955
|
-
},
|
|
956
|
-
{
|
|
957
|
-
provide: AXP_FEATURE_DEFINITION_PROVIDER,
|
|
958
|
-
useClass: AXMSecurityManagementFeatureDefinitionProvider,
|
|
959
|
-
multi: true,
|
|
960
|
-
},
|
|
961
|
-
{
|
|
962
|
-
provide: AXP_STATUS_PROVIDERS,
|
|
963
|
-
useClass: AXMUserStatusProvider,
|
|
964
|
-
multi: true,
|
|
965
|
-
},
|
|
966
|
-
{
|
|
967
|
-
provide: AXP_STATUS_PROVIDERS,
|
|
968
|
-
useClass: AXMUserAccessStateProvider,
|
|
969
|
-
multi: true,
|
|
970
|
-
},
|
|
971
|
-
{
|
|
972
|
-
provide: AXP_STATUS_PROVIDERS,
|
|
973
|
-
useClass: AXMUserEffectiveStateProvider,
|
|
974
|
-
multi: true,
|
|
975
|
-
},
|
|
976
|
-
{
|
|
977
|
-
provide: AXP_DATASOURCE_DEFINITION_PROVIDER,
|
|
978
|
-
useClass: AXMUsersDataSourceDefinition,
|
|
979
|
-
multi: true,
|
|
980
|
-
},
|
|
981
|
-
{
|
|
982
|
-
provide: AXP_CONTEXT_DEFINITION_PROVIDER,
|
|
983
|
-
useClass: AXMSecurityManagementContextDefinitionProvider,
|
|
984
|
-
multi: true,
|
|
985
|
-
},
|
|
986
|
-
provideCommandSetups([
|
|
987
|
-
{
|
|
988
|
-
key: 'Session:Terminate',
|
|
989
|
-
command: () => import('./acorex-platform-module-security-management-client-terminate-session.command-CKq-4Z4O.mjs').then((c) => c.AXPTerminateSessionCommand),
|
|
990
|
-
},
|
|
991
|
-
{
|
|
992
|
-
key: 'SecurityManagement:User:SendPasswordResetEmail',
|
|
993
|
-
command: () => import('./acorex-platform-module-security-management-client-send-password-reset-email.command-DzfUvFk2.mjs').then((c) => c.AXPSendPasswordResetEmailCommand),
|
|
994
|
-
},
|
|
995
|
-
]),
|
|
996
|
-
],
|
|
997
|
-
}]
|
|
998
|
-
}] });
|
|
999
|
-
|
|
1000
|
-
// #region ---------------- Imports ----------------
|
|
1001
|
-
// #endregion
|
|
1002
|
-
class AXPPermissionsEditorComponent extends AXBasePageComponent {
|
|
1003
|
-
// #endregion
|
|
1004
|
-
// #region ---------------- Constructor ----------------
|
|
1005
|
-
constructor() {
|
|
1006
|
-
super();
|
|
1007
|
-
// #region ---------------- Class Properties ----------------
|
|
1008
|
-
/** Service for fetching permission groups and definitions */
|
|
1009
|
-
this.permissionService = inject(AXPPermissionDefinitionService);
|
|
1010
|
-
/** Session service for checking module access */
|
|
1011
|
-
this.sessionService = inject(AXPSessionService);
|
|
1012
|
-
/** Translation service for resolving permission labels during search */
|
|
1013
|
-
this.translationService = inject(AXTranslationService);
|
|
1014
|
-
/** Currently selected permission group */
|
|
1015
|
-
this.selectedGroup = signal(null, ...(ngDevMode ? [{ debugName: "selectedGroup" }] : /* istanbul ignore next */ []));
|
|
1016
|
-
/** Current search query */
|
|
1017
|
-
this.searchQuery = signal('', ...(ngDevMode ? [{ debugName: "searchQuery" }] : /* istanbul ignore next */ []));
|
|
1018
|
-
/** All available permission groups */
|
|
1019
|
-
this.groups = signal([], ...(ngDevMode ? [{ debugName: "groups" }] : /* istanbul ignore next */ []));
|
|
1020
|
-
/** Initial permissions to be selected - exposed as signal input */
|
|
1021
|
-
this.initialPermissions = input([], ...(ngDevMode ? [{ debugName: "initialPermissions" }] : /* istanbul ignore next */ []));
|
|
1022
|
-
/** Whether the editor is in read-only mode */
|
|
1023
|
-
this.readOnly = input(false, ...(ngDevMode ? [{ debugName: "readOnly" }] : /* istanbul ignore next */ []));
|
|
1024
|
-
/** Whether popup footer actions (Cancel / Select) are shown */
|
|
1025
|
-
this.showActions = input(false, ...(ngDevMode ? [{ debugName: "showActions" }] : /* istanbul ignore next */ []));
|
|
1026
|
-
/** List of selected permission names */
|
|
1027
|
-
this.selectedPermissions = signal([], ...(ngDevMode ? [{ debugName: "selectedPermissions" }] : /* istanbul ignore next */ []));
|
|
1028
|
-
/** Event emitted when permissions selection changes */
|
|
1029
|
-
this.permissionsChanged = output();
|
|
1030
|
-
/** Event emitted when permissions are applied/selected */
|
|
1031
|
-
this.permissionsSelected = output();
|
|
1032
|
-
/** Whether permission groups are being loaded */
|
|
1033
|
-
this.isEditorLoading = signal(true, ...(ngDevMode ? [{ debugName: "isEditorLoading" }] : /* istanbul ignore next */ []));
|
|
1034
|
-
/**
|
|
1035
|
-
* Computed: Returns mapped permissions for the selected group.
|
|
1036
|
-
*/
|
|
1037
|
-
this.items = computed(() => {
|
|
1038
|
-
const permissions = this.selectedGroup()?.permissions || [];
|
|
1039
|
-
return this.mapPermissions(permissions);
|
|
1040
|
-
}, ...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
|
|
1041
|
-
/**
|
|
1042
|
-
* Computed: Returns groups with badge counts, sorted by title.
|
|
1043
|
-
*/
|
|
1044
|
-
this.groupsWithBadge = computed(() => {
|
|
1045
|
-
const groups = this.groups();
|
|
1046
|
-
const result = groups.map((group) => {
|
|
1047
|
-
return {
|
|
1048
|
-
...group,
|
|
1049
|
-
badge: this.getBadge(group) || '',
|
|
1050
|
-
};
|
|
1051
|
-
});
|
|
1052
|
-
return result.sort((a, b) => a.title.localeCompare(b.title));
|
|
1053
|
-
}, ...(ngDevMode ? [{ debugName: "groupsWithBadge" }] : /* istanbul ignore next */ []));
|
|
1054
|
-
/** Sidebar group placeholders for the loading skeleton */
|
|
1055
|
-
this.groupSkeletonPlaceholders = computed(() => [
|
|
1056
|
-
'w-4/5',
|
|
1057
|
-
'w-3/5',
|
|
1058
|
-
'w-4/5',
|
|
1059
|
-
'w-3/5',
|
|
1060
|
-
'w-4/5',
|
|
1061
|
-
'w-3/5',
|
|
1062
|
-
'w-4/5',
|
|
1063
|
-
], ...(ngDevMode ? [{ debugName: "groupSkeletonPlaceholders" }] : /* istanbul ignore next */ []));
|
|
1064
|
-
/** Permission group placeholders (parent + child rows) for the loading skeleton */
|
|
1065
|
-
this.permissionGroupSkeletonPlaceholders = computed(() => [
|
|
1066
|
-
{ childCount: 4 },
|
|
1067
|
-
{ childCount: 4 },
|
|
1068
|
-
{ childCount: 2 },
|
|
1069
|
-
{ childCount: 4 },
|
|
1070
|
-
], ...(ngDevMode ? [{ debugName: "permissionGroupSkeletonPlaceholders" }] : /* istanbul ignore next */ []));
|
|
1071
|
-
/** Child row placeholders for a permission group skeleton */
|
|
1072
|
-
this.childSkeletonPlaceholders = (count) => Array.from({ length: count }, (_, index) => index);
|
|
1073
|
-
/** Whether the user is actively searching permissions */
|
|
1074
|
-
this.isSearchActive = computed(() => !isEmpty(trim(this.searchQuery())), ...(ngDevMode ? [{ debugName: "isSearchActive" }] : /* istanbul ignore next */ []));
|
|
1075
|
-
/** Grouped search results across all permission groups */
|
|
1076
|
-
this.searchResultsGrouped = computed(() => {
|
|
1077
|
-
const normalizedQuery = trim(this.searchQuery()).toLowerCase();
|
|
1078
|
-
if (!normalizedQuery) {
|
|
1079
|
-
return [];
|
|
1080
|
-
}
|
|
1081
|
-
return this.groupsWithBadge()
|
|
1082
|
-
.map((group) => ({
|
|
1083
|
-
group,
|
|
1084
|
-
items: this.mapPermissions(this.getFilteredGroupPermissions(group, normalizedQuery)),
|
|
1085
|
-
}))
|
|
1086
|
-
.filter((result) => !isEmpty(result.items));
|
|
1087
|
-
}, ...(ngDevMode ? [{ debugName: "searchResultsGrouped" }] : /* istanbul ignore next */ []));
|
|
1088
|
-
/** Whether the active search returned any permission groups */
|
|
1089
|
-
this.hasSearchResults = computed(() => !isEmpty(this.searchResultsGrouped()), ...(ngDevMode ? [{ debugName: "hasSearchResults" }] : /* istanbul ignore next */ []));
|
|
1090
|
-
setupPopupFooterKeyboardShortcuts(() => {
|
|
1091
|
-
if (!this.isShowActions()) {
|
|
1092
|
-
return [];
|
|
1093
|
-
}
|
|
1094
|
-
return [
|
|
1095
|
-
{
|
|
1096
|
-
title: '@general:actions.cancel.title',
|
|
1097
|
-
command: 'cancel',
|
|
1098
|
-
handler: () => this.handleCancelClick(),
|
|
1099
|
-
},
|
|
1100
|
-
{
|
|
1101
|
-
title: '@general:actions.select.title',
|
|
1102
|
-
command: 'select',
|
|
1103
|
-
handler: () => this.handleSelectClick(),
|
|
1104
|
-
},
|
|
1105
|
-
];
|
|
1106
|
-
}, { id: 'permissions-editor-popup', when: () => this.isShowActions() });
|
|
1107
|
-
}
|
|
1108
|
-
// #endregion
|
|
1109
|
-
// #region ---------------- Computed Properties ----------------
|
|
1110
|
-
/**
|
|
1111
|
-
* Reads a signal input or a plain value assigned by AXPopupService data binding.
|
|
1112
|
-
*/
|
|
1113
|
-
readBoundValue(value, fallback) {
|
|
1114
|
-
if (typeof value === 'function') {
|
|
1115
|
-
return value();
|
|
1116
|
-
}
|
|
1117
|
-
return (value ?? fallback);
|
|
1118
|
-
}
|
|
1119
|
-
/** Whether popup footer actions are visible (supports popup data and input binding). */
|
|
1120
|
-
isShowActions() {
|
|
1121
|
-
return this.readBoundValue(this.showActions, false);
|
|
1122
|
-
}
|
|
1123
|
-
/** Whether the editor is read-only (supports popup data and input binding). */
|
|
1124
|
-
isReadOnlyMode() {
|
|
1125
|
-
return this.readBoundValue(this.readOnly, false);
|
|
1126
|
-
}
|
|
1127
|
-
/** Initial permission selection (supports popup data and input binding). */
|
|
1128
|
-
getInitialPermissions() {
|
|
1129
|
-
return this.readBoundValue(this.initialPermissions, []);
|
|
1130
|
-
}
|
|
1131
|
-
/**
|
|
1132
|
-
* Recursively maps permissions to include selection state and children.
|
|
1133
|
-
*/
|
|
1134
|
-
mapPermissions(items) {
|
|
1135
|
-
return (items || []).map((item) => {
|
|
1136
|
-
const children = item.children;
|
|
1137
|
-
return {
|
|
1138
|
-
...item,
|
|
1139
|
-
isSelected: this.isSelected(item.name),
|
|
1140
|
-
children: children?.length ? this.mapPermissions(children) : null,
|
|
1141
|
-
};
|
|
1142
|
-
});
|
|
1143
|
-
}
|
|
1144
|
-
// #endregion
|
|
1145
|
-
// #region ---------------- Utility Methods ----------------
|
|
1146
|
-
/**
|
|
1147
|
-
* Extracts module name from a permission group name.
|
|
1148
|
-
* Handles multiple formats:
|
|
1149
|
-
* - Permission key format: 'SecurityManagement:Permission:SecurityManagement.Management' -> 'SecurityManagement'
|
|
1150
|
-
* - PascalCase: 'TenantManagement' -> 'TenantManagement'
|
|
1151
|
-
* - kebab-case: 'tenant-management' -> 'TenantManagement' (converts to PascalCase)
|
|
1152
|
-
*/
|
|
1153
|
-
extractModuleNameFromGroupName(groupName) {
|
|
1154
|
-
// If it contains ':', extract the first part (permission key format)
|
|
1155
|
-
if (groupName.includes(':')) {
|
|
1156
|
-
const parts = groupName.split(':');
|
|
1157
|
-
return parts[0];
|
|
1158
|
-
}
|
|
1159
|
-
// If it's kebab-case, convert to PascalCase
|
|
1160
|
-
if (groupName.includes('-')) {
|
|
1161
|
-
return groupName
|
|
1162
|
-
.split('-')
|
|
1163
|
-
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
|
1164
|
-
.join('');
|
|
1165
|
-
}
|
|
1166
|
-
// Otherwise, assume it's already PascalCase
|
|
1167
|
-
return groupName;
|
|
1168
|
-
}
|
|
1169
|
-
/**
|
|
1170
|
-
* Filters permission groups to only include those from enabled modules.
|
|
1171
|
-
*/
|
|
1172
|
-
filterGroupsByEnabledModules(groups) {
|
|
1173
|
-
return groups.filter((group) => {
|
|
1174
|
-
const moduleName = this.extractModuleNameFromGroupName(group.name);
|
|
1175
|
-
return this.sessionService.isModuleEnabled(moduleName);
|
|
1176
|
-
});
|
|
1177
|
-
}
|
|
1178
|
-
/**
|
|
1179
|
-
* Recursively collects all permission names (parents and children) from an array.
|
|
1180
|
-
*/
|
|
1181
|
-
collectAllPermissionNames(permissions) {
|
|
1182
|
-
let names = [];
|
|
1183
|
-
for (const item of permissions) {
|
|
1184
|
-
names.push(item.name);
|
|
1185
|
-
if (item.children?.length) {
|
|
1186
|
-
names = names.concat(this.collectAllPermissionNames(item.children));
|
|
1187
|
-
}
|
|
1188
|
-
}
|
|
1189
|
-
return names;
|
|
1190
|
-
}
|
|
1191
|
-
/**
|
|
1192
|
-
* Returns the badge count for a group (all selected permissions, including parents).
|
|
1193
|
-
*/
|
|
1194
|
-
getBadge(group) {
|
|
1195
|
-
const allNames = this.collectAllPermissionNames(group.permissions || []);
|
|
1196
|
-
const selectedPermissions = this.selectedPermissions();
|
|
1197
|
-
const count = allNames.filter((name) => selectedPermissions.includes(name)).length;
|
|
1198
|
-
return count.toString();
|
|
1199
|
-
}
|
|
1200
|
-
/**
|
|
1201
|
-
* Checks if a permission is selected.
|
|
1202
|
-
*/
|
|
1203
|
-
isSelected(name) {
|
|
1204
|
-
return this.selectedPermissions().includes(name);
|
|
1205
|
-
}
|
|
1206
|
-
/**
|
|
1207
|
-
* Checks if a permission is selected.
|
|
1208
|
-
*/
|
|
1209
|
-
badgeToNumber(value) {
|
|
1210
|
-
return Number(value);
|
|
1211
|
-
}
|
|
1212
|
-
/**
|
|
1213
|
-
* Returns true when searchable text contains the query.
|
|
1214
|
-
*/
|
|
1215
|
-
matchesSearchText(text, normalizedQuery) {
|
|
1216
|
-
if (!text) {
|
|
1217
|
-
return false;
|
|
1218
|
-
}
|
|
1219
|
-
const translated = this.translationService.translateSync(text) || text;
|
|
1220
|
-
return (translated.toLowerCase().includes(normalizedQuery) ||
|
|
1221
|
-
text.toLowerCase().includes(normalizedQuery));
|
|
1222
|
-
}
|
|
1223
|
-
/**
|
|
1224
|
-
* Returns true when a permission title or description matches the query.
|
|
1225
|
-
*/
|
|
1226
|
-
matchesPermission(permission, normalizedQuery) {
|
|
1227
|
-
return (this.matchesSearchText(permission.title, normalizedQuery) ||
|
|
1228
|
-
this.matchesSearchText(permission.description, normalizedQuery));
|
|
1229
|
-
}
|
|
1230
|
-
/**
|
|
1231
|
-
* Returns true when a permission group title or description matches the query.
|
|
1232
|
-
*/
|
|
1233
|
-
matchesGroup(group, normalizedQuery) {
|
|
1234
|
-
return (this.matchesSearchText(group.title, normalizedQuery) ||
|
|
1235
|
-
this.matchesSearchText(group.description, normalizedQuery));
|
|
1236
|
-
}
|
|
1237
|
-
/**
|
|
1238
|
-
* Filters permissions while keeping ancestor nodes for matching descendants.
|
|
1239
|
-
*/
|
|
1240
|
-
filterPermissions(items, normalizedQuery) {
|
|
1241
|
-
const result = [];
|
|
1242
|
-
for (const item of items) {
|
|
1243
|
-
const filteredChildren = this.filterPermissions(item.children ?? [], normalizedQuery);
|
|
1244
|
-
const selfMatches = this.matchesPermission(item, normalizedQuery);
|
|
1245
|
-
if (selfMatches || !isEmpty(filteredChildren)) {
|
|
1246
|
-
result.push({
|
|
1247
|
-
...item,
|
|
1248
|
-
children: filteredChildren,
|
|
1249
|
-
});
|
|
1250
|
-
}
|
|
1251
|
-
}
|
|
1252
|
-
return result;
|
|
1253
|
-
}
|
|
1254
|
-
/**
|
|
1255
|
-
* Returns filtered permissions for a group, or all permissions when the group itself matches.
|
|
1256
|
-
*/
|
|
1257
|
-
getFilteredGroupPermissions(group, normalizedQuery) {
|
|
1258
|
-
const permissions = group.permissions ?? [];
|
|
1259
|
-
if (this.matchesGroup(group, normalizedQuery)) {
|
|
1260
|
-
return permissions;
|
|
1261
|
-
}
|
|
1262
|
-
return this.filterPermissions(permissions, normalizedQuery);
|
|
1263
|
-
}
|
|
1264
|
-
/**
|
|
1265
|
-
* Recursively collects all descendant permission names for a single item (including itself).
|
|
1266
|
-
*/
|
|
1267
|
-
collectPermissionNames(item) {
|
|
1268
|
-
let names = [item.name];
|
|
1269
|
-
if (item.children) {
|
|
1270
|
-
for (const child of item.children) {
|
|
1271
|
-
names = names.concat(this.collectPermissionNames(child));
|
|
1272
|
-
}
|
|
1273
|
-
}
|
|
1274
|
-
return names;
|
|
1275
|
-
}
|
|
1276
|
-
/**
|
|
1277
|
-
* Gets the current selected permissions.
|
|
1278
|
-
*/
|
|
1279
|
-
getSelectedPermissions() {
|
|
1280
|
-
return this.selectedPermissions();
|
|
1281
|
-
}
|
|
1282
|
-
/**
|
|
1283
|
-
* Sets the selected permissions.
|
|
1284
|
-
*/
|
|
1285
|
-
setSelectedPermissions(permissions) {
|
|
1286
|
-
this.selectedPermissions.set(permissions || []);
|
|
1287
|
-
this.permissionsChanged.emit(this.selectedPermissions());
|
|
1288
|
-
}
|
|
1289
|
-
/**
|
|
1290
|
-
* Applies/selects the current permissions.
|
|
1291
|
-
*/
|
|
1292
|
-
selectPermissions() {
|
|
1293
|
-
this.permissionsSelected.emit(this.selectedPermissions());
|
|
1294
|
-
}
|
|
1295
|
-
// #endregion
|
|
1296
|
-
// #region ---------------- Lifecycle Methods ----------------
|
|
1297
|
-
/**
|
|
1298
|
-
* Initializes the component and loads permission groups.
|
|
1299
|
-
* Filters groups to only show permissions from modules enabled for the current tenant.
|
|
1300
|
-
*/
|
|
1301
|
-
async ngOnInit() {
|
|
1302
|
-
super.ngOnInit();
|
|
1303
|
-
try {
|
|
1304
|
-
const allGroups = await this.permissionService.getGroups();
|
|
1305
|
-
const filteredGroups = this.filterGroupsByEnabledModules(allGroups);
|
|
1306
|
-
this.groups.set(filteredGroups);
|
|
1307
|
-
// Select first group if available
|
|
1308
|
-
if (filteredGroups.length > 0) {
|
|
1309
|
-
this.selectedGroup.set(filteredGroups[0]);
|
|
1310
|
-
}
|
|
1311
|
-
this.selectedPermissions.set(this.getInitialPermissions());
|
|
1312
|
-
}
|
|
1313
|
-
finally {
|
|
1314
|
-
this.isEditorLoading.set(false);
|
|
1315
|
-
}
|
|
1316
|
-
}
|
|
1317
|
-
// #endregion
|
|
1318
|
-
// #region ---------------- UI Handlers ----------------
|
|
1319
|
-
/**
|
|
1320
|
-
* Handles group tab/menu item click.
|
|
1321
|
-
*/
|
|
1322
|
-
handleMenuItemClick(item) {
|
|
1323
|
-
this.selectedGroup.set(item);
|
|
1324
|
-
}
|
|
1325
|
-
/**
|
|
1326
|
-
* Updates the search query when the search box value changes.
|
|
1327
|
-
*/
|
|
1328
|
-
handleSearchChanged(event) {
|
|
1329
|
-
this.searchQuery.set(event.value ?? '');
|
|
1330
|
-
}
|
|
1331
|
-
/**
|
|
1332
|
-
* Handles permission checkbox toggle (checks/unchecks all children recursively).
|
|
1333
|
-
*/
|
|
1334
|
-
onPermissionToggle(item, e) {
|
|
1335
|
-
// Don't allow changes in read-only mode
|
|
1336
|
-
if (this.isReadOnlyMode()) {
|
|
1337
|
-
return;
|
|
1338
|
-
}
|
|
1339
|
-
const names = this.collectPermissionNames(item);
|
|
1340
|
-
if (e.value) {
|
|
1341
|
-
// Add all names (parent + children), avoiding duplicates
|
|
1342
|
-
const set = new Set([...this.selectedPermissions(), ...names]);
|
|
1343
|
-
this.selectedPermissions.set(Array.from(set));
|
|
1344
|
-
}
|
|
1345
|
-
else {
|
|
1346
|
-
// Remove all names (parent + children)
|
|
1347
|
-
this.selectedPermissions.set(this.selectedPermissions().filter((name) => !names.includes(name)));
|
|
1348
|
-
}
|
|
1349
|
-
this.permissionsChanged.emit(this.selectedPermissions());
|
|
1350
|
-
}
|
|
1351
|
-
/**
|
|
1352
|
-
* Closes the popup without applying permission changes.
|
|
1353
|
-
*/
|
|
1354
|
-
handleCancelClick() {
|
|
1355
|
-
this.close();
|
|
1356
|
-
}
|
|
1357
|
-
/**
|
|
1358
|
-
* Applies the current selection and closes the popup.
|
|
1359
|
-
*/
|
|
1360
|
-
handleSelectClick() {
|
|
1361
|
-
const permissions = this.getSelectedPermissions();
|
|
1362
|
-
this.permissionsSelected.emit(permissions);
|
|
1363
|
-
this.close(permissions);
|
|
1364
|
-
}
|
|
1365
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPPermissionsEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1366
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: AXPPermissionsEditorComponent, isStandalone: true, selector: "axm-permissions-editor", inputs: { initialPermissions: { classPropertyName: "initialPermissions", publicName: "initialPermissions", isSignal: true, isRequired: false, transformFunction: null }, readOnly: { classPropertyName: "readOnly", publicName: "readOnly", isSignal: true, isRequired: false, transformFunction: null }, showActions: { classPropertyName: "showActions", publicName: "showActions", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { permissionsChanged: "permissionsChanged", permissionsSelected: "permissionsSelected" }, host: { properties: { "attr.aria-busy": "isEditorLoading() ? \"true\" : null" }, classAttribute: "axm-permissions-editor flex h-full max-h-[75vh] min-h-0 flex-col overflow-hidden" }, usesInheritance: true, ngImport: i0, template: "@if (isEditorLoading()) {\n <div class=\"__body flex min-h-0 flex-1 overflow-hidden\">\n <div class=\"__groups p-4 w-2/5\" aria-hidden=\"true\">\n <div class=\"flex flex-col gap-3 h-full\">\n @for (widthClass of groupSkeletonPlaceholders(); track $index) {\n <div class=\"flex items-center py-2\">\n <div class=\"h-5 rounded-md overflow-hidden\" [class]=\"widthClass\">\n <ax-skeleton\n [animated]=\"true\"\n style=\"display: block; width: 100%; height: 100%\"\n ></ax-skeleton>\n </div>\n </div>\n }\n </div>\n </div>\n\n <div\n class=\"__permissions flex w-3/5 flex-col overflow-hidden border-s\"\n aria-hidden=\"true\"\n >\n <div class=\"__header p-4 border-b border-lightest\">\n <div class=\"h-6 w-48 rounded-md overflow-hidden\">\n <ax-skeleton\n [animated]=\"true\"\n style=\"display: block; width: 100%; height: 100%\"\n ></ax-skeleton>\n </div>\n </div>\n\n <div class=\"__content flex flex-col gap-1 p-4\">\n @for (group of permissionGroupSkeletonPlaceholders(); track $index) {\n <div class=\"__skeleton-permission-group\">\n <div class=\"flex items-center gap-2 py-1\">\n <div class=\"h-4 w-4 shrink-0 rounded-sm overflow-hidden\">\n <ax-skeleton\n [animated]=\"true\"\n style=\"display: block; width: 100%; height: 100%\"\n ></ax-skeleton>\n </div>\n <div class=\"h-4 w-40 max-w-full rounded-md overflow-hidden\">\n <ax-skeleton\n [animated]=\"true\"\n style=\"display: block; width: 100%; height: 100%\"\n ></ax-skeleton>\n </div>\n <div class=\"h-3 w-3 shrink-0 rounded-full overflow-hidden\">\n <ax-skeleton\n [animated]=\"true\"\n style=\"display: block; width: 100%; height: 100%\"\n ></ax-skeleton>\n </div>\n </div>\n\n <div class=\"__children p-2 mt-1 ms-1\">\n @for (\n childIndex of childSkeletonPlaceholders(group.childCount);\n track childIndex\n ) {\n <div class=\"flex items-center gap-2 py-1 ps-2\">\n <div class=\"h-4 w-4 shrink-0 rounded-sm overflow-hidden\">\n <ax-skeleton\n [animated]=\"true\"\n style=\"display: block; width: 100%; height: 100%\"\n ></ax-skeleton>\n </div>\n <div class=\"h-4 w-32 max-w-full rounded-md overflow-hidden\">\n <ax-skeleton\n [animated]=\"true\"\n style=\"display: block; width: 100%; height: 100%\"\n ></ax-skeleton>\n </div>\n <div class=\"h-3 w-3 shrink-0 rounded-full overflow-hidden\">\n <ax-skeleton\n [animated]=\"true\"\n style=\"display: block; width: 100%; height: 100%\"\n ></ax-skeleton>\n </div>\n </div>\n }\n </div>\n </div>\n }\n </div>\n </div>\n </div>\n} @else {\n <div class=\"__search px-4 pt-4 pb-3 border-b border-border-lightest\">\n <ax-search-box\n [axAutoFocus]\n [ngModel]=\"searchQuery()\"\n (onValueChanged)=\"handleSearchChanged($event)\"\n [placeholder]=\"\n '@security-management:permissions-feature.components.permissions-editor.search.placeholder'\n | translate\n | async\n \"\n >\n <ax-clear-button></ax-clear-button>\n </ax-search-box>\n </div>\n\n <div class=\"__body flex min-h-0 flex-1 overflow-hidden\">\n @if (isSearchActive()) {\n <div\n class=\"__permissions __permissions--search flex min-h-0 flex-1 flex-col overflow-hidden\"\n >\n @if (hasSearchResults()) {\n <div class=\"__search-results flex flex-col\">\n @for (result of searchResultsGrouped(); track result.group.name) {\n <section class=\"__search-group\">\n <div\n class=\"__header shrink-0 p-4 border-b border-border-lightest\"\n >\n <span class=\"h6\">{{\n result.group.title | translate | async\n }}</span>\n </div>\n\n <div class=\"__content flex p-4\">\n <ul class=\"__list\">\n <ng-container\n *ngTemplateOutlet=\"\n recursiveList;\n context: { $implicit: result.items }\n \"\n ></ng-container>\n </ul>\n </div>\n </section>\n }\n </div>\n } @else {\n <div class=\"__empty flex flex-1 items-center justify-center p-8\">\n <axp-state-message\n icon=\"fa-light fa-search\"\n [title]=\"\n '@security-management:permissions-feature.components.permissions-editor.empty-states.no-results.title'\n \"\n [description]=\"\n '@security-management:permissions-feature.components.permissions-editor.empty-states.no-results.description'\n \"\n >\n </axp-state-message>\n </div>\n }\n </div>\n } @else {\n <div class=\"__groups w-2/5 p-4\">\n <ax-tabs location=\"start\" look=\"with-line-color\" class=\"h-full\">\n @for (group of groupsWithBadge(); track $index) {\n <ax-tab-item\n [active]=\"group.name === selectedGroup()?.name\"\n [text]=\"(group.title | translate | async)!\"\n (onClick)=\"handleMenuItemClick(group)\"\n >\n @if (badgeToNumber(group.badge) > 0) {\n <ax-suffix>\n <ax-badge color=\"primary\" [text]=\"group.badge\"></ax-badge>\n </ax-suffix>\n }\n </ax-tab-item>\n }\n </ax-tabs>\n </div>\n\n <div class=\"__permissions flex w-3/5 flex-col overflow-hidden border-s\">\n <div class=\"__header shrink-0 p-4 border-b border-border-lightest\">\n <span>{{ selectedGroup()?.title! | translate | async }}</span>\n </div>\n\n <div class=\"__content flex p-4\">\n <ul class=\"__list\">\n <ng-container\n *ngTemplateOutlet=\"recursiveList; context: { $implicit: items() }\"\n ></ng-container>\n </ul>\n </div>\n </div>\n }\n </div>\n}\n\n@if (isShowActions()) {\n <ax-footer>\n <ax-suffix>\n <ax-button\n look=\"solid\"\n [text]=\"'@general:actions.cancel.title' | translate | async\"\n (onClick)=\"handleCancelClick()\"\n ></ax-button>\n <ax-button\n look=\"solid\"\n color=\"primary\"\n [text]=\"'@general:actions.select.title' | translate | async\"\n (onClick)=\"handleSelectClick()\"\n >\n <ax-prefix>\n <i class=\"fa-solid fa-check\"></i>\n </ax-prefix>\n </ax-button>\n </ax-suffix>\n </ax-footer>\n}\n\n<ng-template #recursiveList let-items>\n @for (item of items; track $index) {\n <li class=\"__item py-1 ps-2\">\n <ax-check-box\n [ngModel]=\"item.isSelected\"\n [readonly]=\"isReadOnlyMode()\"\n (onValueChanged)=\"onPermissionToggle(item, $event)\"\n >\n <ax-label>\n {{ item.title | translate | async }}\n @if (item.description) {\n <i\n class=\"ps-2 text-xs fa-light fa-circle-info\"\n [title]=\"(item.description | translate | async) ?? ''\"\n ></i>\n }\n </ax-label>\n </ax-check-box>\n\n @if (item.children && item.children.length > 0) {\n <ul class=\"__children p-2 mt-1 ms-1\">\n <ng-container\n *ngTemplateOutlet=\"\n recursiveList;\n context: { $implicit: item.children }\n \"\n ></ng-container>\n </ul>\n }\n </li>\n }\n</ng-template>\n", styles: ["@layer properties;.axm-permissions-editor{display:flex;height:100%;max-height:75vh;min-height:0;flex-direction:column;overflow:hidden}.axm-permissions-editor .__search{flex-shrink:0}.axm-permissions-editor .__body{display:flex;min-height:0;flex:1;overflow:hidden}.axm-permissions-editor .__groups{min-height:0;overflow-y:auto}.axm-permissions-editor .__groups .__tabs{height:100%}.axm-permissions-editor .__permissions{display:flex;min-height:0;flex-direction:column;overflow:hidden}.axm-permissions-editor .__permissions .__header{flex-shrink:0}.axm-permissions-editor .__permissions .__header .__title{font-size:1rem;font-weight:500;line-height:1rem}.axm-permissions-editor .__permissions .__content{min-height:0;flex:1;overflow-y:auto}.axm-permissions-editor .__permissions .__content .__list{width:100%}.axm-permissions-editor .__permissions .__content .__list .__item .__children{margin-left:calc(var(--spacing, .25rem) * 2);border-left-style:var(--tw-border-style);border-left-width:1px;--tw-border-style: dashed;border-style:dashed;border-color:rgba(var(--ax-sys-color-border-lighter-surface))}.axm-permissions-editor .__permissions .__permissions--search{width:100%}.axm-permissions-editor .__permissions .__permissions--search .__search-results{min-height:0;width:100%;flex:1;overflow-y:auto}.axm-permissions-editor .__permissions .__permissions--search .__search-group:not(:last-child){border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:rgba(var(--ax-sys-color-lightest-surface))}@property --tw-border-style{syntax: \"*\"; inherits: false; initial-value: solid;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-style: solid}}}\n/*! tailwindcss v4.3.2 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "ngmodule", type:
|
|
1367
|
-
// Angular
|
|
1368
|
-
CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: AXCommonModule }, { kind: "directive", type: i3.AXAutoFocusDirective, selector: "[axAutoFocus]", inputs: ["axAutoFocus", "axAutoFocusTime"] }, { kind: "ngmodule", type:
|
|
1369
|
-
// ACoreX
|
|
1370
|
-
AXFormModule }, { kind: "ngmodule", type: AXTextBoxModule }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i4.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "ngmodule", type: AXLabelModule }, { kind: "component", type: i5.AXLabelComponent, selector: "ax-label", inputs: ["required", "for"], outputs: ["requiredChange"] }, { kind: "ngmodule", type: AXCheckBoxModule }, { kind: "component", type: i6.AXCheckBoxComponent, selector: "ax-check-box", inputs: ["disabled", "tabIndex", "readonly", "color", "value", "name", "id", "isLoading", "indeterminate"], outputs: ["onBlur", "onFocus", "valueChange", "onValueChanged"] }, { kind: "ngmodule", type: AXTabsModule }, { kind: "component", type: i7.AXTabsComponent, selector: "ax-tabs", inputs: ["look", "location", "fitParent", "minWidth", "content"], outputs: ["onActiveTabChanged"] }, { kind: "component", type: i7.AXTabItemComponent, selector: "ax-tab-item", inputs: ["disabled", "text", "key", "headerTemplate", "active"], outputs: ["disabledChange", "onClick", "onBlur", "onFocus", "activeChange"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i8.AXDecoratorClearButtonComponent, selector: "ax-clear-button", inputs: ["icon"] }, { kind: "component", type: i8.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "ngmodule", type: AXBadgeModule }, { kind: "component", type: i9.AXBadgeComponent, selector: "ax-badge", inputs: ["color", "look", "text"] }, { kind: "ngmodule", type: AXTooltipModule }, { kind: "ngmodule", type: AXSkeletonModule }, { kind: "component", type: i10.AXSkeletonComponent, selector: "ax-skeleton", inputs: ["animated"] }, { kind: "ngmodule", type: AXSearchBoxModule }, { kind: "component", type: i11.AXSearchBoxComponent, selector: "ax-search-box", inputs: ["disabled", "readonly", "tabIndex", "placeholder", "value", "state", "name", "id", "look", "class", "delayTime", "type", "autoSearch"], outputs: ["valueChange", "stateChange", "onValueChanged", "onBlur", "onFocus", "readonlyChange", "disabledChange", "onKeyDown", "onKeyUp", "onKeyPress"] }, { kind: "component", type: AXPStateMessageComponent, selector: "axp-state-message", inputs: ["mode", "icon", "title", "description", "titleParams", "descriptionParams", "look"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "pipe", type: i12.AXTranslatorPipe, name: "translate" }], encapsulation: i0.ViewEncapsulation.None }); }
|
|
1371
|
-
}
|
|
1372
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPPermissionsEditorComponent, decorators: [{
|
|
1373
|
-
type: Component,
|
|
1374
|
-
args: [{ selector: 'axm-permissions-editor', imports: [
|
|
1375
|
-
// Angular
|
|
1376
|
-
CommonModule,
|
|
1377
|
-
FormsModule,
|
|
1378
|
-
AXCommonModule,
|
|
1379
|
-
// ACoreX
|
|
1380
|
-
AXFormModule,
|
|
1381
|
-
AXTextBoxModule,
|
|
1382
|
-
AXButtonModule,
|
|
1383
|
-
AXLabelModule,
|
|
1384
|
-
AXCheckBoxModule,
|
|
1385
|
-
AXTabsModule,
|
|
1386
|
-
AXDecoratorModule,
|
|
1387
|
-
AXTranslationModule,
|
|
1388
|
-
AXBadgeModule,
|
|
1389
|
-
AXCheckBoxModule,
|
|
1390
|
-
AXTooltipModule,
|
|
1391
|
-
AXSkeletonModule,
|
|
1392
|
-
AXSearchBoxModule,
|
|
1393
|
-
AXPStateMessageComponent,
|
|
1394
|
-
], encapsulation: ViewEncapsulation.None, host: {
|
|
1395
|
-
class: 'axm-permissions-editor flex h-full max-h-[75vh] min-h-0 flex-col overflow-hidden',
|
|
1396
|
-
'[attr.aria-busy]': 'isEditorLoading() ? "true" : null',
|
|
1397
|
-
}, template: "@if (isEditorLoading()) {\n <div class=\"__body flex min-h-0 flex-1 overflow-hidden\">\n <div class=\"__groups p-4 w-2/5\" aria-hidden=\"true\">\n <div class=\"flex flex-col gap-3 h-full\">\n @for (widthClass of groupSkeletonPlaceholders(); track $index) {\n <div class=\"flex items-center py-2\">\n <div class=\"h-5 rounded-md overflow-hidden\" [class]=\"widthClass\">\n <ax-skeleton\n [animated]=\"true\"\n style=\"display: block; width: 100%; height: 100%\"\n ></ax-skeleton>\n </div>\n </div>\n }\n </div>\n </div>\n\n <div\n class=\"__permissions flex w-3/5 flex-col overflow-hidden border-s\"\n aria-hidden=\"true\"\n >\n <div class=\"__header p-4 border-b border-lightest\">\n <div class=\"h-6 w-48 rounded-md overflow-hidden\">\n <ax-skeleton\n [animated]=\"true\"\n style=\"display: block; width: 100%; height: 100%\"\n ></ax-skeleton>\n </div>\n </div>\n\n <div class=\"__content flex flex-col gap-1 p-4\">\n @for (group of permissionGroupSkeletonPlaceholders(); track $index) {\n <div class=\"__skeleton-permission-group\">\n <div class=\"flex items-center gap-2 py-1\">\n <div class=\"h-4 w-4 shrink-0 rounded-sm overflow-hidden\">\n <ax-skeleton\n [animated]=\"true\"\n style=\"display: block; width: 100%; height: 100%\"\n ></ax-skeleton>\n </div>\n <div class=\"h-4 w-40 max-w-full rounded-md overflow-hidden\">\n <ax-skeleton\n [animated]=\"true\"\n style=\"display: block; width: 100%; height: 100%\"\n ></ax-skeleton>\n </div>\n <div class=\"h-3 w-3 shrink-0 rounded-full overflow-hidden\">\n <ax-skeleton\n [animated]=\"true\"\n style=\"display: block; width: 100%; height: 100%\"\n ></ax-skeleton>\n </div>\n </div>\n\n <div class=\"__children p-2 mt-1 ms-1\">\n @for (\n childIndex of childSkeletonPlaceholders(group.childCount);\n track childIndex\n ) {\n <div class=\"flex items-center gap-2 py-1 ps-2\">\n <div class=\"h-4 w-4 shrink-0 rounded-sm overflow-hidden\">\n <ax-skeleton\n [animated]=\"true\"\n style=\"display: block; width: 100%; height: 100%\"\n ></ax-skeleton>\n </div>\n <div class=\"h-4 w-32 max-w-full rounded-md overflow-hidden\">\n <ax-skeleton\n [animated]=\"true\"\n style=\"display: block; width: 100%; height: 100%\"\n ></ax-skeleton>\n </div>\n <div class=\"h-3 w-3 shrink-0 rounded-full overflow-hidden\">\n <ax-skeleton\n [animated]=\"true\"\n style=\"display: block; width: 100%; height: 100%\"\n ></ax-skeleton>\n </div>\n </div>\n }\n </div>\n </div>\n }\n </div>\n </div>\n </div>\n} @else {\n <div class=\"__search px-4 pt-4 pb-3 border-b border-border-lightest\">\n <ax-search-box\n [axAutoFocus]\n [ngModel]=\"searchQuery()\"\n (onValueChanged)=\"handleSearchChanged($event)\"\n [placeholder]=\"\n '@security-management:permissions-feature.components.permissions-editor.search.placeholder'\n | translate\n | async\n \"\n >\n <ax-clear-button></ax-clear-button>\n </ax-search-box>\n </div>\n\n <div class=\"__body flex min-h-0 flex-1 overflow-hidden\">\n @if (isSearchActive()) {\n <div\n class=\"__permissions __permissions--search flex min-h-0 flex-1 flex-col overflow-hidden\"\n >\n @if (hasSearchResults()) {\n <div class=\"__search-results flex flex-col\">\n @for (result of searchResultsGrouped(); track result.group.name) {\n <section class=\"__search-group\">\n <div\n class=\"__header shrink-0 p-4 border-b border-border-lightest\"\n >\n <span class=\"h6\">{{\n result.group.title | translate | async\n }}</span>\n </div>\n\n <div class=\"__content flex p-4\">\n <ul class=\"__list\">\n <ng-container\n *ngTemplateOutlet=\"\n recursiveList;\n context: { $implicit: result.items }\n \"\n ></ng-container>\n </ul>\n </div>\n </section>\n }\n </div>\n } @else {\n <div class=\"__empty flex flex-1 items-center justify-center p-8\">\n <axp-state-message\n icon=\"fa-light fa-search\"\n [title]=\"\n '@security-management:permissions-feature.components.permissions-editor.empty-states.no-results.title'\n \"\n [description]=\"\n '@security-management:permissions-feature.components.permissions-editor.empty-states.no-results.description'\n \"\n >\n </axp-state-message>\n </div>\n }\n </div>\n } @else {\n <div class=\"__groups w-2/5 p-4\">\n <ax-tabs location=\"start\" look=\"with-line-color\" class=\"h-full\">\n @for (group of groupsWithBadge(); track $index) {\n <ax-tab-item\n [active]=\"group.name === selectedGroup()?.name\"\n [text]=\"(group.title | translate | async)!\"\n (onClick)=\"handleMenuItemClick(group)\"\n >\n @if (badgeToNumber(group.badge) > 0) {\n <ax-suffix>\n <ax-badge color=\"primary\" [text]=\"group.badge\"></ax-badge>\n </ax-suffix>\n }\n </ax-tab-item>\n }\n </ax-tabs>\n </div>\n\n <div class=\"__permissions flex w-3/5 flex-col overflow-hidden border-s\">\n <div class=\"__header shrink-0 p-4 border-b border-border-lightest\">\n <span>{{ selectedGroup()?.title! | translate | async }}</span>\n </div>\n\n <div class=\"__content flex p-4\">\n <ul class=\"__list\">\n <ng-container\n *ngTemplateOutlet=\"recursiveList; context: { $implicit: items() }\"\n ></ng-container>\n </ul>\n </div>\n </div>\n }\n </div>\n}\n\n@if (isShowActions()) {\n <ax-footer>\n <ax-suffix>\n <ax-button\n look=\"solid\"\n [text]=\"'@general:actions.cancel.title' | translate | async\"\n (onClick)=\"handleCancelClick()\"\n ></ax-button>\n <ax-button\n look=\"solid\"\n color=\"primary\"\n [text]=\"'@general:actions.select.title' | translate | async\"\n (onClick)=\"handleSelectClick()\"\n >\n <ax-prefix>\n <i class=\"fa-solid fa-check\"></i>\n </ax-prefix>\n </ax-button>\n </ax-suffix>\n </ax-footer>\n}\n\n<ng-template #recursiveList let-items>\n @for (item of items; track $index) {\n <li class=\"__item py-1 ps-2\">\n <ax-check-box\n [ngModel]=\"item.isSelected\"\n [readonly]=\"isReadOnlyMode()\"\n (onValueChanged)=\"onPermissionToggle(item, $event)\"\n >\n <ax-label>\n {{ item.title | translate | async }}\n @if (item.description) {\n <i\n class=\"ps-2 text-xs fa-light fa-circle-info\"\n [title]=\"(item.description | translate | async) ?? ''\"\n ></i>\n }\n </ax-label>\n </ax-check-box>\n\n @if (item.children && item.children.length > 0) {\n <ul class=\"__children p-2 mt-1 ms-1\">\n <ng-container\n *ngTemplateOutlet=\"\n recursiveList;\n context: { $implicit: item.children }\n \"\n ></ng-container>\n </ul>\n }\n </li>\n }\n</ng-template>\n", styles: ["@layer properties;.axm-permissions-editor{display:flex;height:100%;max-height:75vh;min-height:0;flex-direction:column;overflow:hidden}.axm-permissions-editor .__search{flex-shrink:0}.axm-permissions-editor .__body{display:flex;min-height:0;flex:1;overflow:hidden}.axm-permissions-editor .__groups{min-height:0;overflow-y:auto}.axm-permissions-editor .__groups .__tabs{height:100%}.axm-permissions-editor .__permissions{display:flex;min-height:0;flex-direction:column;overflow:hidden}.axm-permissions-editor .__permissions .__header{flex-shrink:0}.axm-permissions-editor .__permissions .__header .__title{font-size:1rem;font-weight:500;line-height:1rem}.axm-permissions-editor .__permissions .__content{min-height:0;flex:1;overflow-y:auto}.axm-permissions-editor .__permissions .__content .__list{width:100%}.axm-permissions-editor .__permissions .__content .__list .__item .__children{margin-left:calc(var(--spacing, .25rem) * 2);border-left-style:var(--tw-border-style);border-left-width:1px;--tw-border-style: dashed;border-style:dashed;border-color:rgba(var(--ax-sys-color-border-lighter-surface))}.axm-permissions-editor .__permissions .__permissions--search{width:100%}.axm-permissions-editor .__permissions .__permissions--search .__search-results{min-height:0;width:100%;flex:1;overflow-y:auto}.axm-permissions-editor .__permissions .__permissions--search .__search-group:not(:last-child){border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:rgba(var(--ax-sys-color-lightest-surface))}@property --tw-border-style{syntax: \"*\"; inherits: false; initial-value: solid;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-style: solid}}}\n/*! tailwindcss v4.3.2 | MIT License | https://tailwindcss.com */\n"] }]
|
|
1398
|
-
}], ctorParameters: () => [], propDecorators: { initialPermissions: [{ type: i0.Input, args: [{ isSignal: true, alias: "initialPermissions", required: false }] }], readOnly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readOnly", required: false }] }], showActions: [{ type: i0.Input, args: [{ isSignal: true, alias: "showActions", required: false }] }], permissionsChanged: [{ type: i0.Output, args: ["permissionsChanged"] }], permissionsSelected: [{ type: i0.Output, args: ["permissionsSelected"] }] } });
|
|
1399
|
-
|
|
1400
|
-
var permissionsEditor_component = /*#__PURE__*/Object.freeze({
|
|
1401
|
-
__proto__: null,
|
|
1402
|
-
AXPPermissionsEditorComponent: AXPPermissionsEditorComponent
|
|
1403
|
-
});
|
|
1404
|
-
|
|
1405
|
-
//#endregion
|
|
1406
|
-
//#region ---- Command ----
|
|
1407
|
-
class AXMAssignPermissionsCommand {
|
|
1408
|
-
constructor() {
|
|
1409
|
-
//#region ---- Services & Dependencies ----
|
|
1410
|
-
this.popupService = inject(AXPopupService);
|
|
1411
|
-
this.translationService = inject(AXTranslationService);
|
|
1412
|
-
this.entityRegistry = inject(AXPEntityDefinitionRegistryService);
|
|
1413
|
-
this.toastService = inject(AXToastService);
|
|
1414
|
-
this.settingsService = inject(AXPSettingsService);
|
|
1415
|
-
this.workflowEventService = inject(AXPWorkflowEventService);
|
|
1416
|
-
}
|
|
1417
|
-
//#endregion
|
|
1418
|
-
//#region ---- Execute ----
|
|
1419
|
-
async execute(input) {
|
|
1420
|
-
try {
|
|
1421
|
-
const role = input.__context__?.data;
|
|
1422
|
-
if (!role || !isString(role['id']) || isEmpty(role['id'])) {
|
|
1423
|
-
return {
|
|
1424
|
-
success: false,
|
|
1425
|
-
message: {
|
|
1426
|
-
text: await this.translationService.translateAsync('@general:messages.entity.invalid-data'),
|
|
1427
|
-
},
|
|
1428
|
-
};
|
|
1429
|
-
}
|
|
1430
|
-
const component = await Promise.resolve().then(function () { return permissionsEditor_component; }).then((m) => m.AXPPermissionsEditorComponent);
|
|
1431
|
-
const editorTitle = await this.translationService.translateAsync('@security-management:permissions-feature.terms.permissions-editor');
|
|
1432
|
-
const roleTitle = this.resolveRoleTitle(role['title']);
|
|
1433
|
-
const initialPermissions = isArray(role['permissions']) ? role['permissions'] : [];
|
|
1434
|
-
const popupResult = await this.popupService.open(component, {
|
|
1435
|
-
title: roleTitle ? `${editorTitle} - ${roleTitle}` : editorTitle,
|
|
1436
|
-
size: 'md',
|
|
1437
|
-
data: {
|
|
1438
|
-
initialPermissions,
|
|
1439
|
-
showActions: true,
|
|
1440
|
-
},
|
|
1441
|
-
});
|
|
1442
|
-
const permissions = popupResult?.data;
|
|
1443
|
-
if (!isArray(permissions)) {
|
|
1444
|
-
return { success: true, data: null, metadata: { refresh: false } };
|
|
1445
|
-
}
|
|
1446
|
-
const entityKey = `${RootConfig.module.name}.${RootConfig.entities.roles.name}`;
|
|
1447
|
-
const [moduleName, entityName] = entityKey.split('.');
|
|
1448
|
-
const entityRef = await this.entityRegistry.resolve(moduleName, entityName);
|
|
1449
|
-
const updatePayload = {
|
|
1450
|
-
...role,
|
|
1451
|
-
permissions,
|
|
1452
|
-
};
|
|
1453
|
-
const updated = await (entityRef.commands?.update?.execute)(updatePayload);
|
|
1454
|
-
await this.showSuccessToast();
|
|
1455
|
-
this.workflowEventService.dispatch(AXPEntityModifyEvent({ entity: entityKey, values: updated }));
|
|
1456
|
-
this.workflowEventService.dispatch(AXPRefreshEvent({ entity: entityKey }));
|
|
1457
|
-
return {
|
|
1458
|
-
success: true,
|
|
1459
|
-
data: permissions,
|
|
1460
|
-
};
|
|
1461
|
-
}
|
|
1462
|
-
catch (error) {
|
|
1463
|
-
const message = error instanceof Error ? error.message : 'Failed to assign permissions.';
|
|
1464
|
-
return {
|
|
1465
|
-
success: false,
|
|
1466
|
-
message: { text: message },
|
|
1467
|
-
};
|
|
1468
|
-
}
|
|
1469
|
-
}
|
|
1470
|
-
//#endregion
|
|
1471
|
-
//#region ---- Utility Methods ----
|
|
1472
|
-
resolveRoleTitle(title) {
|
|
1473
|
-
if (!title) {
|
|
1474
|
-
return '';
|
|
1475
|
-
}
|
|
1476
|
-
const locale = this.translationService.getActiveLang?.() ?? 'en-US';
|
|
1477
|
-
if (isString(title)) {
|
|
1478
|
-
return resolveMultiLanguageString(title, locale).trim();
|
|
1479
|
-
}
|
|
1480
|
-
return resolveMultiLanguageString(String(title), locale).trim();
|
|
1481
|
-
}
|
|
1482
|
-
async showSuccessToast() {
|
|
1483
|
-
const enableOperationToasts = await this.settingsService.get(AXPCommonSettings.EnableOperationToasts);
|
|
1484
|
-
if (!enableOperationToasts) {
|
|
1485
|
-
return;
|
|
1486
|
-
}
|
|
1487
|
-
this.toastService.show({
|
|
1488
|
-
color: 'success',
|
|
1489
|
-
title: await this.translationService.translateAsync('@general:workflow.entity-modified-title'),
|
|
1490
|
-
content: await this.translationService.translateAsync('@general:workflow.entity-modified-body'),
|
|
1491
|
-
});
|
|
1492
|
-
}
|
|
1493
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMAssignPermissionsCommand, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1494
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMAssignPermissionsCommand }); }
|
|
1495
|
-
}
|
|
1496
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMAssignPermissionsCommand, decorators: [{
|
|
1497
|
-
type: Injectable
|
|
1498
|
-
}] });
|
|
1499
|
-
|
|
1500
|
-
var assignPermissions_command = /*#__PURE__*/Object.freeze({
|
|
1501
|
-
__proto__: null,
|
|
1502
|
-
AXMAssignPermissionsCommand: AXMAssignPermissionsCommand
|
|
1503
|
-
});
|
|
1504
|
-
|
|
1505
|
-
class AXPPermissionsWidgetViewComponent extends AXPValueWidgetComponent {
|
|
1506
|
-
constructor() {
|
|
1507
|
-
super(...arguments);
|
|
1508
|
-
this.permissionService = inject(AXPPermissionDefinitionService);
|
|
1509
|
-
this.commandService = inject(AXPCommandService);
|
|
1510
|
-
this.groups = signal([], ...(ngDevMode ? [{ debugName: "groups" }] : /* istanbul ignore next */ []));
|
|
1511
|
-
this.loadGroupsEffect = effect(async () => {
|
|
1512
|
-
// Load groups when component initializes
|
|
1513
|
-
if (this.groups().length === 0) {
|
|
1514
|
-
const groupsData = await this.permissionService.getGroups();
|
|
1515
|
-
this.groups.set(groupsData);
|
|
1516
|
-
}
|
|
1517
|
-
}, ...(ngDevMode ? [{ debugName: "loadGroupsEffect" }] : /* istanbul ignore next */ []));
|
|
1518
|
-
this.selectedPermissionsByGroup = computed(() => {
|
|
1519
|
-
const selected = new Set(this.getValue() ?? []);
|
|
1520
|
-
const result = {};
|
|
1521
|
-
for (const group of this.groups()) {
|
|
1522
|
-
const permPaths = [];
|
|
1523
|
-
const findPermissionPaths = (items, path = []) => {
|
|
1524
|
-
for (const item of items) {
|
|
1525
|
-
const currentPath = [...path, item];
|
|
1526
|
-
if (selected.has(item.name)) {
|
|
1527
|
-
const displayPath = currentPath.map(p => p.title).join(' > ');
|
|
1528
|
-
permPaths.push({
|
|
1529
|
-
group,
|
|
1530
|
-
path: currentPath,
|
|
1531
|
-
displayPath
|
|
1532
|
-
});
|
|
1533
|
-
}
|
|
1534
|
-
if (item.children?.length) {
|
|
1535
|
-
findPermissionPaths(item.children, currentPath);
|
|
1536
|
-
}
|
|
1537
|
-
}
|
|
1538
|
-
};
|
|
1539
|
-
findPermissionPaths(group.permissions);
|
|
1540
|
-
result[group.name] = permPaths;
|
|
1541
|
-
}
|
|
1542
|
-
return result;
|
|
1543
|
-
}, ...(ngDevMode ? [{ debugName: "selectedPermissionsByGroup" }] : /* istanbul ignore next */ []));
|
|
1544
|
-
this.isEmpty = computed(() => {
|
|
1545
|
-
return Object.values(this.selectedPermissionsByGroup()).every(arr => !arr.length);
|
|
1546
|
-
}, ...(ngDevMode ? [{ debugName: "isEmpty" }] : /* istanbul ignore next */ []));
|
|
1547
|
-
}
|
|
1548
|
-
async openPermissionsEditor() {
|
|
1549
|
-
await this.commandService.execute('assign-permissions', {
|
|
1550
|
-
__context__: {
|
|
1551
|
-
data: this.contextService.data(),
|
|
1552
|
-
entity: `${RootConfig.module.name}.${RootConfig.entities.roles.name}`,
|
|
1553
|
-
},
|
|
1554
|
-
});
|
|
1555
|
-
}
|
|
1556
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPPermissionsWidgetViewComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
1557
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: AXPPermissionsWidgetViewComponent, isStandalone: true, selector: "axm-permissions-widget-view", usesInheritance: true, ngImport: i0, template: `
|
|
1558
|
-
<!-- <axm-permissions-editor
|
|
1559
|
-
[initialPermissions]="getValue() || []"
|
|
1560
|
-
[readOnly]="true">
|
|
1561
|
-
</axm-permissions-editor> -->
|
|
1562
|
-
<div class="flex flex-col gap-2">
|
|
1563
|
-
@for (group of groups(); track group.name) {
|
|
1564
|
-
@if (selectedPermissionsByGroup()[group.name]?.length) {
|
|
1565
|
-
<div>
|
|
1566
|
-
<div class="text-xs font-semibold mb-2 text-muted">{{ group.title }}</div>
|
|
1567
|
-
<div class="flex gap-1 flex-wrap">
|
|
1568
|
-
@for (permPath of selectedPermissionsByGroup()[group.name]; track permPath.path[permPath.path.length - 1].name) {
|
|
1569
|
-
<ax-badge [text]="permPath.displayPath" color="primary" size="sm"></ax-badge>
|
|
1570
|
-
}
|
|
1571
|
-
</div>
|
|
1572
|
-
</div>
|
|
1573
|
-
}
|
|
1574
|
-
}
|
|
1575
|
-
@if (isEmpty()) {
|
|
1576
|
-
<axp-state-message
|
|
1577
|
-
icon="fa-light fa-shield-check"
|
|
1578
|
-
[title]="'@security-management:permissions-feature.components.permissions-editor.empty-states.no-permissions.title'"
|
|
1579
|
-
[description]="'@security-management:permissions-feature.components.permissions-editor.empty-states.no-permissions.description'">
|
|
1580
|
-
</axp-state-message>
|
|
1581
|
-
}
|
|
1582
|
-
</div>
|
|
1583
|
-
`, isInline: true, dependencies: [{ kind: "ngmodule", type: AXBadgeModule }, { kind: "component", type: i9.AXBadgeComponent, selector: "ax-badge", inputs: ["color", "look", "text"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "component", type: AXPStateMessageComponent, selector: "axp-state-message", inputs: ["mode", "icon", "title", "description", "titleParams", "descriptionParams", "look"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
1584
|
-
}
|
|
1585
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPPermissionsWidgetViewComponent, decorators: [{
|
|
1586
|
-
type: Component,
|
|
1587
|
-
args: [{
|
|
1588
|
-
selector: 'axm-permissions-widget-view',
|
|
1589
|
-
template: `
|
|
1590
|
-
<!-- <axm-permissions-editor
|
|
1591
|
-
[initialPermissions]="getValue() || []"
|
|
1592
|
-
[readOnly]="true">
|
|
1593
|
-
</axm-permissions-editor> -->
|
|
1594
|
-
<div class="flex flex-col gap-2">
|
|
1595
|
-
@for (group of groups(); track group.name) {
|
|
1596
|
-
@if (selectedPermissionsByGroup()[group.name]?.length) {
|
|
1597
|
-
<div>
|
|
1598
|
-
<div class="text-xs font-semibold mb-2 text-muted">{{ group.title }}</div>
|
|
1599
|
-
<div class="flex gap-1 flex-wrap">
|
|
1600
|
-
@for (permPath of selectedPermissionsByGroup()[group.name]; track permPath.path[permPath.path.length - 1].name) {
|
|
1601
|
-
<ax-badge [text]="permPath.displayPath" color="primary" size="sm"></ax-badge>
|
|
1602
|
-
}
|
|
1603
|
-
</div>
|
|
1604
|
-
</div>
|
|
1605
|
-
}
|
|
1606
|
-
}
|
|
1607
|
-
@if (isEmpty()) {
|
|
1608
|
-
<axp-state-message
|
|
1609
|
-
icon="fa-light fa-shield-check"
|
|
1610
|
-
[title]="'@security-management:permissions-feature.components.permissions-editor.empty-states.no-permissions.title'"
|
|
1611
|
-
[description]="'@security-management:permissions-feature.components.permissions-editor.empty-states.no-permissions.description'">
|
|
1612
|
-
</axp-state-message>
|
|
1613
|
-
}
|
|
1614
|
-
</div>
|
|
1615
|
-
`,
|
|
1616
|
-
standalone: true,
|
|
1617
|
-
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
1618
|
-
imports: [AXBadgeModule, AXTranslationModule, AXPStateMessageComponent]
|
|
1619
|
-
}]
|
|
1620
|
-
}] });
|
|
1621
|
-
|
|
1622
|
-
var permissionsWidgetView_component = /*#__PURE__*/Object.freeze({
|
|
1623
|
-
__proto__: null,
|
|
1624
|
-
AXPPermissionsWidgetViewComponent: AXPPermissionsWidgetViewComponent
|
|
1625
|
-
});
|
|
1626
|
-
|
|
1627
|
-
class AXPPermissionsWidgetEditComponent extends AXPValueWidgetComponent {
|
|
1628
|
-
// #region ---------------- UI Handlers ----------------
|
|
1629
|
-
/**
|
|
1630
|
-
* Handles permissions changes from the editor.
|
|
1631
|
-
*/
|
|
1632
|
-
onPermissionsChanged(permissions) {
|
|
1633
|
-
this.setValue(permissions);
|
|
1634
|
-
}
|
|
1635
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPPermissionsWidgetEditComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
1636
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.9", type: AXPPermissionsWidgetEditComponent, isStandalone: true, selector: "axm-permissions-widget-edit", usesInheritance: true, ngImport: i0, template: `
|
|
1637
|
-
<axm-permissions-editor
|
|
1638
|
-
[initialPermissions]="getValue() || []"
|
|
1639
|
-
[readOnly]="false"
|
|
1640
|
-
(permissionsChanged)="onPermissionsChanged($event)">
|
|
1641
|
-
</axm-permissions-editor>
|
|
1642
|
-
`, isInline: true, dependencies: [{ kind: "component", type: AXPPermissionsEditorComponent, selector: "axm-permissions-editor", inputs: ["initialPermissions", "readOnly", "showActions"], outputs: ["permissionsChanged", "permissionsSelected"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
1643
|
-
}
|
|
1644
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPPermissionsWidgetEditComponent, decorators: [{
|
|
1645
|
-
type: Component,
|
|
1646
|
-
args: [{
|
|
1647
|
-
selector: 'axm-permissions-widget-edit',
|
|
1648
|
-
template: `
|
|
1649
|
-
<axm-permissions-editor
|
|
1650
|
-
[initialPermissions]="getValue() || []"
|
|
1651
|
-
[readOnly]="false"
|
|
1652
|
-
(permissionsChanged)="onPermissionsChanged($event)">
|
|
1653
|
-
</axm-permissions-editor>
|
|
1654
|
-
`,
|
|
1655
|
-
standalone: true,
|
|
1656
|
-
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
1657
|
-
imports: [
|
|
1658
|
-
AXPPermissionsEditorComponent
|
|
1659
|
-
]
|
|
1660
|
-
}]
|
|
1661
|
-
}] });
|
|
1662
|
-
|
|
1663
|
-
var permissionsWidgetEdit_component = /*#__PURE__*/Object.freeze({
|
|
1664
|
-
__proto__: null,
|
|
1665
|
-
AXPPermissionsWidgetEditComponent: AXPPermissionsWidgetEditComponent
|
|
1666
|
-
});
|
|
1667
|
-
|
|
1668
|
-
class AXPPermissionsWidgetColumnComponent extends AXPColumnWidgetComponent {
|
|
1669
|
-
constructor() {
|
|
1670
|
-
super(...arguments);
|
|
1671
|
-
this.count = computed(() => castArray(this.rawValue).length, ...(ngDevMode ? [{ debugName: "count" }] : /* istanbul ignore next */ []));
|
|
1672
|
-
this.commandService = inject(AXPCommandService);
|
|
1673
|
-
}
|
|
1674
|
-
async openPermissionsEditor() {
|
|
1675
|
-
await this.commandService.execute('assign-permissions', {
|
|
1676
|
-
__context__: {
|
|
1677
|
-
data: this.rowData,
|
|
1678
|
-
entity: `${RootConfig.module.name}.${RootConfig.entities.roles.name}`,
|
|
1679
|
-
},
|
|
1680
|
-
});
|
|
1681
|
-
}
|
|
1682
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPPermissionsWidgetColumnComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
1683
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.9", type: AXPPermissionsWidgetColumnComponent, isStandalone: true, selector: "axm-permissions-widget-column", inputs: { rawValue: "rawValue", rowData: "rowData" }, usesInheritance: true, ngImport: i0, template: `
|
|
1684
|
-
<span class="cursor-pointer text-primary underline" (click)="openPermissionsEditor()">{{count()}} {{'@security-management:permissions-feature.terms.permissions' | translate | async}}</span>
|
|
1685
|
-
`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "pipe", type: i12.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
1686
|
-
}
|
|
1687
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPPermissionsWidgetColumnComponent, decorators: [{
|
|
1688
|
-
type: Component,
|
|
1689
|
-
args: [{
|
|
1690
|
-
selector: 'axm-permissions-widget-column',
|
|
1691
|
-
template: `
|
|
1692
|
-
<span class="cursor-pointer text-primary underline" (click)="openPermissionsEditor()">{{count()}} {{'@security-management:permissions-feature.terms.permissions' | translate | async}}</span>
|
|
1693
|
-
`,
|
|
1694
|
-
standalone: true,
|
|
1695
|
-
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
1696
|
-
imports: [CommonModule, AXTranslationModule],
|
|
1697
|
-
inputs: ['rawValue', 'rowData']
|
|
1698
|
-
}]
|
|
1699
|
-
}] });
|
|
1700
|
-
|
|
1701
|
-
var permissionsWidgetColumn_component = /*#__PURE__*/Object.freeze({
|
|
1702
|
-
__proto__: null,
|
|
1703
|
-
AXPPermissionsWidgetColumnComponent: AXPPermissionsWidgetColumnComponent
|
|
1704
|
-
});
|
|
1705
|
-
|
|
1706
|
-
// Permission Editor Components
|
|
1707
|
-
|
|
1708
|
-
/**
|
|
1709
|
-
* Generated bundle index. Do not edit.
|
|
1710
|
-
*/
|
|
1711
|
-
|
|
1712
|
-
export { AXMAssignPermissionsCommand, AXMDeviceSessionsService, AXMPermissionsModule, AXMSecurityManagementModule, AXPPermissionsEditorComponent, AXPPermissionsWidget, AXPPermissionsWidgetColumnComponent, AXPPermissionsWidgetEditComponent, AXPPermissionsWidgetViewComponent };
|
|
1
|
+
export { A as AXMAssignPermissionsCommand, a as AXMDeviceSessionsService, b as AXMPermissionsModule, c as AXMRolePermissionGrantService, d as AXMRolePermissionsPageComponent, e as AXMRolePermissionsPageComponentProvider, f as AXMSecurityManagementModule, g as AXPPermissionsEditorComponent, h as AXPPermissionsWidget, i as AXPPermissionsWidgetColumnComponent, j as AXPPermissionsWidgetEditComponent, k as AXPPermissionsWidgetViewComponent } from './acorex-platform-module-security-management-client-acorex-platform-module-security-management-client-jSDkyMMZ.mjs';
|
|
1713
2
|
//# sourceMappingURL=acorex-platform-module-security-management-client.mjs.map
|