@valtimo/access-control-management 13.30.0 → 13.32.0
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/valtimo-access-control-management.mjs +692 -25
- package/fesm2022/valtimo-access-control-management.mjs.map +1 -1
- package/lib/access-control-management.module.d.ts +3 -1
- package/lib/components/access-control-json-editor-tab/access-control-json-editor-tab.component.d.ts +49 -0
- package/lib/components/access-control-overview-tab/access-control-overview-tab.component.d.ts +56 -0
- package/lib/components/editor/access-control-editor.component.d.ts +7 -1
- package/lib/constants/access-control-labels.d.ts +4 -0
- package/lib/constants/index.d.ts +1 -0
- package/lib/models/access-control-editor-tab.model.d.ts +9 -0
- package/lib/models/index.d.ts +3 -0
- package/lib/models/permission-schema.model.d.ts +47 -0
- package/lib/models/permission.model.d.ts +35 -0
- package/lib/pipes/index.d.ts +1 -0
- package/lib/pipes/resource-type-label.pipe.d.ts +10 -0
- package/lib/services/access-control.service.d.ts +3 -2
- package/lib/services/index.d.ts +1 -0
- package/lib/services/permission-schema-metadata.service.d.ts +20 -0
- package/lib/utils/format-condition.d.ts +8 -0
- package/lib/utils/index.d.ts +1 -0
- package/package.json +1 -1
|
@@ -1,22 +1,76 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { Injectable, EventEmitter, Output, Input, ChangeDetectionStrategy, Component, ViewChild, NgModule } from '@angular/core';
|
|
3
|
-
import { BehaviorSubject, tap, switchMap, take, catchError, of,
|
|
2
|
+
import { Injectable, EventEmitter, Output, Input, ChangeDetectionStrategy, Component, ViewChild, Pipe, signal, NgModule } from '@angular/core';
|
|
3
|
+
import { BehaviorSubject, tap, switchMap, take, catchError, of, shareReplay, map, combineLatest, Subject, finalize, delay, Subscription, skip, filter } from 'rxjs';
|
|
4
4
|
import * as i1 from '@valtimo/shared';
|
|
5
5
|
import { ROLE_ADMIN } from '@valtimo/shared';
|
|
6
6
|
import * as i2 from '@angular/common/http';
|
|
7
7
|
import * as i4 from '@angular/common';
|
|
8
8
|
import { CommonModule } from '@angular/common';
|
|
9
|
-
import * as
|
|
9
|
+
import * as i4$1 from '@ngx-translate/core';
|
|
10
10
|
import { TranslateModule } from '@ngx-translate/core';
|
|
11
|
-
import * as i3
|
|
12
|
-
import { CARBON_CONSTANTS, ViewType, CarbonListComponent,
|
|
13
|
-
import * as
|
|
11
|
+
import * as i3 from '@valtimo/components';
|
|
12
|
+
import { CARBON_CONSTANTS, ViewType, CarbonListComponent, EditorModule, OverflowMenuComponent, ConfirmationModalModule, RenderInPageHeaderDirective, OverflowMenuOptionComponent, OverflowMenuTriggerComponent, CarbonListModule } from '@valtimo/components';
|
|
13
|
+
import * as i1$2 from '@angular/router';
|
|
14
14
|
import { RouterModule } from '@angular/router';
|
|
15
15
|
import { AuthGuardService } from '@valtimo/security';
|
|
16
|
-
import * as
|
|
17
|
-
import { ButtonModule, ModalModule, InputModule,
|
|
16
|
+
import * as i2$1 from 'carbon-components-angular';
|
|
17
|
+
import { ButtonModule, DropdownModule, IconModule, ModalModule, InputModule, LoadingModule, NotificationModule, TabsModule } from 'carbon-components-angular';
|
|
18
18
|
import * as i1$1 from '@angular/forms';
|
|
19
19
|
import { Validators, FormsModule, ReactiveFormsModule } from '@angular/forms';
|
|
20
|
+
import { Filter16, TrashCan16 } from '@carbon/icons';
|
|
21
|
+
|
|
22
|
+
/*
|
|
23
|
+
* Copyright 2015-2026 Ritense BV, the Netherlands.
|
|
24
|
+
*
|
|
25
|
+
* Licensed under EUPL, Version 1.2 (the "License");
|
|
26
|
+
* you may not use this file except in compliance with the License.
|
|
27
|
+
* You may obtain a copy of the License at
|
|
28
|
+
*
|
|
29
|
+
* https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
|
|
30
|
+
*
|
|
31
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
32
|
+
* distributed under the License is distributed on an "AS IS" basis,
|
|
33
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
34
|
+
* See the License for the specific language governing permissions and
|
|
35
|
+
* limitations under the License.
|
|
36
|
+
*/
|
|
37
|
+
var AccessControlEditorTab;
|
|
38
|
+
(function (AccessControlEditorTab) {
|
|
39
|
+
AccessControlEditorTab["SUMMARY"] = "summary";
|
|
40
|
+
AccessControlEditorTab["JSON_EDITOR"] = "jsonEditor";
|
|
41
|
+
})(AccessControlEditorTab || (AccessControlEditorTab = {}));
|
|
42
|
+
|
|
43
|
+
/*
|
|
44
|
+
* Copyright 2015-2026 Ritense BV, the Netherlands.
|
|
45
|
+
*
|
|
46
|
+
* Licensed under EUPL, Version 1.2 (the "License");
|
|
47
|
+
* you may not use this file except in compliance with the License.
|
|
48
|
+
* You may obtain a copy of the License at
|
|
49
|
+
*
|
|
50
|
+
* https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
|
|
51
|
+
*
|
|
52
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
53
|
+
* distributed under the License is distributed on an "AS IS" basis,
|
|
54
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
55
|
+
* See the License for the specific language governing permissions and
|
|
56
|
+
* limitations under the License.
|
|
57
|
+
*/
|
|
58
|
+
|
|
59
|
+
/*
|
|
60
|
+
* Copyright 2015-2026 Ritense BV, the Netherlands.
|
|
61
|
+
*
|
|
62
|
+
* Licensed under EUPL, Version 1.2 (the "License");
|
|
63
|
+
* you may not use this file except in compliance with the License.
|
|
64
|
+
* You may obtain a copy of the License at
|
|
65
|
+
*
|
|
66
|
+
* https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
|
|
67
|
+
*
|
|
68
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
69
|
+
* distributed under the License is distributed on an "AS IS" basis,
|
|
70
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
71
|
+
* See the License for the specific language governing permissions and
|
|
72
|
+
* limitations under the License.
|
|
73
|
+
*/
|
|
20
74
|
|
|
21
75
|
/*
|
|
22
76
|
* Copyright 2015-2025 Ritense BV, the Netherlands.
|
|
@@ -106,6 +160,9 @@ class AccessControlService {
|
|
|
106
160
|
updateRolePermissions(roleKey, updatedPermission) {
|
|
107
161
|
return this.http.put(`${this.valtimoEndpointUri}v1/roles/${roleKey}/permissions`, updatedPermission);
|
|
108
162
|
}
|
|
163
|
+
getPermissionSchema() {
|
|
164
|
+
return this.http.get(`${this.valtimoEndpointUri}v1/permissions/schema`);
|
|
165
|
+
}
|
|
109
166
|
updateRole(roleKey, request) {
|
|
110
167
|
return this.http.put(`${this.valtimoEndpointUri}v1/roles/${roleKey}`, request);
|
|
111
168
|
}
|
|
@@ -117,6 +174,88 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
117
174
|
args: [{ providedIn: 'root' }]
|
|
118
175
|
}], ctorParameters: () => [{ type: i1.ConfigService }, { type: i2.HttpClient }] });
|
|
119
176
|
|
|
177
|
+
/*
|
|
178
|
+
* Copyright 2015-2026 Ritense BV, the Netherlands.
|
|
179
|
+
*
|
|
180
|
+
* Licensed under EUPL, Version 1.2 (the "License");
|
|
181
|
+
* you may not use this file except in compliance with the License.
|
|
182
|
+
* You may obtain a copy of the License at
|
|
183
|
+
*
|
|
184
|
+
* https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
|
|
185
|
+
*
|
|
186
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
187
|
+
* distributed under the License is distributed on an "AS IS" basis,
|
|
188
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
189
|
+
* See the License for the specific language governing permissions and
|
|
190
|
+
* limitations under the License.
|
|
191
|
+
*/
|
|
192
|
+
class PermissionSchemaMetadataService {
|
|
193
|
+
constructor(accessControlService) {
|
|
194
|
+
this.accessControlService = accessControlService;
|
|
195
|
+
this._knownResourceTypes$ = new BehaviorSubject(new Set());
|
|
196
|
+
this._fieldsByResourceType$ = new BehaviorSubject({});
|
|
197
|
+
this.schema$ = this.accessControlService
|
|
198
|
+
.getPermissionSchema()
|
|
199
|
+
.pipe(tap(schema => {
|
|
200
|
+
this._knownResourceTypes$.next(this.extractKnownResourceTypes(schema));
|
|
201
|
+
this._fieldsByResourceType$.next(this.extractFieldsByResourceType(schema));
|
|
202
|
+
}), shareReplay({ bufferSize: 1, refCount: false }));
|
|
203
|
+
this.actionsByResourceType$ = this.schema$.pipe(map(schema => this.extractActionsByResourceType(schema)));
|
|
204
|
+
this.allResourceTypes$ = this.actionsByResourceType$.pipe(map(actions => Object.keys(actions)));
|
|
205
|
+
}
|
|
206
|
+
isResourceTypeKnown(fqn) {
|
|
207
|
+
return this._knownResourceTypes$.value.has(fqn);
|
|
208
|
+
}
|
|
209
|
+
isFieldKnown(resourceType, field) {
|
|
210
|
+
return this._fieldsByResourceType$.value[resourceType]?.has(field) ?? false;
|
|
211
|
+
}
|
|
212
|
+
extractActionsByResourceType(schema) {
|
|
213
|
+
const branches = schema?.items?.allOf ?? [];
|
|
214
|
+
const result = {};
|
|
215
|
+
for (const branch of branches) {
|
|
216
|
+
const resourceType = branch?.if?.properties?.resourceType?.const;
|
|
217
|
+
const actions = branch?.then?.properties?.action?.enum;
|
|
218
|
+
if (resourceType && Array.isArray(actions)) {
|
|
219
|
+
result[resourceType] = [...actions];
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return result;
|
|
223
|
+
}
|
|
224
|
+
extractKnownResourceTypes(schema) {
|
|
225
|
+
const entries = schema?.items?.properties?.resourceType?.oneOf ?? [];
|
|
226
|
+
const result = new Set();
|
|
227
|
+
for (const entry of entries) {
|
|
228
|
+
if (entry?.const)
|
|
229
|
+
result.add(entry.const);
|
|
230
|
+
}
|
|
231
|
+
return result;
|
|
232
|
+
}
|
|
233
|
+
extractFieldsByResourceType(schema) {
|
|
234
|
+
const definitions = schema?.definitions ?? {};
|
|
235
|
+
const result = {};
|
|
236
|
+
for (const [key, def] of Object.entries(definitions)) {
|
|
237
|
+
if (!key.startsWith('condList.'))
|
|
238
|
+
continue;
|
|
239
|
+
const resourceType = key.substring('condList.'.length);
|
|
240
|
+
const fields = new Set();
|
|
241
|
+
for (const variant of def?.items?.oneOf ?? []) {
|
|
242
|
+
for (const part of variant?.allOf ?? []) {
|
|
243
|
+
for (const f of part?.properties?.field?.enum ?? [])
|
|
244
|
+
fields.add(f);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
result[resourceType] = fields;
|
|
248
|
+
}
|
|
249
|
+
return result;
|
|
250
|
+
}
|
|
251
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: PermissionSchemaMetadataService, deps: [{ token: AccessControlService }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
252
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: PermissionSchemaMetadataService, providedIn: 'root' }); }
|
|
253
|
+
}
|
|
254
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: PermissionSchemaMetadataService, decorators: [{
|
|
255
|
+
type: Injectable,
|
|
256
|
+
args: [{ providedIn: 'root' }]
|
|
257
|
+
}], ctorParameters: () => [{ type: AccessControlService }] });
|
|
258
|
+
|
|
120
259
|
/*
|
|
121
260
|
* Copyright 2015-2025 Ritense BV, the Netherlands.
|
|
122
261
|
*
|
|
@@ -237,7 +376,7 @@ class RoleMetadataModalComponent {
|
|
|
237
376
|
}, CARBON_CONSTANTS.modalAnimationMs);
|
|
238
377
|
}
|
|
239
378
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: RoleMetadataModalComponent, deps: [{ token: i1$1.FormBuilder }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
240
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.20", type: RoleMetadataModalComponent, isStandalone: false, selector: "valtimo-role-metadata-modal", inputs: { open: "open", type: "type", defaultKeyValue: "defaultKeyValue" }, outputs: { closeEvent: "closeEvent" }, ngImport: i0, template: "<!--\n ~ Copyright 2015-2025 Ritense BV, the Netherlands.\n ~\n ~ Licensed under EUPL, Version 1.2 (the \"License\");\n ~ you may not use this file except in compliance with the License.\n ~ You may obtain a copy of the License at\n ~\n ~ https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n ~\n ~ Unless required by applicable law or agreed to in writing, software\n ~ distributed under the License is distributed on an \"AS IS\" basis,\n ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n ~ See the License for the specific language governing permissions and\n ~ limitations under the License.\n -->\n\n<cds-modal\n *ngIf=\"{\n isAdd: type === 'add',\n } as vars\"\n [open]=\"open\"\n showFooter=\"true\"\n [title]=\"vars.title\"\n valtimoCdsModal\n (close)=\"onCancel()\"\n>\n <cds-modal-header [showCloseButton]=\"true\" (closeSelect)=\"onCancel()\">\n <h3 cdsModalHeaderHeading>\n {{\n vars.isAdd\n ? ('accessControl.roles.add' | translate)\n : ('accessControl.roles.editRole' | translate)\n }}\n </h3>\n </cds-modal-header>\n\n <section cdsModalContent>\n <form [formGroup]=\"form\">\n <cds-label [invalid]=\"key.dirty && key.invalid\">\n {{ 'accessControl.roles.name' | translate }}\n\n <input\n formControlName=\"key\"\n cdsText\n placeholder=\"{{ 'accessControl.roles.name' | translate }}\"\n [attr.modal-primary-focus]=\"true\"\n [invalid]=\"key.dirty && key.invalid\"\n />\n </cds-label>\n </form>\n </section>\n\n <cds-modal-footer>\n <button cdsButton=\"secondary\" (click)=\"onCancel()\">\n {{ 'interface.cancel' | translate }}\n </button>\n\n <button [disabled]=\"this.form.invalid\" (click)=\"onConfirm()\" cdsButton=\"primary\">\n {{ vars.isAdd ? ('interface.create' | translate) : ('interface.save' | translate) }}\n </button>\n </cds-modal-footer>\n</cds-modal>\n", dependencies: [{ kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type:
|
|
379
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.20", type: RoleMetadataModalComponent, isStandalone: false, selector: "valtimo-role-metadata-modal", inputs: { open: "open", type: "type", defaultKeyValue: "defaultKeyValue" }, outputs: { closeEvent: "closeEvent" }, ngImport: i0, template: "<!--\n ~ Copyright 2015-2025 Ritense BV, the Netherlands.\n ~\n ~ Licensed under EUPL, Version 1.2 (the \"License\");\n ~ you may not use this file except in compliance with the License.\n ~ You may obtain a copy of the License at\n ~\n ~ https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n ~\n ~ Unless required by applicable law or agreed to in writing, software\n ~ distributed under the License is distributed on an \"AS IS\" basis,\n ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n ~ See the License for the specific language governing permissions and\n ~ limitations under the License.\n -->\n\n<cds-modal\n *ngIf=\"{\n isAdd: type === 'add',\n } as vars\"\n [open]=\"open\"\n showFooter=\"true\"\n [title]=\"vars.title\"\n valtimoCdsModal\n (close)=\"onCancel()\"\n>\n <cds-modal-header [showCloseButton]=\"true\" (closeSelect)=\"onCancel()\">\n <h3 cdsModalHeaderHeading>\n {{\n vars.isAdd\n ? ('accessControl.roles.add' | translate)\n : ('accessControl.roles.editRole' | translate)\n }}\n </h3>\n </cds-modal-header>\n\n <section cdsModalContent>\n <form [formGroup]=\"form\">\n <cds-label [invalid]=\"key.dirty && key.invalid\">\n {{ 'accessControl.roles.name' | translate }}\n\n <input\n formControlName=\"key\"\n cdsText\n placeholder=\"{{ 'accessControl.roles.name' | translate }}\"\n [attr.modal-primary-focus]=\"true\"\n [invalid]=\"key.dirty && key.invalid\"\n />\n </cds-label>\n </form>\n </section>\n\n <cds-modal-footer>\n <button cdsButton=\"secondary\" (click)=\"onCancel()\">\n {{ 'interface.cancel' | translate }}\n </button>\n\n <button [disabled]=\"this.form.invalid\" (click)=\"onConfirm()\" cdsButton=\"primary\">\n {{ vars.isAdd ? ('interface.create' | translate) : ('interface.save' | translate) }}\n </button>\n </cds-modal-footer>\n</cds-modal>\n", dependencies: [{ kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2$1.Button, selector: "[cdsButton], [ibmButton]", inputs: ["ibmButton", "cdsButton", "size", "skeleton", "iconOnly", "isExpressive"] }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "component", type: i2$1.Modal, selector: "cds-modal, ibm-modal", inputs: ["size", "theme", "ariaLabel", "open", "trigger", "hasScrollingContent"], outputs: ["overlaySelected", "close"] }, { kind: "component", type: i2$1.ModalHeader, selector: "cds-modal-header, ibm-modal-header", inputs: ["theme", "closeLabel", "showCloseButton"], outputs: ["closeSelect"] }, { kind: "component", type: i2$1.ModalFooter, selector: "cds-modal-footer, ibm-modal-footer" }, { kind: "directive", type: i2$1.ModalContent, selector: "[cdsModalContent], [ibmModalContent]", inputs: ["hasForm"] }, { kind: "directive", type: i2$1.ModalHeaderHeading, selector: "[cdsModalHeaderHeading], [ibmModalHeaderHeading]" }, { kind: "directive", type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: i2$1.Label, selector: "cds-label, ibm-label", inputs: ["labelInputID", "disabled", "skeleton", "helperText", "invalidText", "invalid", "warn", "warnText", "ariaLabel"] }, { kind: "directive", type: i2$1.TextInput, selector: "[cdsText], [ibmText]", inputs: ["theme", "size", "invalid", "warn", "skeleton"] }, { kind: "pipe", type: i4$1.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
241
380
|
}
|
|
242
381
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: RoleMetadataModalComponent, decorators: [{
|
|
243
382
|
type: Component,
|
|
@@ -275,7 +414,7 @@ class DeleteRoleModalComponent {
|
|
|
275
414
|
this.deleteEvent.emit(roles);
|
|
276
415
|
}
|
|
277
416
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: DeleteRoleModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
278
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.20", type: DeleteRoleModalComponent, isStandalone: false, selector: "valtimo-delete-role-modal", inputs: { deleteRowKeys: "deleteRowKeys", showDeleteModal$: "showDeleteModal$" }, outputs: { deleteEvent: "deleteEvent" }, ngImport: i0, template: "<!--\n ~ Copyright 2015-2025 Ritense BV, the Netherlands.\n ~\n ~ Licensed under EUPL, Version 1.2 (the \"License\");\n ~ you may not use this file except in compliance with the License.\n ~ You may obtain a copy of the License at\n ~\n ~ https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n ~\n ~ Unless required by applicable law or agreed to in writing, software\n ~ distributed under the License is distributed on an \"AS IS\" basis,\n ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n ~ See the License for the specific language governing permissions and\n ~ limitations under the License.\n -->\n\n<valtimo-confirmation-modal\n confirmButtonTextTranslationKey=\"interface.delete\"\n confirmButtonType=\"danger\"\n contentTranslationKey=\"accessControl.roles.deleteModalContent\"\n [outputOnConfirm]=\"deleteRowKeys\"\n [showModalSubject$]=\"showDeleteModal$\"\n titleTranslationKey=\"interface.delete\"\n (confirmEvent)=\"onDelete($event)\"\n></valtimo-confirmation-modal>\n", dependencies: [{ kind: "component", type: i3
|
|
417
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.20", type: DeleteRoleModalComponent, isStandalone: false, selector: "valtimo-delete-role-modal", inputs: { deleteRowKeys: "deleteRowKeys", showDeleteModal$: "showDeleteModal$" }, outputs: { deleteEvent: "deleteEvent" }, ngImport: i0, template: "<!--\n ~ Copyright 2015-2025 Ritense BV, the Netherlands.\n ~\n ~ Licensed under EUPL, Version 1.2 (the \"License\");\n ~ you may not use this file except in compliance with the License.\n ~ You may obtain a copy of the License at\n ~\n ~ https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n ~\n ~ Unless required by applicable law or agreed to in writing, software\n ~ distributed under the License is distributed on an \"AS IS\" basis,\n ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n ~ See the License for the specific language governing permissions and\n ~ limitations under the License.\n -->\n\n<valtimo-confirmation-modal\n confirmButtonTextTranslationKey=\"interface.delete\"\n confirmButtonType=\"danger\"\n contentTranslationKey=\"accessControl.roles.deleteModalContent\"\n [outputOnConfirm]=\"deleteRowKeys\"\n [showModalSubject$]=\"showDeleteModal$\"\n titleTranslationKey=\"interface.delete\"\n (confirmEvent)=\"onDelete($event)\"\n></valtimo-confirmation-modal>\n", dependencies: [{ kind: "component", type: i3.ConfirmationModalComponent, selector: "valtimo-confirmation-modal", inputs: ["titleTranslationKey", "title", "content", "contentTranslationKey", "confirmButtonText", "confirmButtonTextTranslationKey", "confirmButtonType", "showOptionalButton", "optionalButtonText", "optionalButtonTextTranslationKey", "optionalButtonType", "cancelButtonText", "cancelButtonTextTranslationKey", "cancelButtonType", "showModalSubject$", "outputOnConfirm", "outputOnOptional", "spacerAfterCancelButton"], outputs: ["confirmEvent", "optionalEvent", "cancelEvent"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
279
418
|
}
|
|
280
419
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: DeleteRoleModalComponent, decorators: [{
|
|
281
420
|
type: Component,
|
|
@@ -340,7 +479,7 @@ class ExportRoleModalComponent {
|
|
|
340
479
|
});
|
|
341
480
|
}
|
|
342
481
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: ExportRoleModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
343
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.20", type: ExportRoleModalComponent, isStandalone: false, selector: "valtimo-export-role-modal", inputs: { open: "open", exportRowKeys: "exportRowKeys", reset$: "reset$", disabled: "disabled" }, outputs: { exportEvent: "exportEvent", closeEvent: "closeEvent" }, ngImport: i0, template: "<!--\n ~ Copyright 2015-2025 Ritense BV, the Netherlands.\n ~\n ~ Licensed under EUPL, Version 1.2 (the \"License\");\n ~ you may not use this file except in compliance with the License.\n ~ You may obtain a copy of the License at\n ~\n ~ https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n ~\n ~ Unless required by applicable law or agreed to in writing, software\n ~ distributed under the License is distributed on an \"AS IS\" basis,\n ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n ~ See the License for the specific language governing permissions and\n ~ limitations under the License.\n -->\n\n<cds-modal\n *ngIf=\"{title: ('interface.export' | translate), selectedType: selectedType$ | async} as vars\"\n valtimoCdsModal\n [open]=\"open\"\n size=\"sm\"\n showFooter=\"true\"\n [title]=\"vars.title\"\n (close)=\"onCancel()\"\n>\n <cds-modal-header [showCloseButton]=\"true\" (closeSelect)=\"onCancel()\">\n <h3 cdsModalHeaderHeading>\n {{ vars.title }}\n </h3>\n </cds-modal-header>\n\n <section cdsModalContent>\n <div class=\"export-buttons\">\n <button [disabled]=\"disabled\" cdsButton=\"secondary\" (click)=\"selectType('unified')\">\n {{ 'accessControl.roles.singleExport' | translate }}\n\n <svg class=\"cds--btn__icon\" cdsIcon=\"document\" size=\"16\"></svg>\n </button>\n\n <button [disabled]=\"disabled\" cdsButton=\"secondary\" (click)=\"selectType('separate')\">\n {{ 'accessControl.roles.multipleExport' | translate }}\n\n <svg class=\"cds--btn__icon\" cdsIcon=\"copy--file\" size=\"16\"></svg>\n </button>\n </div>\n </section>\n\n <cds-modal-footer>\n <button [disabled]=\"disabled\" cdsButton=\"secondary\" (click)=\"onCancel()\">\n {{ 'interface.cancel' | translate }}\n </button>\n\n <button\n [disabled]=\"!vars.selectedType || disabled\"\n cdsButton=\"primary\"\n (click)=\"onConfirm(vars.selectedType)\"\n >\n {{ vars.title }}\n </button>\n </cds-modal-footer>\n</cds-modal>\n", styles: [".export-buttons{display:grid;width:100%;grid-template-columns:1fr 1fr;column-gap:16px}.export-buttons button{width:100%}\n/*!\n * Copyright 2015-2025 Ritense BV, the Netherlands.\n *\n * Licensed under EUPL, Version 1.2 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n"], dependencies: [{ kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type:
|
|
482
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.20", type: ExportRoleModalComponent, isStandalone: false, selector: "valtimo-export-role-modal", inputs: { open: "open", exportRowKeys: "exportRowKeys", reset$: "reset$", disabled: "disabled" }, outputs: { exportEvent: "exportEvent", closeEvent: "closeEvent" }, ngImport: i0, template: "<!--\n ~ Copyright 2015-2025 Ritense BV, the Netherlands.\n ~\n ~ Licensed under EUPL, Version 1.2 (the \"License\");\n ~ you may not use this file except in compliance with the License.\n ~ You may obtain a copy of the License at\n ~\n ~ https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n ~\n ~ Unless required by applicable law or agreed to in writing, software\n ~ distributed under the License is distributed on an \"AS IS\" basis,\n ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n ~ See the License for the specific language governing permissions and\n ~ limitations under the License.\n -->\n\n<cds-modal\n *ngIf=\"{title: ('interface.export' | translate), selectedType: selectedType$ | async} as vars\"\n valtimoCdsModal\n [open]=\"open\"\n size=\"sm\"\n showFooter=\"true\"\n [title]=\"vars.title\"\n (close)=\"onCancel()\"\n>\n <cds-modal-header [showCloseButton]=\"true\" (closeSelect)=\"onCancel()\">\n <h3 cdsModalHeaderHeading>\n {{ vars.title }}\n </h3>\n </cds-modal-header>\n\n <section cdsModalContent>\n <div class=\"export-buttons\">\n <button [disabled]=\"disabled\" cdsButton=\"secondary\" (click)=\"selectType('unified')\">\n {{ 'accessControl.roles.singleExport' | translate }}\n\n <svg class=\"cds--btn__icon\" cdsIcon=\"document\" size=\"16\"></svg>\n </button>\n\n <button [disabled]=\"disabled\" cdsButton=\"secondary\" (click)=\"selectType('separate')\">\n {{ 'accessControl.roles.multipleExport' | translate }}\n\n <svg class=\"cds--btn__icon\" cdsIcon=\"copy--file\" size=\"16\"></svg>\n </button>\n </div>\n </section>\n\n <cds-modal-footer>\n <button [disabled]=\"disabled\" cdsButton=\"secondary\" (click)=\"onCancel()\">\n {{ 'interface.cancel' | translate }}\n </button>\n\n <button\n [disabled]=\"!vars.selectedType || disabled\"\n cdsButton=\"primary\"\n (click)=\"onConfirm(vars.selectedType)\"\n >\n {{ vars.title }}\n </button>\n </cds-modal-footer>\n</cds-modal>\n", styles: [".export-buttons{display:grid;width:100%;grid-template-columns:1fr 1fr;column-gap:16px}.export-buttons button{width:100%}\n/*!\n * Copyright 2015-2025 Ritense BV, the Netherlands.\n *\n * Licensed under EUPL, Version 1.2 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n"], dependencies: [{ kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2$1.Button, selector: "[cdsButton], [ibmButton]", inputs: ["ibmButton", "cdsButton", "size", "skeleton", "iconOnly", "isExpressive"] }, { kind: "component", type: i2$1.Modal, selector: "cds-modal, ibm-modal", inputs: ["size", "theme", "ariaLabel", "open", "trigger", "hasScrollingContent"], outputs: ["overlaySelected", "close"] }, { kind: "component", type: i2$1.ModalHeader, selector: "cds-modal-header, ibm-modal-header", inputs: ["theme", "closeLabel", "showCloseButton"], outputs: ["closeSelect"] }, { kind: "component", type: i2$1.ModalFooter, selector: "cds-modal-footer, ibm-modal-footer" }, { kind: "directive", type: i2$1.ModalContent, selector: "[cdsModalContent], [ibmModalContent]", inputs: ["hasForm"] }, { kind: "directive", type: i2$1.ModalHeaderHeading, selector: "[cdsModalHeaderHeading], [ibmModalHeaderHeading]" }, { kind: "directive", type: i2$1.IconDirective, selector: "[cdsIcon], [ibmIcon]", inputs: ["ibmIcon", "cdsIcon", "size", "title", "ariaLabel", "ariaLabelledBy", "ariaHidden", "isFocusable"] }, { kind: "pipe", type: i4.AsyncPipe, name: "async" }, { kind: "pipe", type: i4$1.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
344
483
|
}
|
|
345
484
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: ExportRoleModalComponent, decorators: [{
|
|
346
485
|
type: Component,
|
|
@@ -451,17 +590,494 @@ class AccessControlOverviewComponent {
|
|
|
451
590
|
setSelectedRoleKeys() {
|
|
452
591
|
this.selectedRowKeys$.next(this.carbonList.selectedItems.map((role) => role.roleKey));
|
|
453
592
|
}
|
|
454
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: AccessControlOverviewComponent, deps: [{ token: AccessControlService }, { token: AccessControlExportService }, { token:
|
|
455
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.20", type: AccessControlOverviewComponent, isStandalone: false, selector: "ng-component", viewQueries: [{ propertyName: "carbonList", first: true, predicate: CarbonListComponent, descendants: true }], ngImport: i0, template: "<!--\n ~ Copyright 2015-2025 Ritense BV, the Netherlands.\n ~\n ~ Licensed under EUPL, Version 1.2 (the \"License\");\n ~ you may not use this file except in compliance with the License.\n ~ You may obtain a copy of the License at\n ~\n ~ https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n ~\n ~ Unless required by applicable law or agreed to in writing, software\n ~ distributed under the License is distributed on an \"AS IS\" basis,\n ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n ~ See the License for the specific language governing permissions and\n ~ limitations under the License.\n -->\n\n<valtimo-carbon-list\n [header]=\"false\"\n [items]=\"roles$ | async\"\n [fields]=\"fields\"\n [loading]=\"loading$ | async\"\n [showSelectionColumn]=\"true\"\n (rowClicked)=\"onRowClick($event)\"\n>\n <ng-container header> {{ 'accessControl.roles.title' | translate }} </ng-container>\n\n <div carbonToolbarContent>\n <ng-container [ngTemplateOutlet]=\"addRoleButton\"></ng-container>\n </div>\n\n <ng-container carbonToolbarActions>\n <button cdsButton=\"primary\" (click)=\"showDeleteModal()\">\n {{ 'interface.delete' | translate }}\n\n <svg class=\"cds--btn__icon\" cdsIcon=\"trash-can\" size=\"16\"></svg>\n </button>\n\n <button cdsButton=\"primary\" (click)=\"showExportModal()\">\n {{ 'interface.export' | translate }}\n\n <svg class=\"cds--btn__icon\" cdsIcon=\"download\" size=\"16\"></svg>\n </button>\n </ng-container>\n\n <valtimo-no-results\n [action]=\"addRoleButton\"\n description=\"{{ 'accessControl.roles.noResults.description' | translate }}\"\n title=\"{{ 'accessControl.roles.noResults.title' | translate }}\"\n ></valtimo-no-results>\n</valtimo-carbon-list>\n\n<valtimo-role-metadata-modal\n [open]=\"showAddModal$ | async\"\n (closeEvent)=\"onAdd($event)\"\n></valtimo-role-metadata-modal>\n\n<ng-container *ngIf=\"{selectedRowKeys: selectedRowKeys$ | async} as obs\">\n <valtimo-delete-role-modal\n [deleteRowKeys]=\"obs.selectedRowKeys\"\n [showDeleteModal$]=\"showDeleteModal$\"\n (deleteEvent)=\"onDelete($event)\"\n >\n </valtimo-delete-role-modal>\n\n <valtimo-export-role-modal\n [disabled]=\"exportDisabled$ | async\"\n [exportRowKeys]=\"obs.selectedRowKeys\"\n [open]=\"showExportModal$ | async\"\n [reset$]=\"resetExportType$\"\n (exportEvent)=\"onExport($event)\"\n (closeEvent)=\"closeExportModal()\"\n >\n </valtimo-export-role-modal>\n</ng-container>\n\n<ng-template #addRoleButton>\n <button cdsButton=\"primary\" (click)=\"openAddModal()\">\n {{ 'accessControl.roles.add' | translate }}\n\n <svg class=\"cds--btn__icon\" cdsIcon=\"add\" size=\"16\"></svg>\n </button>\n</ng-template>\n", dependencies: [{ kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i4.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type:
|
|
593
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: AccessControlOverviewComponent, deps: [{ token: AccessControlService }, { token: AccessControlExportService }, { token: i1$2.Router }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
594
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.20", type: AccessControlOverviewComponent, isStandalone: false, selector: "ng-component", viewQueries: [{ propertyName: "carbonList", first: true, predicate: CarbonListComponent, descendants: true }], ngImport: i0, template: "<!--\n ~ Copyright 2015-2025 Ritense BV, the Netherlands.\n ~\n ~ Licensed under EUPL, Version 1.2 (the \"License\");\n ~ you may not use this file except in compliance with the License.\n ~ You may obtain a copy of the License at\n ~\n ~ https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n ~\n ~ Unless required by applicable law or agreed to in writing, software\n ~ distributed under the License is distributed on an \"AS IS\" basis,\n ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n ~ See the License for the specific language governing permissions and\n ~ limitations under the License.\n -->\n\n<valtimo-carbon-list\n [header]=\"false\"\n [items]=\"roles$ | async\"\n [fields]=\"fields\"\n [loading]=\"loading$ | async\"\n [showSelectionColumn]=\"true\"\n (rowClicked)=\"onRowClick($event)\"\n>\n <ng-container header> {{ 'accessControl.roles.title' | translate }} </ng-container>\n\n <div carbonToolbarContent>\n <ng-container [ngTemplateOutlet]=\"addRoleButton\"></ng-container>\n </div>\n\n <ng-container carbonToolbarActions>\n <button cdsButton=\"primary\" (click)=\"showDeleteModal()\">\n {{ 'interface.delete' | translate }}\n\n <svg class=\"cds--btn__icon\" cdsIcon=\"trash-can\" size=\"16\"></svg>\n </button>\n\n <button cdsButton=\"primary\" (click)=\"showExportModal()\">\n {{ 'interface.export' | translate }}\n\n <svg class=\"cds--btn__icon\" cdsIcon=\"download\" size=\"16\"></svg>\n </button>\n </ng-container>\n\n <valtimo-no-results\n [action]=\"addRoleButton\"\n description=\"{{ 'accessControl.roles.noResults.description' | translate }}\"\n title=\"{{ 'accessControl.roles.noResults.title' | translate }}\"\n ></valtimo-no-results>\n</valtimo-carbon-list>\n\n<valtimo-role-metadata-modal\n [open]=\"showAddModal$ | async\"\n (closeEvent)=\"onAdd($event)\"\n></valtimo-role-metadata-modal>\n\n<ng-container *ngIf=\"{selectedRowKeys: selectedRowKeys$ | async} as obs\">\n <valtimo-delete-role-modal\n [deleteRowKeys]=\"obs.selectedRowKeys\"\n [showDeleteModal$]=\"showDeleteModal$\"\n (deleteEvent)=\"onDelete($event)\"\n >\n </valtimo-delete-role-modal>\n\n <valtimo-export-role-modal\n [disabled]=\"exportDisabled$ | async\"\n [exportRowKeys]=\"obs.selectedRowKeys\"\n [open]=\"showExportModal$ | async\"\n [reset$]=\"resetExportType$\"\n (exportEvent)=\"onExport($event)\"\n (closeEvent)=\"closeExportModal()\"\n >\n </valtimo-export-role-modal>\n</ng-container>\n\n<ng-template #addRoleButton>\n <button cdsButton=\"primary\" (click)=\"openAddModal()\">\n {{ 'accessControl.roles.add' | translate }}\n\n <svg class=\"cds--btn__icon\" cdsIcon=\"add\" size=\"16\"></svg>\n </button>\n</ng-template>\n", dependencies: [{ kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i4.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i2$1.Button, selector: "[cdsButton], [ibmButton]", inputs: ["ibmButton", "cdsButton", "size", "skeleton", "iconOnly", "isExpressive"] }, { kind: "directive", type: i2$1.IconDirective, selector: "[cdsIcon], [ibmIcon]", inputs: ["ibmIcon", "cdsIcon", "size", "title", "ariaLabel", "ariaLabelledBy", "ariaHidden", "isFocusable"] }, { kind: "component", type: i3.CarbonListComponent, selector: "valtimo-carbon-list", inputs: ["items", "fields", "tableTranslations", "paginatorConfig", "pagination", "loading", "skeletonRowCount", "actions", "actionItems", "showActionItems", "header", "hideColumnHeader", "initialSortState", "sortState", "isSearchable", "enableSingleSelection", "lastColumnTemplate", "paginationIdentifier", "showSelectionColumn", "striped", "hideToolbar", "lockedTooltipTranslationKey", "movingRowsEnabled", "dragAndDrop", "dragAndDropDisabled"], outputs: ["rowClicked", "paginationClicked", "paginationSet", "search", "sortChanged", "moveRow", "itemsReordered"] }, { kind: "component", type: i3.CarbonNoResultsComponent, selector: "valtimo-no-results", inputs: ["action", "description", "illustration", "title", "smallPadding", "collapseVertically", "alwaysRenderVertically"] }, { kind: "component", type: RoleMetadataModalComponent, selector: "valtimo-role-metadata-modal", inputs: ["open", "type", "defaultKeyValue"], outputs: ["closeEvent"] }, { kind: "component", type: DeleteRoleModalComponent, selector: "valtimo-delete-role-modal", inputs: ["deleteRowKeys", "showDeleteModal$"], outputs: ["deleteEvent"] }, { kind: "component", type: ExportRoleModalComponent, selector: "valtimo-export-role-modal", inputs: ["open", "exportRowKeys", "reset$", "disabled"], outputs: ["exportEvent", "closeEvent"] }, { kind: "pipe", type: i4.AsyncPipe, name: "async" }, { kind: "pipe", type: i4$1.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
456
595
|
}
|
|
457
596
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: AccessControlOverviewComponent, decorators: [{
|
|
458
597
|
type: Component,
|
|
459
598
|
args: [{ standalone: false, changeDetection: ChangeDetectionStrategy.OnPush, template: "<!--\n ~ Copyright 2015-2025 Ritense BV, the Netherlands.\n ~\n ~ Licensed under EUPL, Version 1.2 (the \"License\");\n ~ you may not use this file except in compliance with the License.\n ~ You may obtain a copy of the License at\n ~\n ~ https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n ~\n ~ Unless required by applicable law or agreed to in writing, software\n ~ distributed under the License is distributed on an \"AS IS\" basis,\n ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n ~ See the License for the specific language governing permissions and\n ~ limitations under the License.\n -->\n\n<valtimo-carbon-list\n [header]=\"false\"\n [items]=\"roles$ | async\"\n [fields]=\"fields\"\n [loading]=\"loading$ | async\"\n [showSelectionColumn]=\"true\"\n (rowClicked)=\"onRowClick($event)\"\n>\n <ng-container header> {{ 'accessControl.roles.title' | translate }} </ng-container>\n\n <div carbonToolbarContent>\n <ng-container [ngTemplateOutlet]=\"addRoleButton\"></ng-container>\n </div>\n\n <ng-container carbonToolbarActions>\n <button cdsButton=\"primary\" (click)=\"showDeleteModal()\">\n {{ 'interface.delete' | translate }}\n\n <svg class=\"cds--btn__icon\" cdsIcon=\"trash-can\" size=\"16\"></svg>\n </button>\n\n <button cdsButton=\"primary\" (click)=\"showExportModal()\">\n {{ 'interface.export' | translate }}\n\n <svg class=\"cds--btn__icon\" cdsIcon=\"download\" size=\"16\"></svg>\n </button>\n </ng-container>\n\n <valtimo-no-results\n [action]=\"addRoleButton\"\n description=\"{{ 'accessControl.roles.noResults.description' | translate }}\"\n title=\"{{ 'accessControl.roles.noResults.title' | translate }}\"\n ></valtimo-no-results>\n</valtimo-carbon-list>\n\n<valtimo-role-metadata-modal\n [open]=\"showAddModal$ | async\"\n (closeEvent)=\"onAdd($event)\"\n></valtimo-role-metadata-modal>\n\n<ng-container *ngIf=\"{selectedRowKeys: selectedRowKeys$ | async} as obs\">\n <valtimo-delete-role-modal\n [deleteRowKeys]=\"obs.selectedRowKeys\"\n [showDeleteModal$]=\"showDeleteModal$\"\n (deleteEvent)=\"onDelete($event)\"\n >\n </valtimo-delete-role-modal>\n\n <valtimo-export-role-modal\n [disabled]=\"exportDisabled$ | async\"\n [exportRowKeys]=\"obs.selectedRowKeys\"\n [open]=\"showExportModal$ | async\"\n [reset$]=\"resetExportType$\"\n (exportEvent)=\"onExport($event)\"\n (closeEvent)=\"closeExportModal()\"\n >\n </valtimo-export-role-modal>\n</ng-container>\n\n<ng-template #addRoleButton>\n <button cdsButton=\"primary\" (click)=\"openAddModal()\">\n {{ 'accessControl.roles.add' | translate }}\n\n <svg class=\"cds--btn__icon\" cdsIcon=\"add\" size=\"16\"></svg>\n </button>\n</ng-template>\n" }]
|
|
460
|
-
}], ctorParameters: () => [{ type: AccessControlService }, { type: AccessControlExportService }, { type:
|
|
599
|
+
}], ctorParameters: () => [{ type: AccessControlService }, { type: AccessControlExportService }, { type: i1$2.Router }], propDecorators: { carbonList: [{
|
|
461
600
|
type: ViewChild,
|
|
462
601
|
args: [CarbonListComponent]
|
|
463
602
|
}] } });
|
|
464
603
|
|
|
604
|
+
/*
|
|
605
|
+
* Copyright 2015-2026 Ritense BV, the Netherlands.
|
|
606
|
+
*
|
|
607
|
+
* Licensed under EUPL, Version 1.2 (the "License");
|
|
608
|
+
* you may not use this file except in compliance with the License.
|
|
609
|
+
* You may obtain a copy of the License at
|
|
610
|
+
*
|
|
611
|
+
* https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
|
|
612
|
+
*
|
|
613
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
614
|
+
* distributed under the License is distributed on an "AS IS" basis,
|
|
615
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
616
|
+
* See the License for the specific language governing permissions and
|
|
617
|
+
* limitations under the License.
|
|
618
|
+
*/
|
|
619
|
+
const OPERATOR_LABEL = {
|
|
620
|
+
'==': 'accessControl.overview.operators.eq',
|
|
621
|
+
'!=': 'accessControl.overview.operators.neq',
|
|
622
|
+
'>': 'accessControl.overview.operators.gt',
|
|
623
|
+
'>=': 'accessControl.overview.operators.gte',
|
|
624
|
+
'<': 'accessControl.overview.operators.lt',
|
|
625
|
+
'<=': 'accessControl.overview.operators.lte',
|
|
626
|
+
in: 'accessControl.overview.operators.in',
|
|
627
|
+
list_contains: 'accessControl.overview.operators.list_contains',
|
|
628
|
+
};
|
|
629
|
+
const NO_CONTEXT_RESOURCE_TYPE = 'com.ritense.authorization.NoContext';
|
|
630
|
+
|
|
631
|
+
/*
|
|
632
|
+
* Copyright 2015-2026 Ritense BV, the Netherlands.
|
|
633
|
+
*
|
|
634
|
+
* Licensed under EUPL, Version 1.2 (the "License");
|
|
635
|
+
* you may not use this file except in compliance with the License.
|
|
636
|
+
* You may obtain a copy of the License at
|
|
637
|
+
*
|
|
638
|
+
* https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
|
|
639
|
+
*
|
|
640
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
641
|
+
* distributed under the License is distributed on an "AS IS" basis,
|
|
642
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
643
|
+
* See the License for the specific language governing permissions and
|
|
644
|
+
* limitations under the License.
|
|
645
|
+
*/
|
|
646
|
+
|
|
647
|
+
/*
|
|
648
|
+
* Copyright 2015-2026 Ritense BV, the Netherlands.
|
|
649
|
+
*
|
|
650
|
+
* Licensed under EUPL, Version 1.2 (the "License");
|
|
651
|
+
* you may not use this file except in compliance with the License.
|
|
652
|
+
* You may obtain a copy of the License at
|
|
653
|
+
*
|
|
654
|
+
* https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
|
|
655
|
+
*
|
|
656
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
657
|
+
* distributed under the License is distributed on an "AS IS" basis,
|
|
658
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
659
|
+
* See the License for the specific language governing permissions and
|
|
660
|
+
* limitations under the License.
|
|
661
|
+
*/
|
|
662
|
+
const SPECIAL_VALUE_PATTERN = /^\$\{([A-Za-z_$][A-Za-z0-9_$]*)\}$/;
|
|
663
|
+
function lowerFirst(s) {
|
|
664
|
+
return s.charAt(0).toLowerCase() + s.slice(1);
|
|
665
|
+
}
|
|
666
|
+
function upperFirst(s) {
|
|
667
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
668
|
+
}
|
|
669
|
+
function resourceTypeI18nKey(fqn) {
|
|
670
|
+
const lastSegment = fqn.split('.').pop();
|
|
671
|
+
if (!lastSegment)
|
|
672
|
+
return null;
|
|
673
|
+
return `accessControl.resourceTypes.${lowerFirst(lastSegment)}`;
|
|
674
|
+
}
|
|
675
|
+
function fieldI18nKey(resourceType, field) {
|
|
676
|
+
const resourceSegment = resourceType.split('.').pop();
|
|
677
|
+
if (!resourceSegment || !field)
|
|
678
|
+
return null;
|
|
679
|
+
const fieldSegment = field
|
|
680
|
+
.split('.')
|
|
681
|
+
.map((part, i) => (i === 0 ? part : upperFirst(part)))
|
|
682
|
+
.join('');
|
|
683
|
+
return `accessControl.overview.fields.${lowerFirst(resourceSegment)}.${fieldSegment}`;
|
|
684
|
+
}
|
|
685
|
+
function tryTranslate(translate, key) {
|
|
686
|
+
const translated = translate.instant(key);
|
|
687
|
+
return translated === key ? null : translated;
|
|
688
|
+
}
|
|
689
|
+
function formatResourceType(translate, fqn) {
|
|
690
|
+
const key = resourceTypeI18nKey(fqn);
|
|
691
|
+
const translated = key ? tryTranslate(translate, key) : null;
|
|
692
|
+
if (translated !== null)
|
|
693
|
+
return translated;
|
|
694
|
+
const segments = fqn.split('.');
|
|
695
|
+
return segments[segments.length - 1] || fqn;
|
|
696
|
+
}
|
|
697
|
+
function formatField(translate, resourceType, field) {
|
|
698
|
+
const key = fieldI18nKey(resourceType, field);
|
|
699
|
+
const translated = key ? tryTranslate(translate, key) : null;
|
|
700
|
+
if (translated !== null)
|
|
701
|
+
return translated;
|
|
702
|
+
return humanizeFieldPath(field);
|
|
703
|
+
}
|
|
704
|
+
function formatOperator(translate, operator) {
|
|
705
|
+
const key = OPERATOR_LABEL[operator];
|
|
706
|
+
return key ? translate.instant(key) : operator;
|
|
707
|
+
}
|
|
708
|
+
function formatValue(translate, value) {
|
|
709
|
+
if (value === null) {
|
|
710
|
+
return translate.instant('accessControl.overview.specialValues.null');
|
|
711
|
+
}
|
|
712
|
+
if (typeof value === 'string') {
|
|
713
|
+
if (value === '')
|
|
714
|
+
return translate.instant('accessControl.overview.specialValues.empty');
|
|
715
|
+
const match = SPECIAL_VALUE_PATTERN.exec(value);
|
|
716
|
+
if (match) {
|
|
717
|
+
const key = `accessControl.overview.specialValues.${match[1]}`;
|
|
718
|
+
const translated = tryTranslate(translate, key);
|
|
719
|
+
if (translated !== null)
|
|
720
|
+
return translated;
|
|
721
|
+
}
|
|
722
|
+
return value;
|
|
723
|
+
}
|
|
724
|
+
if (Array.isArray(value)) {
|
|
725
|
+
return value.map(item => formatValue(translate, item)).join(', ');
|
|
726
|
+
}
|
|
727
|
+
return String(value);
|
|
728
|
+
}
|
|
729
|
+
function humanizeFieldPath(field) {
|
|
730
|
+
return field
|
|
731
|
+
.split('.')
|
|
732
|
+
.map(part => part.replace(/([a-z0-9])([A-Z])/g, '$1 $2').toLowerCase())
|
|
733
|
+
.join(' ');
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
/*
|
|
737
|
+
* Copyright 2015-2026 Ritense BV, the Netherlands.
|
|
738
|
+
*
|
|
739
|
+
* Licensed under EUPL, Version 1.2 (the "License");
|
|
740
|
+
* you may not use this file except in compliance with the License.
|
|
741
|
+
* You may obtain a copy of the License at
|
|
742
|
+
*
|
|
743
|
+
* https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
|
|
744
|
+
*
|
|
745
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
746
|
+
* distributed under the License is distributed on an "AS IS" basis,
|
|
747
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
748
|
+
* See the License for the specific language governing permissions and
|
|
749
|
+
* limitations under the License.
|
|
750
|
+
*/
|
|
751
|
+
|
|
752
|
+
/*
|
|
753
|
+
* Copyright 2015-2026 Ritense BV, the Netherlands.
|
|
754
|
+
*
|
|
755
|
+
* Licensed under EUPL, Version 1.2 (the "License");
|
|
756
|
+
* you may not use this file except in compliance with the License.
|
|
757
|
+
* You may obtain a copy of the License at
|
|
758
|
+
*
|
|
759
|
+
* https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
|
|
760
|
+
*
|
|
761
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
762
|
+
* distributed under the License is distributed on an "AS IS" basis,
|
|
763
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
764
|
+
* See the License for the specific language governing permissions and
|
|
765
|
+
* limitations under the License.
|
|
766
|
+
*/
|
|
767
|
+
class AccessControlJsonEditorTabComponent {
|
|
768
|
+
constructor(activatedRoute, iconService, metadataService, router, translateService) {
|
|
769
|
+
this.activatedRoute = activatedRoute;
|
|
770
|
+
this.iconService = iconService;
|
|
771
|
+
this.metadataService = metadataService;
|
|
772
|
+
this.router = router;
|
|
773
|
+
this.translateService = translateService;
|
|
774
|
+
this.disabled = false;
|
|
775
|
+
this.validEvent = new EventEmitter();
|
|
776
|
+
this.valueChangeEvent = new EventEmitter();
|
|
777
|
+
this._sourceModel$ = new BehaviorSubject(null);
|
|
778
|
+
this._filter$ = new BehaviorSubject({
|
|
779
|
+
resourceType: null,
|
|
780
|
+
action: null,
|
|
781
|
+
});
|
|
782
|
+
this._subscriptions = new Subscription();
|
|
783
|
+
this.isFilterActive$ = this._filter$.pipe(map(({ resourceType, action }) => !!resourceType || !!action));
|
|
784
|
+
this.resourceTypeItems$ = combineLatest([
|
|
785
|
+
this._sourceModel$,
|
|
786
|
+
this._filter$,
|
|
787
|
+
]).pipe(map(([sourceModel, currentFilter]) => {
|
|
788
|
+
const permissions = this.parsePermissions(sourceModel);
|
|
789
|
+
const resourceTypes = Array.from(new Set(permissions.map(p => p.resourceType).filter(Boolean))).sort();
|
|
790
|
+
return resourceTypes.map(resourceType => ({
|
|
791
|
+
content: formatResourceType(this.translateService, resourceType),
|
|
792
|
+
resourceType,
|
|
793
|
+
selected: currentFilter.resourceType === resourceType,
|
|
794
|
+
}));
|
|
795
|
+
}));
|
|
796
|
+
this.actionItems$ = combineLatest([
|
|
797
|
+
this._sourceModel$,
|
|
798
|
+
this._filter$,
|
|
799
|
+
]).pipe(map(([sourceModel, currentFilter]) => {
|
|
800
|
+
const permissions = this.parsePermissions(sourceModel);
|
|
801
|
+
const scoped = currentFilter.resourceType
|
|
802
|
+
? permissions.filter(p => p.resourceType === currentFilter.resourceType)
|
|
803
|
+
: permissions;
|
|
804
|
+
const actions = Array.from(new Set(scoped.flatMap(p => [...(p.actions ?? []), ...(p.action ? [p.action] : [])]))).sort();
|
|
805
|
+
return actions.map(action => ({
|
|
806
|
+
content: this.translateService.instant(`accessControl.actions.${action}`),
|
|
807
|
+
action,
|
|
808
|
+
selected: currentFilter.action === action,
|
|
809
|
+
}));
|
|
810
|
+
}));
|
|
811
|
+
this.filteredModel$ = combineLatest([
|
|
812
|
+
this._sourceModel$,
|
|
813
|
+
this._filter$,
|
|
814
|
+
]).pipe(map(([sourceModel, currentFilter]) => {
|
|
815
|
+
if (!sourceModel)
|
|
816
|
+
return null;
|
|
817
|
+
if (!currentFilter.resourceType && !currentFilter.action)
|
|
818
|
+
return sourceModel;
|
|
819
|
+
const permissions = this.parsePermissions(sourceModel);
|
|
820
|
+
const filtered = permissions.filter(permission => {
|
|
821
|
+
const resourceMatch = !currentFilter.resourceType || permission.resourceType === currentFilter.resourceType;
|
|
822
|
+
const actionMatch = !currentFilter.action ||
|
|
823
|
+
(permission.actions ?? []).includes(currentFilter.action) ||
|
|
824
|
+
permission.action === currentFilter.action;
|
|
825
|
+
return resourceMatch && actionMatch;
|
|
826
|
+
});
|
|
827
|
+
return {
|
|
828
|
+
...sourceModel,
|
|
829
|
+
value: JSON.stringify(filtered, null, 2),
|
|
830
|
+
};
|
|
831
|
+
}));
|
|
832
|
+
this.editorDisabled$ = this._filter$.pipe(map(({ resourceType, action }) => !!resourceType || !!action));
|
|
833
|
+
this.permissionSchema$ = this.metadataService.schema$;
|
|
834
|
+
this.iconService.registerAll([Filter16, TrashCan16]);
|
|
835
|
+
}
|
|
836
|
+
ngOnInit() {
|
|
837
|
+
this.activatedRoute.queryParamMap.pipe(take(1)).subscribe(params => {
|
|
838
|
+
this._filter$.next({
|
|
839
|
+
resourceType: params.get('filterResourceType'),
|
|
840
|
+
action: params.get('filterAction'),
|
|
841
|
+
});
|
|
842
|
+
});
|
|
843
|
+
this._subscriptions.add(this._filter$.pipe(skip(1)).subscribe(currentFilter => {
|
|
844
|
+
this.router.navigate([], {
|
|
845
|
+
relativeTo: this.activatedRoute,
|
|
846
|
+
queryParams: {
|
|
847
|
+
filterResourceType: currentFilter.resourceType ?? null,
|
|
848
|
+
filterAction: currentFilter.action ?? null,
|
|
849
|
+
},
|
|
850
|
+
queryParamsHandling: 'merge',
|
|
851
|
+
replaceUrl: true,
|
|
852
|
+
});
|
|
853
|
+
}));
|
|
854
|
+
}
|
|
855
|
+
ngOnChanges(changes) {
|
|
856
|
+
if (changes['model']) {
|
|
857
|
+
this._sourceModel$.next(this.model);
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
ngOnDestroy() {
|
|
861
|
+
this._subscriptions.unsubscribe();
|
|
862
|
+
}
|
|
863
|
+
onValid(valid) {
|
|
864
|
+
this.validEvent.emit(valid);
|
|
865
|
+
}
|
|
866
|
+
onValueChange(value) {
|
|
867
|
+
if (this._filter$.getValue().resourceType || this._filter$.getValue().action)
|
|
868
|
+
return;
|
|
869
|
+
this.valueChangeEvent.emit(value);
|
|
870
|
+
}
|
|
871
|
+
onResourceTypeSelected(event) {
|
|
872
|
+
const resourceType = event.item?.resourceType ?? null;
|
|
873
|
+
this._filter$.next({ ...this._filter$.getValue(), resourceType, action: null });
|
|
874
|
+
}
|
|
875
|
+
onActionSelected(event) {
|
|
876
|
+
const action = event.item?.action ?? null;
|
|
877
|
+
this._filter$.next({ ...this._filter$.getValue(), action });
|
|
878
|
+
}
|
|
879
|
+
onClearFilter() {
|
|
880
|
+
this._filter$.next({ resourceType: null, action: null });
|
|
881
|
+
}
|
|
882
|
+
parsePermissions(model) {
|
|
883
|
+
if (!model?.value)
|
|
884
|
+
return [];
|
|
885
|
+
try {
|
|
886
|
+
const parsed = JSON.parse(model.value);
|
|
887
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
888
|
+
}
|
|
889
|
+
catch {
|
|
890
|
+
return [];
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: AccessControlJsonEditorTabComponent, deps: [{ token: i1$2.ActivatedRoute }, { token: i2$1.IconService }, { token: PermissionSchemaMetadataService }, { token: i1$2.Router }, { token: i4$1.TranslateService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
894
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.20", type: AccessControlJsonEditorTabComponent, isStandalone: true, selector: "valtimo-access-control-json-editor-tab", inputs: { disabled: "disabled", model: "model" }, outputs: { validEvent: "validEvent", valueChangeEvent: "valueChangeEvent" }, usesOnChanges: true, ngImport: i0, template: "<!--\n ~ Copyright 2015-2026 Ritense BV, the Netherlands.\n ~\n ~ Licensed under EUPL, Version 1.2 (the \"License\");\n ~ you may not use this file except in compliance with the License.\n ~ You may obtain a copy of the License at\n ~\n ~ https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n ~\n ~ Unless required by applicable law or agreed to in writing, software\n ~ distributed under the License is distributed on an \"AS IS\" basis,\n ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n ~ See the License for the specific language governing permissions and\n ~ limitations under the License.\n -->\n\n<div\n *ngIf=\"{\n filteredModel: filteredModel$ | async,\n editorDisabled: editorDisabled$ | async,\n permissionSchema: permissionSchema$ | async,\n } as obs\"\n class=\"json-editor-tab\"\n>\n <div class=\"json-editor-tab__toolbar\">\n <v-overflow-menu placement=\"bottom-end\" [closeOnSelect]=\"false\">\n <button\n overflowTrigger\n cdsButton=\"ghost\"\n class=\"json-editor-tab__filter-trigger\"\n [class.json-editor-tab__filter-trigger--active]=\"isFilterActive$ | async\"\n [iconOnly]=\"true\"\n >\n <svg cdsIcon=\"filter\" size=\"16\"></svg>\n </button>\n\n <div overflowPane>\n <ng-container *ngTemplateOutlet=\"filterPane\"></ng-container>\n </div>\n </v-overflow-menu>\n </div>\n\n <valtimo-editor\n *ngIf=\"obs.permissionSchema\"\n [disabled]=\"obs.editorDisabled || disabled\"\n [fitPage]=\"true\"\n [jsonSchema]=\"obs.permissionSchema\"\n [model]=\"obs.filteredModel\"\n (validEvent)=\"onValid($event)\"\n (valueChangeEvent)=\"onValueChange($event)\"\n ></valtimo-editor>\n</div>\n\n<ng-template #filterPane>\n <form class=\"json-editor-tab__filter\" (click)=\"$event.stopImmediatePropagation()\">\n <cds-dropdown\n [appendInline]=\"true\"\n [label]=\"'accessControl.filter.resourceType' | translate\"\n [placeholder]=\"'accessControl.filter.allResourceTypes' | translate\"\n (selected)=\"onResourceTypeSelected($event)\"\n >\n <cds-dropdown-list [items]=\"resourceTypeItems$ | async\"></cds-dropdown-list>\n </cds-dropdown>\n\n <cds-dropdown\n [appendInline]=\"true\"\n [label]=\"'accessControl.filter.action' | translate\"\n [placeholder]=\"'accessControl.filter.allActions' | translate\"\n (selected)=\"onActionSelected($event)\"\n >\n <cds-dropdown-list [items]=\"actionItems$ | async\"></cds-dropdown-list>\n </cds-dropdown>\n\n <button cdsButton=\"tertiary\" type=\"button\" (click)=\"onClearFilter()\">\n {{ 'interface.clear' | translate }}\n\n <svg class=\"cds--btn__icon\" cdsIcon=\"trash-can\" size=\"16\"></svg>\n </button>\n </form>\n</ng-template>\n", styles: [":host{display:block;height:100%}.json-editor-tab{display:flex;flex-direction:column;height:100%}.json-editor-tab__toolbar{display:flex;justify-content:flex-end;padding:.25rem 0}.json-editor-tab__filter-trigger{position:relative}.json-editor-tab__filter-trigger--active:after{content:\"\";position:absolute;top:.375rem;right:.375rem;width:.5rem;height:.5rem;border-radius:50%;background-color:var(--cds-support-info, #0f62fe)}.json-editor-tab__filter{display:flex;flex-direction:column;gap:1rem;padding:1rem;min-width:16rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i4.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "pipe", type: i4.AsyncPipe, name: "async" }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i4$1.TranslatePipe, name: "translate" }, { kind: "ngmodule", type: ButtonModule }, { kind: "directive", type: i2$1.Button, selector: "[cdsButton], [ibmButton]", inputs: ["ibmButton", "cdsButton", "size", "skeleton", "iconOnly", "isExpressive"] }, { kind: "ngmodule", type: DropdownModule }, { kind: "component", type: i2$1.Dropdown, selector: "cds-dropdown, ibm-dropdown", inputs: ["id", "label", "hideLabel", "helperText", "placeholder", "displayValue", "clearText", "size", "type", "theme", "disabled", "readonly", "skeleton", "inline", "disableArrowKeys", "invalid", "invalidText", "warn", "warnText", "appendInline", "scrollableContainer", "itemValueKey", "selectionFeedback", "menuButtonLabel", "selectedLabel", "dropUp", "fluid"], outputs: ["selected", "onClose", "close"] }, { kind: "component", type: i2$1.DropdownList, selector: "cds-dropdown-list, ibm-dropdown-list", inputs: ["ariaLabel", "items", "listTpl", "type", "showTitles"], outputs: ["select", "scroll", "blurIntent"] }, { kind: "ngmodule", type: EditorModule }, { kind: "component", type: i3.EditorComponent, selector: "valtimo-editor", inputs: ["editorOptions", "model", "disabled", "formatOnLoad", "widthPx", "heightPx", "heightStyle", "jsonSchema", "fitPage", "fitPageSpaceAdjustment"], outputs: ["validEvent", "valueChangeEvent"] }, { kind: "ngmodule", type: IconModule }, { kind: "directive", type: i2$1.IconDirective, selector: "[cdsIcon], [ibmIcon]", inputs: ["ibmIcon", "cdsIcon", "size", "title", "ariaLabel", "ariaLabelledBy", "ariaHidden", "isFocusable"] }, { kind: "component", type: OverflowMenuComponent, selector: "v-overflow-menu", inputs: ["open", "placement", "menuWidth", "offsetX", "offsetY", "closeOnSelect", "useHostAsReference", "portalToBody"], outputs: ["openChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
895
|
+
}
|
|
896
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: AccessControlJsonEditorTabComponent, decorators: [{
|
|
897
|
+
type: Component,
|
|
898
|
+
args: [{ standalone: true, selector: 'valtimo-access-control-json-editor-tab', changeDetection: ChangeDetectionStrategy.OnPush, imports: [
|
|
899
|
+
CommonModule,
|
|
900
|
+
TranslateModule,
|
|
901
|
+
ButtonModule,
|
|
902
|
+
DropdownModule,
|
|
903
|
+
EditorModule,
|
|
904
|
+
IconModule,
|
|
905
|
+
OverflowMenuComponent,
|
|
906
|
+
], template: "<!--\n ~ Copyright 2015-2026 Ritense BV, the Netherlands.\n ~\n ~ Licensed under EUPL, Version 1.2 (the \"License\");\n ~ you may not use this file except in compliance with the License.\n ~ You may obtain a copy of the License at\n ~\n ~ https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n ~\n ~ Unless required by applicable law or agreed to in writing, software\n ~ distributed under the License is distributed on an \"AS IS\" basis,\n ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n ~ See the License for the specific language governing permissions and\n ~ limitations under the License.\n -->\n\n<div\n *ngIf=\"{\n filteredModel: filteredModel$ | async,\n editorDisabled: editorDisabled$ | async,\n permissionSchema: permissionSchema$ | async,\n } as obs\"\n class=\"json-editor-tab\"\n>\n <div class=\"json-editor-tab__toolbar\">\n <v-overflow-menu placement=\"bottom-end\" [closeOnSelect]=\"false\">\n <button\n overflowTrigger\n cdsButton=\"ghost\"\n class=\"json-editor-tab__filter-trigger\"\n [class.json-editor-tab__filter-trigger--active]=\"isFilterActive$ | async\"\n [iconOnly]=\"true\"\n >\n <svg cdsIcon=\"filter\" size=\"16\"></svg>\n </button>\n\n <div overflowPane>\n <ng-container *ngTemplateOutlet=\"filterPane\"></ng-container>\n </div>\n </v-overflow-menu>\n </div>\n\n <valtimo-editor\n *ngIf=\"obs.permissionSchema\"\n [disabled]=\"obs.editorDisabled || disabled\"\n [fitPage]=\"true\"\n [jsonSchema]=\"obs.permissionSchema\"\n [model]=\"obs.filteredModel\"\n (validEvent)=\"onValid($event)\"\n (valueChangeEvent)=\"onValueChange($event)\"\n ></valtimo-editor>\n</div>\n\n<ng-template #filterPane>\n <form class=\"json-editor-tab__filter\" (click)=\"$event.stopImmediatePropagation()\">\n <cds-dropdown\n [appendInline]=\"true\"\n [label]=\"'accessControl.filter.resourceType' | translate\"\n [placeholder]=\"'accessControl.filter.allResourceTypes' | translate\"\n (selected)=\"onResourceTypeSelected($event)\"\n >\n <cds-dropdown-list [items]=\"resourceTypeItems$ | async\"></cds-dropdown-list>\n </cds-dropdown>\n\n <cds-dropdown\n [appendInline]=\"true\"\n [label]=\"'accessControl.filter.action' | translate\"\n [placeholder]=\"'accessControl.filter.allActions' | translate\"\n (selected)=\"onActionSelected($event)\"\n >\n <cds-dropdown-list [items]=\"actionItems$ | async\"></cds-dropdown-list>\n </cds-dropdown>\n\n <button cdsButton=\"tertiary\" type=\"button\" (click)=\"onClearFilter()\">\n {{ 'interface.clear' | translate }}\n\n <svg class=\"cds--btn__icon\" cdsIcon=\"trash-can\" size=\"16\"></svg>\n </button>\n </form>\n</ng-template>\n", styles: [":host{display:block;height:100%}.json-editor-tab{display:flex;flex-direction:column;height:100%}.json-editor-tab__toolbar{display:flex;justify-content:flex-end;padding:.25rem 0}.json-editor-tab__filter-trigger{position:relative}.json-editor-tab__filter-trigger--active:after{content:\"\";position:absolute;top:.375rem;right:.375rem;width:.5rem;height:.5rem;border-radius:50%;background-color:var(--cds-support-info, #0f62fe)}.json-editor-tab__filter{display:flex;flex-direction:column;gap:1rem;padding:1rem;min-width:16rem}\n"] }]
|
|
907
|
+
}], ctorParameters: () => [{ type: i1$2.ActivatedRoute }, { type: i2$1.IconService }, { type: PermissionSchemaMetadataService }, { type: i1$2.Router }, { type: i4$1.TranslateService }], propDecorators: { disabled: [{
|
|
908
|
+
type: Input
|
|
909
|
+
}], model: [{
|
|
910
|
+
type: Input
|
|
911
|
+
}], validEvent: [{
|
|
912
|
+
type: Output
|
|
913
|
+
}], valueChangeEvent: [{
|
|
914
|
+
type: Output
|
|
915
|
+
}] } });
|
|
916
|
+
|
|
917
|
+
/*
|
|
918
|
+
* Copyright 2015-2026 Ritense BV, the Netherlands.
|
|
919
|
+
*
|
|
920
|
+
* Licensed under EUPL, Version 1.2 (the "License");
|
|
921
|
+
* you may not use this file except in compliance with the License.
|
|
922
|
+
* You may obtain a copy of the License at
|
|
923
|
+
*
|
|
924
|
+
* https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
|
|
925
|
+
*
|
|
926
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
927
|
+
* distributed under the License is distributed on an "AS IS" basis,
|
|
928
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
929
|
+
* See the License for the specific language governing permissions and
|
|
930
|
+
* limitations under the License.
|
|
931
|
+
*/
|
|
932
|
+
class ResourceTypeLabelPipe {
|
|
933
|
+
constructor(translateService) {
|
|
934
|
+
this.translateService = translateService;
|
|
935
|
+
}
|
|
936
|
+
transform(fqn) {
|
|
937
|
+
if (!fqn)
|
|
938
|
+
return '';
|
|
939
|
+
return formatResourceType(this.translateService, fqn);
|
|
940
|
+
}
|
|
941
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: ResourceTypeLabelPipe, deps: [{ token: i4$1.TranslateService }], target: i0.ɵɵFactoryTarget.Pipe }); }
|
|
942
|
+
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.20", ngImport: i0, type: ResourceTypeLabelPipe, isStandalone: true, name: "resourceTypeLabel" }); }
|
|
943
|
+
}
|
|
944
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: ResourceTypeLabelPipe, decorators: [{
|
|
945
|
+
type: Pipe,
|
|
946
|
+
args: [{
|
|
947
|
+
standalone: true,
|
|
948
|
+
name: 'resourceTypeLabel',
|
|
949
|
+
}]
|
|
950
|
+
}], ctorParameters: () => [{ type: i4$1.TranslateService }] });
|
|
951
|
+
|
|
952
|
+
/*
|
|
953
|
+
* Copyright 2015-2026 Ritense BV, the Netherlands.
|
|
954
|
+
*
|
|
955
|
+
* Licensed under EUPL, Version 1.2 (the "License");
|
|
956
|
+
* you may not use this file except in compliance with the License.
|
|
957
|
+
* You may obtain a copy of the License at
|
|
958
|
+
*
|
|
959
|
+
* https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
|
|
960
|
+
*
|
|
961
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
962
|
+
* distributed under the License is distributed on an "AS IS" basis,
|
|
963
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
964
|
+
* See the License for the specific language governing permissions and
|
|
965
|
+
* limitations under the License.
|
|
966
|
+
*/
|
|
967
|
+
|
|
968
|
+
/*
|
|
969
|
+
* Copyright 2015-2026 Ritense BV, the Netherlands.
|
|
970
|
+
*
|
|
971
|
+
* Licensed under EUPL, Version 1.2 (the "License");
|
|
972
|
+
* you may not use this file except in compliance with the License.
|
|
973
|
+
* You may obtain a copy of the License at
|
|
974
|
+
*
|
|
975
|
+
* https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
|
|
976
|
+
*
|
|
977
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
978
|
+
* distributed under the License is distributed on an "AS IS" basis,
|
|
979
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
980
|
+
* See the License for the specific language governing permissions and
|
|
981
|
+
* limitations under the License.
|
|
982
|
+
*/
|
|
983
|
+
class AccessControlOverviewTabComponent {
|
|
984
|
+
set permissions(value) {
|
|
985
|
+
this._permissions$.next(value ?? []);
|
|
986
|
+
}
|
|
987
|
+
constructor(metadataService, translateService) {
|
|
988
|
+
this.metadataService = metadataService;
|
|
989
|
+
this.translateService = translateService;
|
|
990
|
+
this.roleKey = null;
|
|
991
|
+
this._permissions$ = new BehaviorSubject([]);
|
|
992
|
+
this.overview$ = combineLatest([
|
|
993
|
+
this._permissions$,
|
|
994
|
+
this.metadataService.allResourceTypes$,
|
|
995
|
+
this.metadataService.actionsByResourceType$,
|
|
996
|
+
]).pipe(map(([permissions, allResourceTypes, actionsByResourceType]) => this.buildOverview(permissions, allResourceTypes, actionsByResourceType)));
|
|
997
|
+
}
|
|
998
|
+
formatConditions(conditions, resourceType) {
|
|
999
|
+
return (conditions ?? []).map(condition => this.formatCondition(condition, resourceType));
|
|
1000
|
+
}
|
|
1001
|
+
isNoContext(contextResourceType) {
|
|
1002
|
+
return contextResourceType === NO_CONTEXT_RESOURCE_TYPE;
|
|
1003
|
+
}
|
|
1004
|
+
buildOverview(permissions, allResourceTypes, actionsByResourceType) {
|
|
1005
|
+
const knownResourceTypes = new Set(allResourceTypes);
|
|
1006
|
+
const extraResourceTypes = permissions
|
|
1007
|
+
.map(p => p.resourceType)
|
|
1008
|
+
.filter(rt => !!rt && !knownResourceTypes.has(rt));
|
|
1009
|
+
const resourceTypes = [...allResourceTypes, ...Array.from(new Set(extraResourceTypes))];
|
|
1010
|
+
return resourceTypes.map(resourceType => {
|
|
1011
|
+
const forResource = permissions.filter(p => p.resourceType === resourceType);
|
|
1012
|
+
const allowedActions = actionsByResourceType[resourceType] ?? [];
|
|
1013
|
+
const allowedSet = new Set(allowedActions);
|
|
1014
|
+
const extraActions = forResource
|
|
1015
|
+
.flatMap(p => [...(p.actions ?? []), ...(p.action ? [p.action] : [])])
|
|
1016
|
+
.filter(a => !!a && !allowedSet.has(a));
|
|
1017
|
+
const actions = [...allowedActions, ...Array.from(new Set(extraActions))];
|
|
1018
|
+
return {
|
|
1019
|
+
resourceType,
|
|
1020
|
+
actions: actions.map(action => ({
|
|
1021
|
+
action,
|
|
1022
|
+
grants: forResource.filter(p => this.permissionGrants(p, action)),
|
|
1023
|
+
})),
|
|
1024
|
+
};
|
|
1025
|
+
});
|
|
1026
|
+
}
|
|
1027
|
+
permissionGrants(permission, action) {
|
|
1028
|
+
if (permission.action === action)
|
|
1029
|
+
return true;
|
|
1030
|
+
return (permission.actions ?? []).includes(action);
|
|
1031
|
+
}
|
|
1032
|
+
formatCondition(condition, resourceType) {
|
|
1033
|
+
if (condition.type === 'container') {
|
|
1034
|
+
return this.formatContainer(condition);
|
|
1035
|
+
}
|
|
1036
|
+
if (condition.type === 'expression') {
|
|
1037
|
+
return this.formatExpression(condition, resourceType);
|
|
1038
|
+
}
|
|
1039
|
+
return this.formatField(condition, resourceType);
|
|
1040
|
+
}
|
|
1041
|
+
formatField(condition, resourceType) {
|
|
1042
|
+
return {
|
|
1043
|
+
kind: 'field',
|
|
1044
|
+
field: formatField(this.translateService, resourceType, condition.field),
|
|
1045
|
+
operator: formatOperator(this.translateService, condition.operator),
|
|
1046
|
+
value: formatValue(this.translateService, condition.value),
|
|
1047
|
+
customField: !this.metadataService.isFieldKnown(resourceType, condition.field),
|
|
1048
|
+
};
|
|
1049
|
+
}
|
|
1050
|
+
formatExpression(condition, resourceType) {
|
|
1051
|
+
return {
|
|
1052
|
+
kind: 'expression',
|
|
1053
|
+
field: formatField(this.translateService, resourceType, condition.field),
|
|
1054
|
+
path: condition.path,
|
|
1055
|
+
operator: formatOperator(this.translateService, condition.operator),
|
|
1056
|
+
value: formatValue(this.translateService, condition.value),
|
|
1057
|
+
clazz: condition.clazz,
|
|
1058
|
+
customField: !this.metadataService.isFieldKnown(resourceType, condition.field),
|
|
1059
|
+
};
|
|
1060
|
+
}
|
|
1061
|
+
formatContainer(condition) {
|
|
1062
|
+
return {
|
|
1063
|
+
kind: 'container',
|
|
1064
|
+
resourceType: condition.resourceType,
|
|
1065
|
+
conditions: condition.conditions ?? [],
|
|
1066
|
+
customResource: !this.metadataService.isResourceTypeKnown(condition.resourceType),
|
|
1067
|
+
};
|
|
1068
|
+
}
|
|
1069
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: AccessControlOverviewTabComponent, deps: [{ token: PermissionSchemaMetadataService }, { token: i4$1.TranslateService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1070
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.20", type: AccessControlOverviewTabComponent, isStandalone: true, selector: "valtimo-access-control-overview-tab", inputs: { roleKey: "roleKey", permissions: "permissions" }, ngImport: i0, template: "<!--\n ~ Copyright 2015-2026 Ritense BV, the Netherlands.\n ~\n ~ Licensed under EUPL, Version 1.2 (the \"License\");\n ~ you may not use this file except in compliance with the License.\n ~ You may obtain a copy of the License at\n ~\n ~ https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n ~\n ~ Unless required by applicable law or agreed to in writing, software\n ~ distributed under the License is distributed on an \"AS IS\" basis,\n ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n ~ See the License for the specific language governing permissions and\n ~ limitations under the License.\n -->\n\n<div *ngIf=\"overview$ | async as overview\" class=\"ac\">\n @for (resource of overview; track resource.resourceType) {\n <section class=\"ac__group\">\n <h4 class=\"ac__heading\">\n @if (roleKey) {\n <a\n class=\"ac__heading-link\"\n [routerLink]=\"['/access-control', roleKey, 'json-editor']\"\n [queryParams]=\"{filterResourceType: resource.resourceType}\"\n >\n {{ resource.resourceType | resourceTypeLabel }}\n <span class=\"ac__fqn\">({{ resource.resourceType }})</span>\n </a>\n } @else {\n {{ resource.resourceType | resourceTypeLabel }}\n <span class=\"ac__fqn\">({{ resource.resourceType }})</span>\n }\n </h4>\n\n @for (entry of resource.actions; track entry.action) {\n <div class=\"ac__action\">\n @if (entry.grants.length) {\n <span class=\"ac__connective\">{{ 'accessControl.overview.can' | translate }}</span>\n @if (roleKey) {\n <a\n class=\"ac__action-link\"\n [routerLink]=\"['/access-control', roleKey, 'json-editor']\"\n [queryParams]=\"{\n filterResourceType: resource.resourceType,\n filterAction: entry.action,\n }\"\n >\n <span class=\"ac__action-name\">{{\n 'accessControl.actions.' + entry.action | translate\n }}</span>\n </a>\n } @else {\n <span class=\"ac__action-name\">{{\n 'accessControl.actions.' + entry.action | translate\n }}</span>\n }\n\n @for (perm of entry.grants; track $index; let isFirst = $first) {\n @if (!isFirst) {\n <div class=\"ac__or ac__connective\">{{\n 'accessControl.overview.or' | translate\n }}</div>\n }\n <div class=\"ac__grant\">\n @if (perm.conditions?.length) {\n <div class=\"ac__connective\">{{\n 'accessControl.overview.when' | translate\n }}</div>\n <ng-container\n *ngTemplateOutlet=\"\n conditionsTpl;\n context: {\n conditions: perm.conditions,\n resourceType: perm.resourceType,\n }\n \"\n ></ng-container>\n } @else if (!perm.contextResourceType) {\n <div class=\"ac__connective\">{{\n 'accessControl.overview.unconditional' | translate\n }}</div>\n }\n\n @if (perm.contextResourceType) {\n @if (isNoContext(perm.contextResourceType)) {\n <div class=\"ac__connective\">{{\n 'accessControl.overview.noContext' | translate\n }}</div>\n } @else {\n <div>\n <span class=\"ac__connective\">{{\n 'accessControl.overview.inContextOf' | translate\n }}</span>\n <span class=\"ac__resource-type\">{{\n perm.contextResourceType | resourceTypeLabel\n }}</span>\n @if (perm.contextConditions?.length) {\n <span class=\"ac__connective\">{{\n 'accessControl.overview.where' | translate\n }}</span>\n }\n </div>\n @if (perm.contextConditions?.length) {\n <ng-container\n *ngTemplateOutlet=\"\n conditionsTpl;\n context: {\n conditions: perm.contextConditions,\n resourceType: perm.contextResourceType,\n }\n \"\n ></ng-container>\n }\n }\n }\n </div>\n }\n } @else {\n <span class=\"ac__no-access\">{{\n 'accessControl.overview.cannot' | translate\n }}</span>\n @if (roleKey) {\n <a\n class=\"ac__action-link\"\n [routerLink]=\"['/access-control', roleKey, 'json-editor']\"\n [queryParams]=\"{\n filterResourceType: resource.resourceType,\n filterAction: entry.action,\n }\"\n >\n <span class=\"ac__action-name\">{{\n 'accessControl.actions.' + entry.action | translate\n }}</span>\n </a>\n } @else {\n <span class=\"ac__action-name\">{{\n 'accessControl.actions.' + entry.action | translate\n }}</span>\n }\n }\n </div>\n }\n </section>\n }\n</div>\n\n<ng-template #conditionsTpl let-conditions=\"conditions\" let-resourceType=\"resourceType\">\n <div class=\"ac__conditions\">\n @for (\n condition of formatConditions(conditions, resourceType);\n track $index;\n let isFirst = $first\n ) {\n <div class=\"ac__condition\">\n @if (!isFirst) {\n <span class=\"ac__connective\">{{ 'accessControl.overview.and' | translate }}</span>\n }\n @switch (condition.kind) {\n @case ('field') {\n <span>{{ condition.field }}</span>\n @if (condition.customField) {\n <span class=\"ac__connective\">{{\n 'accessControl.overview.customField' | translate\n }}</span>\n }\n <span class=\"ac__operator\">{{ condition.operator }}</span>\n <span>{{ condition.value }}</span>\n }\n\n @case ('expression') {\n <span>{{ condition.field }}</span>\n @if (condition.customField) {\n <span class=\"ac__connective\">{{\n 'accessControl.overview.customField' | translate\n }}</span>\n }\n <span class=\"ac__connective\">{{\n 'accessControl.overview.atJsonPath' | translate\n }}</span>\n <code class=\"ac__path\" [title]=\"condition.clazz\">{{ condition.path }}</code>\n <span class=\"ac__operator\">{{ condition.operator }}</span>\n <span>{{ condition.value }}</span>\n }\n\n @case ('container') {\n <span class=\"ac__connective\">{{\n 'accessControl.overview.relatedResource' | translate\n }}</span>\n <span class=\"ac__resource-type\">{{\n condition.resourceType | resourceTypeLabel\n }}</span>\n @if (condition.customResource) {\n <span class=\"ac__connective\">{{\n 'accessControl.overview.customResource' | translate\n }}</span>\n }\n <span class=\"ac__connective\">{{\n 'accessControl.overview.where' | translate\n }}</span>\n <ng-container\n *ngTemplateOutlet=\"\n conditionsTpl;\n context: {\n conditions: condition.conditions,\n resourceType: condition.resourceType,\n }\n \"\n ></ng-container>\n }\n }\n </div>\n }\n </div>\n</ng-template>\n", styles: [".ac{display:flex;flex-direction:column;gap:2rem;padding:1rem}.ac__group{display:flex;flex-direction:column;gap:.75rem}.ac__heading{border-bottom:1px solid var(--cds-border-subtle, #c6c6c6);margin:0 0 .5rem;padding-bottom:.25rem}.ac__heading-link{color:inherit;text-decoration:none}.ac__heading-link:hover,.ac__heading-link:focus-visible{text-decoration:underline}.ac__action-link{color:inherit;text-decoration:none}.ac__action-link:hover,.ac__action-link:focus-visible{text-decoration:underline}.ac__fqn{color:var(--cds-text-secondary, #525252);font-family:var(--cds-code-01-font-family, monospace);font-size:.875rem;font-weight:400;margin-left:.5rem}.ac__action{line-height:1.6}.ac__action-name,.ac__resource-type{font-weight:600}.ac__operator{font-style:italic}.ac__connective{color:var(--cds-text-secondary, #525252);font-style:italic}.ac__no-access{color:var(--cds-text-error, #da1e28);font-style:italic}.ac__grant,.ac__or,.ac__conditions{padding-left:1.5rem}.ac__path{background-color:var(--cds-layer-accent, #e0e0e0);border-radius:2px;font-family:var(--cds-code-01-font-family, monospace);padding:0 .25rem}\n/*!\n * Copyright 2015-2026 Ritense BV, the Netherlands.\n *\n * Licensed under EUPL, Version 1.2 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i4.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "pipe", type: i4.AsyncPipe, name: "async" }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1$2.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i4$1.TranslatePipe, name: "translate" }, { kind: "pipe", type: ResourceTypeLabelPipe, name: "resourceTypeLabel" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, preserveWhitespaces: true }); }
|
|
1071
|
+
}
|
|
1072
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: AccessControlOverviewTabComponent, decorators: [{
|
|
1073
|
+
type: Component,
|
|
1074
|
+
args: [{ standalone: true, selector: 'valtimo-access-control-overview-tab', changeDetection: ChangeDetectionStrategy.OnPush, preserveWhitespaces: true, imports: [CommonModule, RouterModule, TranslateModule, ResourceTypeLabelPipe], template: "<!--\n ~ Copyright 2015-2026 Ritense BV, the Netherlands.\n ~\n ~ Licensed under EUPL, Version 1.2 (the \"License\");\n ~ you may not use this file except in compliance with the License.\n ~ You may obtain a copy of the License at\n ~\n ~ https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n ~\n ~ Unless required by applicable law or agreed to in writing, software\n ~ distributed under the License is distributed on an \"AS IS\" basis,\n ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n ~ See the License for the specific language governing permissions and\n ~ limitations under the License.\n -->\n\n<div *ngIf=\"overview$ | async as overview\" class=\"ac\">\n @for (resource of overview; track resource.resourceType) {\n <section class=\"ac__group\">\n <h4 class=\"ac__heading\">\n @if (roleKey) {\n <a\n class=\"ac__heading-link\"\n [routerLink]=\"['/access-control', roleKey, 'json-editor']\"\n [queryParams]=\"{filterResourceType: resource.resourceType}\"\n >\n {{ resource.resourceType | resourceTypeLabel }}\n <span class=\"ac__fqn\">({{ resource.resourceType }})</span>\n </a>\n } @else {\n {{ resource.resourceType | resourceTypeLabel }}\n <span class=\"ac__fqn\">({{ resource.resourceType }})</span>\n }\n </h4>\n\n @for (entry of resource.actions; track entry.action) {\n <div class=\"ac__action\">\n @if (entry.grants.length) {\n <span class=\"ac__connective\">{{ 'accessControl.overview.can' | translate }}</span>\n @if (roleKey) {\n <a\n class=\"ac__action-link\"\n [routerLink]=\"['/access-control', roleKey, 'json-editor']\"\n [queryParams]=\"{\n filterResourceType: resource.resourceType,\n filterAction: entry.action,\n }\"\n >\n <span class=\"ac__action-name\">{{\n 'accessControl.actions.' + entry.action | translate\n }}</span>\n </a>\n } @else {\n <span class=\"ac__action-name\">{{\n 'accessControl.actions.' + entry.action | translate\n }}</span>\n }\n\n @for (perm of entry.grants; track $index; let isFirst = $first) {\n @if (!isFirst) {\n <div class=\"ac__or ac__connective\">{{\n 'accessControl.overview.or' | translate\n }}</div>\n }\n <div class=\"ac__grant\">\n @if (perm.conditions?.length) {\n <div class=\"ac__connective\">{{\n 'accessControl.overview.when' | translate\n }}</div>\n <ng-container\n *ngTemplateOutlet=\"\n conditionsTpl;\n context: {\n conditions: perm.conditions,\n resourceType: perm.resourceType,\n }\n \"\n ></ng-container>\n } @else if (!perm.contextResourceType) {\n <div class=\"ac__connective\">{{\n 'accessControl.overview.unconditional' | translate\n }}</div>\n }\n\n @if (perm.contextResourceType) {\n @if (isNoContext(perm.contextResourceType)) {\n <div class=\"ac__connective\">{{\n 'accessControl.overview.noContext' | translate\n }}</div>\n } @else {\n <div>\n <span class=\"ac__connective\">{{\n 'accessControl.overview.inContextOf' | translate\n }}</span>\n <span class=\"ac__resource-type\">{{\n perm.contextResourceType | resourceTypeLabel\n }}</span>\n @if (perm.contextConditions?.length) {\n <span class=\"ac__connective\">{{\n 'accessControl.overview.where' | translate\n }}</span>\n }\n </div>\n @if (perm.contextConditions?.length) {\n <ng-container\n *ngTemplateOutlet=\"\n conditionsTpl;\n context: {\n conditions: perm.contextConditions,\n resourceType: perm.contextResourceType,\n }\n \"\n ></ng-container>\n }\n }\n }\n </div>\n }\n } @else {\n <span class=\"ac__no-access\">{{\n 'accessControl.overview.cannot' | translate\n }}</span>\n @if (roleKey) {\n <a\n class=\"ac__action-link\"\n [routerLink]=\"['/access-control', roleKey, 'json-editor']\"\n [queryParams]=\"{\n filterResourceType: resource.resourceType,\n filterAction: entry.action,\n }\"\n >\n <span class=\"ac__action-name\">{{\n 'accessControl.actions.' + entry.action | translate\n }}</span>\n </a>\n } @else {\n <span class=\"ac__action-name\">{{\n 'accessControl.actions.' + entry.action | translate\n }}</span>\n }\n }\n </div>\n }\n </section>\n }\n</div>\n\n<ng-template #conditionsTpl let-conditions=\"conditions\" let-resourceType=\"resourceType\">\n <div class=\"ac__conditions\">\n @for (\n condition of formatConditions(conditions, resourceType);\n track $index;\n let isFirst = $first\n ) {\n <div class=\"ac__condition\">\n @if (!isFirst) {\n <span class=\"ac__connective\">{{ 'accessControl.overview.and' | translate }}</span>\n }\n @switch (condition.kind) {\n @case ('field') {\n <span>{{ condition.field }}</span>\n @if (condition.customField) {\n <span class=\"ac__connective\">{{\n 'accessControl.overview.customField' | translate\n }}</span>\n }\n <span class=\"ac__operator\">{{ condition.operator }}</span>\n <span>{{ condition.value }}</span>\n }\n\n @case ('expression') {\n <span>{{ condition.field }}</span>\n @if (condition.customField) {\n <span class=\"ac__connective\">{{\n 'accessControl.overview.customField' | translate\n }}</span>\n }\n <span class=\"ac__connective\">{{\n 'accessControl.overview.atJsonPath' | translate\n }}</span>\n <code class=\"ac__path\" [title]=\"condition.clazz\">{{ condition.path }}</code>\n <span class=\"ac__operator\">{{ condition.operator }}</span>\n <span>{{ condition.value }}</span>\n }\n\n @case ('container') {\n <span class=\"ac__connective\">{{\n 'accessControl.overview.relatedResource' | translate\n }}</span>\n <span class=\"ac__resource-type\">{{\n condition.resourceType | resourceTypeLabel\n }}</span>\n @if (condition.customResource) {\n <span class=\"ac__connective\">{{\n 'accessControl.overview.customResource' | translate\n }}</span>\n }\n <span class=\"ac__connective\">{{\n 'accessControl.overview.where' | translate\n }}</span>\n <ng-container\n *ngTemplateOutlet=\"\n conditionsTpl;\n context: {\n conditions: condition.conditions,\n resourceType: condition.resourceType,\n }\n \"\n ></ng-container>\n }\n }\n </div>\n }\n </div>\n</ng-template>\n", styles: [".ac{display:flex;flex-direction:column;gap:2rem;padding:1rem}.ac__group{display:flex;flex-direction:column;gap:.75rem}.ac__heading{border-bottom:1px solid var(--cds-border-subtle, #c6c6c6);margin:0 0 .5rem;padding-bottom:.25rem}.ac__heading-link{color:inherit;text-decoration:none}.ac__heading-link:hover,.ac__heading-link:focus-visible{text-decoration:underline}.ac__action-link{color:inherit;text-decoration:none}.ac__action-link:hover,.ac__action-link:focus-visible{text-decoration:underline}.ac__fqn{color:var(--cds-text-secondary, #525252);font-family:var(--cds-code-01-font-family, monospace);font-size:.875rem;font-weight:400;margin-left:.5rem}.ac__action{line-height:1.6}.ac__action-name,.ac__resource-type{font-weight:600}.ac__operator{font-style:italic}.ac__connective{color:var(--cds-text-secondary, #525252);font-style:italic}.ac__no-access{color:var(--cds-text-error, #da1e28);font-style:italic}.ac__grant,.ac__or,.ac__conditions{padding-left:1.5rem}.ac__path{background-color:var(--cds-layer-accent, #e0e0e0);border-radius:2px;font-family:var(--cds-code-01-font-family, monospace);padding:0 .25rem}\n/*!\n * Copyright 2015-2026 Ritense BV, the Netherlands.\n *\n * Licensed under EUPL, Version 1.2 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n"] }]
|
|
1075
|
+
}], ctorParameters: () => [{ type: PermissionSchemaMetadataService }, { type: i4$1.TranslateService }], propDecorators: { roleKey: [{
|
|
1076
|
+
type: Input
|
|
1077
|
+
}], permissions: [{
|
|
1078
|
+
type: Input
|
|
1079
|
+
}] } });
|
|
1080
|
+
|
|
465
1081
|
/*
|
|
466
1082
|
* Copyright 2015-2025 Ritense BV, the Netherlands.
|
|
467
1083
|
*
|
|
@@ -488,6 +1104,8 @@ class AccessControlEditorComponent {
|
|
|
488
1104
|
this.accessControlExportService = accessControlExportService;
|
|
489
1105
|
this.pageHeaderService = pageHeaderService;
|
|
490
1106
|
this.model$ = new BehaviorSubject(null);
|
|
1107
|
+
this.permissions$ = new BehaviorSubject(null);
|
|
1108
|
+
this.roleKey$ = new BehaviorSubject(null);
|
|
491
1109
|
this.saveDisabled$ = new BehaviorSubject(true);
|
|
492
1110
|
this.editorDisabled$ = new BehaviorSubject(false);
|
|
493
1111
|
this.moreDisabled$ = new BehaviorSubject(true);
|
|
@@ -495,9 +1113,12 @@ class AccessControlEditorComponent {
|
|
|
495
1113
|
this.showEditModal$ = new BehaviorSubject(false);
|
|
496
1114
|
this.selectedRowKeys$ = new BehaviorSubject(null);
|
|
497
1115
|
this.compactMode$ = this.pageHeaderService.compactMode$;
|
|
1116
|
+
this.$activeTab = signal(AccessControlEditorTab.SUMMARY);
|
|
1117
|
+
this.AccessControlEditorTab = AccessControlEditorTab;
|
|
498
1118
|
this._updatedModelValue$ = new BehaviorSubject('');
|
|
499
1119
|
}
|
|
500
1120
|
ngOnInit() {
|
|
1121
|
+
this.restoreActiveTabFromUrl();
|
|
501
1122
|
this.getPermissions();
|
|
502
1123
|
this.openRoleKeySubscription();
|
|
503
1124
|
}
|
|
@@ -559,6 +1180,31 @@ class AccessControlEditorComponent {
|
|
|
559
1180
|
this.showSuccessMessage(data.roleKey);
|
|
560
1181
|
});
|
|
561
1182
|
}
|
|
1183
|
+
setActiveTab(tab) {
|
|
1184
|
+
if (this.$activeTab() === tab)
|
|
1185
|
+
return;
|
|
1186
|
+
const roleKey = this.route.snapshot.paramMap.get('id');
|
|
1187
|
+
if (!roleKey) {
|
|
1188
|
+
this.$activeTab.set(tab);
|
|
1189
|
+
return;
|
|
1190
|
+
}
|
|
1191
|
+
const segments = tab === AccessControlEditorTab.JSON_EDITOR
|
|
1192
|
+
? ['/access-control', roleKey, 'json-editor']
|
|
1193
|
+
: ['/access-control', roleKey];
|
|
1194
|
+
this.router.navigate(segments);
|
|
1195
|
+
}
|
|
1196
|
+
restoreActiveTabFromUrl() {
|
|
1197
|
+
const url = this.route.snapshot.url;
|
|
1198
|
+
const lastSegment = url[url.length - 1]?.path;
|
|
1199
|
+
if (lastSegment === 'json-editor') {
|
|
1200
|
+
this.$activeTab.set(AccessControlEditorTab.JSON_EDITOR);
|
|
1201
|
+
return;
|
|
1202
|
+
}
|
|
1203
|
+
const params = this.route.snapshot.queryParamMap;
|
|
1204
|
+
if (params.get('filterResourceType') || params.get('filterAction')) {
|
|
1205
|
+
this.$activeTab.set(AccessControlEditorTab.JSON_EDITOR);
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
562
1208
|
exportPermissions() {
|
|
563
1209
|
this.accessControlExportService
|
|
564
1210
|
.exportRoles({ type: 'separate', roleKeys: [this._roleKey] })
|
|
@@ -568,6 +1214,7 @@ class AccessControlEditorComponent {
|
|
|
568
1214
|
this._roleKeySubscription = this.route.params
|
|
569
1215
|
.pipe(filter(params => params?.id), map(params => params.id), tap(roleKey => {
|
|
570
1216
|
this._roleKey = roleKey;
|
|
1217
|
+
this.roleKey$.next(roleKey);
|
|
571
1218
|
this.pageTitleService.setCustomPageTitle(roleKey, true);
|
|
572
1219
|
this.selectedRowKeys$.next([roleKey]);
|
|
573
1220
|
}), switchMap(roleKey => this.accessControlService.getRolePermissions(roleKey)), tap(permissions => {
|
|
@@ -582,6 +1229,7 @@ class AccessControlEditorComponent {
|
|
|
582
1229
|
this.route.params
|
|
583
1230
|
.pipe(tap(params => {
|
|
584
1231
|
this.pageTitleService.setCustomPageTitle(params?.id);
|
|
1232
|
+
this.roleKey$.next(params?.id ?? null);
|
|
585
1233
|
this.selectedRowKeys$.next([params?.id]);
|
|
586
1234
|
}), switchMap(params => this.accessControlService.getRolePermissions(params.id)))
|
|
587
1235
|
.subscribe(permissions => {
|
|
@@ -592,10 +1240,13 @@ class AccessControlEditorComponent {
|
|
|
592
1240
|
});
|
|
593
1241
|
}
|
|
594
1242
|
setModel(permissions) {
|
|
1243
|
+
const roleKey = this.roleKey$.value ?? 'unknown';
|
|
595
1244
|
this.model$.next({
|
|
596
1245
|
value: JSON.stringify(permissions),
|
|
597
1246
|
language: 'json',
|
|
1247
|
+
uri: `inmemory://access-control/role-${roleKey}.access-control-permissions.json`,
|
|
598
1248
|
});
|
|
1249
|
+
this.permissions$.next(Array.isArray(permissions) ? permissions : null);
|
|
599
1250
|
}
|
|
600
1251
|
disableMore() {
|
|
601
1252
|
this.moreDisabled$.next(true);
|
|
@@ -624,13 +1275,13 @@ class AccessControlEditorComponent {
|
|
|
624
1275
|
type: 'success',
|
|
625
1276
|
});
|
|
626
1277
|
}
|
|
627
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: AccessControlEditorComponent, deps: [{ token: AccessControlService }, { token:
|
|
628
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.20", type: AccessControlEditorComponent, isStandalone: false, selector: "ng-component", ngImport: i0, template: "<!--\n ~ Copyright 2015-2025 Ritense BV, the Netherlands.\n ~\n ~ Licensed under EUPL, Version 1.2 (the \"License\");\n ~ you may not use this file except in compliance with the License.\n ~ You may obtain a copy of the License at\n ~\n ~ https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n ~\n ~ Unless required by applicable law or agreed to in writing, software\n ~ distributed under the License is distributed on an \"AS IS\" basis,\n ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n ~ See the License for the specific language governing permissions and\n ~ limitations under the License.\n -->\n\n<ng-container *ngIf=\"model$ | async as model; else loading\">\n <valtimo-
|
|
1278
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: AccessControlEditorComponent, deps: [{ token: AccessControlService }, { token: i1$2.ActivatedRoute }, { token: i3.PageTitleService }, { token: i1$2.Router }, { token: i1.GlobalNotificationService }, { token: i4$1.TranslateService }, { token: AccessControlExportService }, { token: i3.PageHeaderService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1279
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.20", type: AccessControlEditorComponent, isStandalone: false, selector: "ng-component", ngImport: i0, template: "<!--\n ~ Copyright 2015-2025 Ritense BV, the Netherlands.\n ~\n ~ Licensed under EUPL, Version 1.2 (the \"License\");\n ~ you may not use this file except in compliance with the License.\n ~ You may obtain a copy of the License at\n ~\n ~ https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n ~\n ~ Unless required by applicable law or agreed to in writing, software\n ~ distributed under the License is distributed on an \"AS IS\" basis,\n ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n ~ See the License for the specific language governing permissions and\n ~ limitations under the License.\n -->\n\n<ng-container *ngIf=\"model$ | async as model; else loading\">\n <cds-tabs type=\"contained\">\n <cds-tab\n [active]=\"$activeTab() === AccessControlEditorTab.SUMMARY\"\n [heading]=\"'accessControl.roles.tabs.summary' | translate\"\n (selected)=\"setActiveTab(AccessControlEditorTab.SUMMARY)\"\n >\n <valtimo-access-control-overview-tab\n *ngIf=\"$activeTab() === AccessControlEditorTab.SUMMARY\"\n [permissions]=\"permissions$ | async\"\n [roleKey]=\"roleKey$ | async\"\n ></valtimo-access-control-overview-tab>\n </cds-tab>\n\n <cds-tab\n [active]=\"$activeTab() === AccessControlEditorTab.JSON_EDITOR\"\n [heading]=\"'accessControl.roles.tabs.jsonEditor' | translate\"\n (selected)=\"setActiveTab(AccessControlEditorTab.JSON_EDITOR)\"\n >\n <valtimo-access-control-json-editor-tab\n *ngIf=\"$activeTab() === AccessControlEditorTab.JSON_EDITOR\"\n [disabled]=\"editorDisabled$ | async\"\n [model]=\"model\"\n (validEvent)=\"onValid($event)\"\n (valueChangeEvent)=\"onValueChange($event)\"\n ></valtimo-access-control-json-editor-tab>\n </cds-tab>\n </cds-tabs>\n</ng-container>\n\n<ng-container renderInPageHeader>\n <ng-template>\n <div\n *ngIf=\"{\n model: model$ | async,\n moreDisabled: moreDisabled$ | async,\n compactMode: compactMode$ | async,\n } as obs\"\n class=\"buttons-container\"\n >\n <v-overflow-menu class=\"overflow-button\" placement=\"bottom-end\">\n <v-overflow-menu-trigger overflowTrigger></v-overflow-menu-trigger>\n <v-overflow-menu-option [disabled]=\"obs.moreDisabled\" (selected)=\"showEditModal()\">{{\n 'accessControl.roles.editMetadata' | translate\n }}</v-overflow-menu-option>\n\n <v-overflow-menu-option [disabled]=\"obs.moreDisabled\" (selected)=\"exportPermissions()\">{{\n 'interface.export' | translate\n }}</v-overflow-menu-option>\n\n <v-overflow-menu-option\n [disabled]=\"obs.moreDisabled\"\n type=\"danger\"\n (selected)=\"showDeleteModal()\"\n >{{ 'interface.delete' | translate }}</v-overflow-menu-option\n >\n </v-overflow-menu>\n\n <button\n [disabled]=\"saveDisabled$ | async\"\n cdsButton=\"primary\"\n [size]=\"obs.compactMode ? 'sm' : 'md'\"\n (click)=\"updatePermissions()\"\n >\n {{ 'interface.save' | translate }}\n\n <svg class=\"cds--btn__icon\" cdsIcon=\"save\" size=\"16\"></svg>\n </button>\n </div>\n </ng-template>\n</ng-container>\n\n<ng-container *ngIf=\"{selectedRowKeys: selectedRowKeys$ | async} as obs\">\n <valtimo-delete-role-modal\n [deleteRowKeys]=\"obs.selectedRowKeys\"\n [showDeleteModal$]=\"showDeleteModal$\"\n (deleteEvent)=\"onDelete($event)\"\n >\n </valtimo-delete-role-modal>\n\n <valtimo-role-metadata-modal\n [defaultKeyValue]=\"obs.selectedRowKeys[0]\"\n [open]=\"showEditModal$ | async\"\n type=\"edit\"\n (closeEvent)=\"onEdit(obs.selectedRowKeys[0], $event)\"\n ></valtimo-role-metadata-modal>\n</ng-container>\n\n<ng-template #loading>\n <div class=\"loading-container\">\n <cds-loading></cds-loading>\n </div>\n</ng-template>\n", styles: [".loading-container{display:flex;width:100%;justify-content:center}.overflow-button{margin-right:16px}.buttons-container{display:flex;flex-direction:row;align-items:center}.buttons-container button{height:min-content}:host ::ng-deep .cds--tab-content{background-color:#fff}\n/*!\n * Copyright 2015-2025 Ritense BV, the Netherlands.\n *\n * Licensed under EUPL, Version 1.2 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n"], dependencies: [{ kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2$1.Button, selector: "[cdsButton], [ibmButton]", inputs: ["ibmButton", "cdsButton", "size", "skeleton", "iconOnly", "isExpressive"] }, { kind: "directive", type: i2$1.IconDirective, selector: "[cdsIcon], [ibmIcon]", inputs: ["ibmIcon", "cdsIcon", "size", "title", "ariaLabel", "ariaLabelledBy", "ariaHidden", "isFocusable"] }, { kind: "directive", type: i3.RenderInPageHeaderDirective, selector: "[renderInPageHeader]", inputs: ["fullWidth"] }, { kind: "component", type: i2$1.Loading, selector: "cds-loading, ibm-loading", inputs: ["title", "isActive", "size", "overlay"] }, { kind: "component", type: i3.OverflowMenuComponent, selector: "v-overflow-menu", inputs: ["open", "placement", "menuWidth", "offsetX", "offsetY", "closeOnSelect", "useHostAsReference", "portalToBody"], outputs: ["openChange"] }, { kind: "component", type: i3.OverflowMenuOptionComponent, selector: "v-overflow-menu-option", inputs: ["disabled", "type", "testId", "optionId"], outputs: ["selected"] }, { kind: "component", type: i3.OverflowMenuTriggerComponent, selector: "v-overflow-menu-trigger", inputs: ["compact"] }, { kind: "component", type: i2$1.Tabs, selector: "cds-tabs, ibm-tabs", inputs: ["position", "cacheActive", "followFocus", "isNavigation", "ariaLabel", "ariaLabelledby", "type", "theme", "skeleton"] }, { kind: "component", type: i2$1.Tab, selector: "cds-tab, ibm-tab", inputs: ["heading", "title", "context", "active", "disabled", "tabIndex", "id", "cacheActive", "tabContent", "templateContext"], outputs: ["selected"] }, { kind: "component", type: AccessControlJsonEditorTabComponent, selector: "valtimo-access-control-json-editor-tab", inputs: ["disabled", "model"], outputs: ["validEvent", "valueChangeEvent"] }, { kind: "component", type: AccessControlOverviewTabComponent, selector: "valtimo-access-control-overview-tab", inputs: ["roleKey", "permissions"] }, { kind: "component", type: RoleMetadataModalComponent, selector: "valtimo-role-metadata-modal", inputs: ["open", "type", "defaultKeyValue"], outputs: ["closeEvent"] }, { kind: "component", type: DeleteRoleModalComponent, selector: "valtimo-delete-role-modal", inputs: ["deleteRowKeys", "showDeleteModal$"], outputs: ["deleteEvent"] }, { kind: "pipe", type: i4.AsyncPipe, name: "async" }, { kind: "pipe", type: i4$1.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
629
1280
|
}
|
|
630
1281
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: AccessControlEditorComponent, decorators: [{
|
|
631
1282
|
type: Component,
|
|
632
|
-
args: [{ standalone: false, changeDetection: ChangeDetectionStrategy.OnPush, template: "<!--\n ~ Copyright 2015-2025 Ritense BV, the Netherlands.\n ~\n ~ Licensed under EUPL, Version 1.2 (the \"License\");\n ~ you may not use this file except in compliance with the License.\n ~ You may obtain a copy of the License at\n ~\n ~ https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n ~\n ~ Unless required by applicable law or agreed to in writing, software\n ~ distributed under the License is distributed on an \"AS IS\" basis,\n ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n ~ See the License for the specific language governing permissions and\n ~ limitations under the License.\n -->\n\n<ng-container *ngIf=\"model$ | async as model; else loading\">\n <valtimo-
|
|
633
|
-
}], ctorParameters: () => [{ type: AccessControlService }, { type:
|
|
1283
|
+
args: [{ standalone: false, changeDetection: ChangeDetectionStrategy.OnPush, template: "<!--\n ~ Copyright 2015-2025 Ritense BV, the Netherlands.\n ~\n ~ Licensed under EUPL, Version 1.2 (the \"License\");\n ~ you may not use this file except in compliance with the License.\n ~ You may obtain a copy of the License at\n ~\n ~ https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n ~\n ~ Unless required by applicable law or agreed to in writing, software\n ~ distributed under the License is distributed on an \"AS IS\" basis,\n ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n ~ See the License for the specific language governing permissions and\n ~ limitations under the License.\n -->\n\n<ng-container *ngIf=\"model$ | async as model; else loading\">\n <cds-tabs type=\"contained\">\n <cds-tab\n [active]=\"$activeTab() === AccessControlEditorTab.SUMMARY\"\n [heading]=\"'accessControl.roles.tabs.summary' | translate\"\n (selected)=\"setActiveTab(AccessControlEditorTab.SUMMARY)\"\n >\n <valtimo-access-control-overview-tab\n *ngIf=\"$activeTab() === AccessControlEditorTab.SUMMARY\"\n [permissions]=\"permissions$ | async\"\n [roleKey]=\"roleKey$ | async\"\n ></valtimo-access-control-overview-tab>\n </cds-tab>\n\n <cds-tab\n [active]=\"$activeTab() === AccessControlEditorTab.JSON_EDITOR\"\n [heading]=\"'accessControl.roles.tabs.jsonEditor' | translate\"\n (selected)=\"setActiveTab(AccessControlEditorTab.JSON_EDITOR)\"\n >\n <valtimo-access-control-json-editor-tab\n *ngIf=\"$activeTab() === AccessControlEditorTab.JSON_EDITOR\"\n [disabled]=\"editorDisabled$ | async\"\n [model]=\"model\"\n (validEvent)=\"onValid($event)\"\n (valueChangeEvent)=\"onValueChange($event)\"\n ></valtimo-access-control-json-editor-tab>\n </cds-tab>\n </cds-tabs>\n</ng-container>\n\n<ng-container renderInPageHeader>\n <ng-template>\n <div\n *ngIf=\"{\n model: model$ | async,\n moreDisabled: moreDisabled$ | async,\n compactMode: compactMode$ | async,\n } as obs\"\n class=\"buttons-container\"\n >\n <v-overflow-menu class=\"overflow-button\" placement=\"bottom-end\">\n <v-overflow-menu-trigger overflowTrigger></v-overflow-menu-trigger>\n <v-overflow-menu-option [disabled]=\"obs.moreDisabled\" (selected)=\"showEditModal()\">{{\n 'accessControl.roles.editMetadata' | translate\n }}</v-overflow-menu-option>\n\n <v-overflow-menu-option [disabled]=\"obs.moreDisabled\" (selected)=\"exportPermissions()\">{{\n 'interface.export' | translate\n }}</v-overflow-menu-option>\n\n <v-overflow-menu-option\n [disabled]=\"obs.moreDisabled\"\n type=\"danger\"\n (selected)=\"showDeleteModal()\"\n >{{ 'interface.delete' | translate }}</v-overflow-menu-option\n >\n </v-overflow-menu>\n\n <button\n [disabled]=\"saveDisabled$ | async\"\n cdsButton=\"primary\"\n [size]=\"obs.compactMode ? 'sm' : 'md'\"\n (click)=\"updatePermissions()\"\n >\n {{ 'interface.save' | translate }}\n\n <svg class=\"cds--btn__icon\" cdsIcon=\"save\" size=\"16\"></svg>\n </button>\n </div>\n </ng-template>\n</ng-container>\n\n<ng-container *ngIf=\"{selectedRowKeys: selectedRowKeys$ | async} as obs\">\n <valtimo-delete-role-modal\n [deleteRowKeys]=\"obs.selectedRowKeys\"\n [showDeleteModal$]=\"showDeleteModal$\"\n (deleteEvent)=\"onDelete($event)\"\n >\n </valtimo-delete-role-modal>\n\n <valtimo-role-metadata-modal\n [defaultKeyValue]=\"obs.selectedRowKeys[0]\"\n [open]=\"showEditModal$ | async\"\n type=\"edit\"\n (closeEvent)=\"onEdit(obs.selectedRowKeys[0], $event)\"\n ></valtimo-role-metadata-modal>\n</ng-container>\n\n<ng-template #loading>\n <div class=\"loading-container\">\n <cds-loading></cds-loading>\n </div>\n</ng-template>\n", styles: [".loading-container{display:flex;width:100%;justify-content:center}.overflow-button{margin-right:16px}.buttons-container{display:flex;flex-direction:row;align-items:center}.buttons-container button{height:min-content}:host ::ng-deep .cds--tab-content{background-color:#fff}\n/*!\n * Copyright 2015-2025 Ritense BV, the Netherlands.\n *\n * Licensed under EUPL, Version 1.2 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n"] }]
|
|
1284
|
+
}], ctorParameters: () => [{ type: AccessControlService }, { type: i1$2.ActivatedRoute }, { type: i3.PageTitleService }, { type: i1$2.Router }, { type: i1.GlobalNotificationService }, { type: i4$1.TranslateService }, { type: AccessControlExportService }, { type: i3.PageHeaderService }] });
|
|
634
1285
|
|
|
635
1286
|
/*
|
|
636
1287
|
* Copyright 2015-2025 Ritense BV, the Netherlands.
|
|
@@ -664,10 +1315,20 @@ const routes = [
|
|
|
664
1315
|
customPageTitle: true,
|
|
665
1316
|
},
|
|
666
1317
|
},
|
|
1318
|
+
{
|
|
1319
|
+
path: 'access-control/:id/json-editor',
|
|
1320
|
+
component: AccessControlEditorComponent,
|
|
1321
|
+
canActivate: [AuthGuardService],
|
|
1322
|
+
data: {
|
|
1323
|
+
title: 'Role details',
|
|
1324
|
+
roles: [ROLE_ADMIN],
|
|
1325
|
+
customPageTitle: true,
|
|
1326
|
+
},
|
|
1327
|
+
},
|
|
667
1328
|
];
|
|
668
1329
|
class AccessControlManagementRoutingModule {
|
|
669
1330
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: AccessControlManagementRoutingModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
|
|
670
|
-
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.20", ngImport: i0, type: AccessControlManagementRoutingModule, imports: [CommonModule,
|
|
1331
|
+
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.20", ngImport: i0, type: AccessControlManagementRoutingModule, imports: [CommonModule, i1$2.RouterModule], exports: [RouterModule] }); }
|
|
671
1332
|
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: AccessControlManagementRoutingModule, imports: [CommonModule, RouterModule.forChild(routes), RouterModule] }); }
|
|
672
1333
|
}
|
|
673
1334
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: AccessControlManagementRoutingModule, decorators: [{
|
|
@@ -709,7 +1370,6 @@ class AccessControlManagementModule {
|
|
|
709
1370
|
InputModule,
|
|
710
1371
|
IconModule,
|
|
711
1372
|
ConfirmationModalModule,
|
|
712
|
-
EditorModule,
|
|
713
1373
|
RenderInPageHeaderDirective,
|
|
714
1374
|
LoadingModule,
|
|
715
1375
|
IconModule,
|
|
@@ -717,7 +1377,10 @@ class AccessControlManagementModule {
|
|
|
717
1377
|
OverflowMenuOptionComponent,
|
|
718
1378
|
OverflowMenuTriggerComponent,
|
|
719
1379
|
NotificationModule,
|
|
720
|
-
CarbonListModule
|
|
1380
|
+
CarbonListModule,
|
|
1381
|
+
TabsModule,
|
|
1382
|
+
AccessControlJsonEditorTabComponent,
|
|
1383
|
+
AccessControlOverviewTabComponent] }); }
|
|
721
1384
|
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: AccessControlManagementModule, imports: [CommonModule,
|
|
722
1385
|
AccessControlManagementRoutingModule,
|
|
723
1386
|
ButtonModule,
|
|
@@ -728,14 +1391,16 @@ class AccessControlManagementModule {
|
|
|
728
1391
|
InputModule,
|
|
729
1392
|
IconModule,
|
|
730
1393
|
ConfirmationModalModule,
|
|
731
|
-
EditorModule,
|
|
732
1394
|
LoadingModule,
|
|
733
1395
|
IconModule,
|
|
734
1396
|
OverflowMenuComponent,
|
|
735
1397
|
OverflowMenuOptionComponent,
|
|
736
1398
|
OverflowMenuTriggerComponent,
|
|
737
1399
|
NotificationModule,
|
|
738
|
-
CarbonListModule
|
|
1400
|
+
CarbonListModule,
|
|
1401
|
+
TabsModule,
|
|
1402
|
+
AccessControlJsonEditorTabComponent,
|
|
1403
|
+
AccessControlOverviewTabComponent] }); }
|
|
739
1404
|
}
|
|
740
1405
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: AccessControlManagementModule, decorators: [{
|
|
741
1406
|
type: NgModule,
|
|
@@ -758,7 +1423,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
758
1423
|
InputModule,
|
|
759
1424
|
IconModule,
|
|
760
1425
|
ConfirmationModalModule,
|
|
761
|
-
EditorModule,
|
|
762
1426
|
RenderInPageHeaderDirective,
|
|
763
1427
|
LoadingModule,
|
|
764
1428
|
IconModule,
|
|
@@ -767,6 +1431,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
767
1431
|
OverflowMenuTriggerComponent,
|
|
768
1432
|
NotificationModule,
|
|
769
1433
|
CarbonListModule,
|
|
1434
|
+
TabsModule,
|
|
1435
|
+
AccessControlJsonEditorTabComponent,
|
|
1436
|
+
AccessControlOverviewTabComponent,
|
|
770
1437
|
],
|
|
771
1438
|
}]
|
|
772
1439
|
}] });
|
|
@@ -794,5 +1461,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
794
1461
|
* Generated bundle index. Do not edit.
|
|
795
1462
|
*/
|
|
796
1463
|
|
|
797
|
-
export { AccessControlManagementModule, AccessControlService };
|
|
1464
|
+
export { AccessControlEditorTab, AccessControlManagementModule, AccessControlService, PermissionSchemaMetadataService };
|
|
798
1465
|
//# sourceMappingURL=valtimo-access-control-management.mjs.map
|