@acorex/platform 21.0.0-next.2 → 21.0.0-next.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/auth/index.d.ts +97 -238
- package/common/index.d.ts +778 -213
- package/core/index.d.ts +562 -433
- package/fesm2022/acorex-platform-auth.mjs +160 -200
- package/fesm2022/acorex-platform-auth.mjs.map +1 -1
- package/fesm2022/acorex-platform-common.mjs +1012 -125
- package/fesm2022/acorex-platform-common.mjs.map +1 -1
- package/fesm2022/acorex-platform-core.mjs +685 -400
- package/fesm2022/acorex-platform-core.mjs.map +1 -1
- package/fesm2022/acorex-platform-domain.mjs +54 -11
- package/fesm2022/acorex-platform-domain.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-builder.mjs +412 -270
- package/fesm2022/acorex-platform-layout-builder.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-components.mjs +2112 -3153
- package/fesm2022/acorex-platform-layout-components.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-designer.mjs +7 -7
- package/fesm2022/acorex-platform-layout-designer.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-entity.mjs +756 -648
- package/fesm2022/acorex-platform-layout-entity.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-views.mjs +4 -4
- package/fesm2022/acorex-platform-layout-views.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-widget-core.mjs +248 -174
- package/fesm2022/acorex-platform-layout-widget-core.mjs.map +1 -1
- package/fesm2022/{acorex-platform-layout-widgets-file-list-popup.component-D0y-9nE5.mjs → acorex-platform-layout-widgets-file-list-popup.component-CxrsI6Hn.mjs} +2 -2
- package/fesm2022/acorex-platform-layout-widgets-file-list-popup.component-CxrsI6Hn.mjs.map +1 -0
- package/fesm2022/{acorex-platform-layout-widgets-tabular-data-edit-popup.component-m8rHZP8L.mjs → acorex-platform-layout-widgets-tabular-data-edit-popup.component-Ck7-wpT2.mjs} +2 -2
- package/fesm2022/acorex-platform-layout-widgets-tabular-data-edit-popup.component-Ck7-wpT2.mjs.map +1 -0
- package/fesm2022/acorex-platform-layout-widgets.mjs +3058 -1038
- package/fesm2022/acorex-platform-layout-widgets.mjs.map +1 -1
- package/fesm2022/{acorex-platform-themes-default-entity-master-create-view.component-mARj77Mr.mjs → acorex-platform-themes-default-entity-master-create-view.component-VIGuU5M4.mjs} +26 -5
- package/fesm2022/acorex-platform-themes-default-entity-master-create-view.component-VIGuU5M4.mjs.map +1 -0
- package/fesm2022/{acorex-platform-themes-default-entity-master-list-view.component-Cym8pq0v.mjs → acorex-platform-themes-default-entity-master-list-view.component-DyDa_hyd.mjs} +4 -5
- package/fesm2022/acorex-platform-themes-default-entity-master-list-view.component-DyDa_hyd.mjs.map +1 -0
- package/fesm2022/acorex-platform-themes-default-entity-master-modify-view.component-Ua3ZA5hk.mjs +101 -0
- package/fesm2022/acorex-platform-themes-default-entity-master-modify-view.component-Ua3ZA5hk.mjs.map +1 -0
- package/fesm2022/{acorex-platform-themes-default-entity-master-single-view.component-B_P0a5KW.mjs → acorex-platform-themes-default-entity-master-single-view.component-eMBby9k4.mjs} +3 -3
- package/fesm2022/acorex-platform-themes-default-entity-master-single-view.component-eMBby9k4.mjs.map +1 -0
- package/fesm2022/acorex-platform-themes-default.mjs +166 -30
- package/fesm2022/acorex-platform-themes-default.mjs.map +1 -1
- package/fesm2022/acorex-platform-themes-shared.mjs +27 -27
- package/fesm2022/acorex-platform-themes-shared.mjs.map +1 -1
- package/layout/builder/index.d.ts +4 -1
- package/layout/components/index.d.ts +405 -327
- package/layout/designer/index.d.ts +3 -3
- package/layout/entity/index.d.ts +163 -108
- package/layout/widget-core/index.d.ts +39 -49
- package/layout/widgets/index.d.ts +368 -54
- package/package.json +5 -5
- package/themes/default/index.d.ts +15 -2
- package/themes/shared/index.d.ts +10 -10
- package/fesm2022/acorex-platform-layout-widgets-extra-properties-schema-widget-edit.component-fhhZOWul.mjs +0 -50
- package/fesm2022/acorex-platform-layout-widgets-extra-properties-schema-widget-edit.component-fhhZOWul.mjs.map +0 -1
- package/fesm2022/acorex-platform-layout-widgets-extra-properties-schema-widget-view.component-C3Qbs0fz.mjs +0 -42
- package/fesm2022/acorex-platform-layout-widgets-extra-properties-schema-widget-view.component-C3Qbs0fz.mjs.map +0 -1
- package/fesm2022/acorex-platform-layout-widgets-extra-properties-values-widget-edit.component-CngQBUlN.mjs +0 -55
- package/fesm2022/acorex-platform-layout-widgets-extra-properties-values-widget-edit.component-CngQBUlN.mjs.map +0 -1
- package/fesm2022/acorex-platform-layout-widgets-extra-properties-values-widget-view.component-DSNo9e4W.mjs +0 -50
- package/fesm2022/acorex-platform-layout-widgets-extra-properties-values-widget-view.component-DSNo9e4W.mjs.map +0 -1
- package/fesm2022/acorex-platform-layout-widgets-extra-properties-widget-edit.component-CL0CwEHX.mjs +0 -48
- package/fesm2022/acorex-platform-layout-widgets-extra-properties-widget-edit.component-CL0CwEHX.mjs.map +0 -1
- package/fesm2022/acorex-platform-layout-widgets-extra-properties-widget-view.component-B6Fi0xTw.mjs +0 -42
- package/fesm2022/acorex-platform-layout-widgets-extra-properties-widget-view.component-B6Fi0xTw.mjs.map +0 -1
- package/fesm2022/acorex-platform-layout-widgets-file-list-popup.component-D0y-9nE5.mjs.map +0 -1
- package/fesm2022/acorex-platform-layout-widgets-tabular-data-edit-popup.component-m8rHZP8L.mjs.map +0 -1
- package/fesm2022/acorex-platform-themes-default-entity-master-create-view.component-mARj77Mr.mjs.map +0 -1
- package/fesm2022/acorex-platform-themes-default-entity-master-list-view.component-Cym8pq0v.mjs.map +0 -1
- package/fesm2022/acorex-platform-themes-default-entity-master-modify-view.component-BTA6h7Xd.mjs +0 -101
- package/fesm2022/acorex-platform-themes-default-entity-master-modify-view.component-BTA6h7Xd.mjs.map +0 -1
- package/fesm2022/acorex-platform-themes-default-entity-master-single-view.component-B_P0a5KW.mjs.map +0 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { InjectionToken, Injector, Injectable, signal, inject, Input, Directive, provideAppInitializer, Optional, Inject, NgModule
|
|
3
|
-
import {
|
|
4
|
-
import { AXPBroadcastEventService } from '@acorex/platform/core';
|
|
2
|
+
import { InjectionToken, Injector, Injectable, signal, inject, Input, Directive, provideAppInitializer, Optional, Inject, NgModule } from '@angular/core';
|
|
3
|
+
import { AXPBroadcastEventService, AXP_MODULE_PROVIDER_LOADER, AXPModuleProviderRegistry, AXP_SESSION_SERVICE } from '@acorex/platform/core';
|
|
5
4
|
import { isEmpty } from 'lodash-es';
|
|
5
|
+
import { map, BehaviorSubject, shareReplay, defaultIfEmpty, switchMap, filter, from, first } from 'rxjs';
|
|
6
6
|
|
|
7
7
|
const AXP_APPLICATION_LOADER = new InjectionToken('AXP_APPLICATION_LOADER', {
|
|
8
8
|
providedIn: 'root',
|
|
@@ -11,25 +11,29 @@ const AXP_APPLICATION_LOADER = new InjectionToken('AXP_APPLICATION_LOADER', {
|
|
|
11
11
|
},
|
|
12
12
|
});
|
|
13
13
|
class AXPApplicationDefaultLoader {
|
|
14
|
-
getList(context) {
|
|
15
|
-
return
|
|
14
|
+
async getList(context) {
|
|
15
|
+
return [
|
|
16
16
|
{
|
|
17
17
|
id: '1',
|
|
18
18
|
name: 'default-app',
|
|
19
19
|
title: 'Default Application',
|
|
20
20
|
version: '1.0.0',
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
edition: {
|
|
22
|
+
id: 'default-edition-1',
|
|
23
|
+
title: 'Standard',
|
|
24
|
+
},
|
|
23
25
|
},
|
|
24
26
|
{
|
|
25
27
|
id: '2',
|
|
26
28
|
name: 'default-app',
|
|
27
29
|
title: 'Default Application',
|
|
28
30
|
version: '1.0.0',
|
|
29
|
-
|
|
30
|
-
|
|
31
|
+
edition: {
|
|
32
|
+
id: 'default-edition-2',
|
|
33
|
+
title: 'Standard',
|
|
34
|
+
},
|
|
31
35
|
},
|
|
32
|
-
]
|
|
36
|
+
];
|
|
33
37
|
}
|
|
34
38
|
}
|
|
35
39
|
|
|
@@ -40,14 +44,14 @@ const AXP_TENANT_LOADER = new InjectionToken('AXP_TENANT_LOADER', {
|
|
|
40
44
|
},
|
|
41
45
|
});
|
|
42
46
|
class AXPTenantDefaultLoader {
|
|
43
|
-
getList(context) {
|
|
44
|
-
return
|
|
47
|
+
async getList(context) {
|
|
48
|
+
return [
|
|
45
49
|
{
|
|
46
50
|
id: '1',
|
|
47
51
|
name: 'default-tenant',
|
|
48
52
|
title: 'Default Tenant',
|
|
49
53
|
},
|
|
50
|
-
]
|
|
54
|
+
];
|
|
51
55
|
}
|
|
52
56
|
}
|
|
53
57
|
|
|
@@ -85,8 +89,8 @@ const AXP_FEATURE_LOADER = new InjectionToken('AXP_FEATURE_LOADER', {
|
|
|
85
89
|
},
|
|
86
90
|
});
|
|
87
91
|
class AXPFeatureDefaultLoader {
|
|
88
|
-
getList(context) {
|
|
89
|
-
return
|
|
92
|
+
async getList(context) {
|
|
93
|
+
return [];
|
|
90
94
|
}
|
|
91
95
|
}
|
|
92
96
|
|
|
@@ -173,6 +177,13 @@ const AXPFeatureGuard = (route, state) => {
|
|
|
173
177
|
}));
|
|
174
178
|
};
|
|
175
179
|
|
|
180
|
+
/**
|
|
181
|
+
* Optional injection token for feature checker.
|
|
182
|
+
* If provided, the checker will be called to potentially override
|
|
183
|
+
* feature enablement results based on custom logic.
|
|
184
|
+
*/
|
|
185
|
+
const AXP_FEATURE_CHECKER = new InjectionToken('AXP_FEATURE_CHECKER');
|
|
186
|
+
|
|
176
187
|
const AXP_PERMISSION_LOADER = new InjectionToken('AXP_PERMISSION_LOADER', {
|
|
177
188
|
providedIn: 'root',
|
|
178
189
|
factory: () => {
|
|
@@ -180,11 +191,18 @@ const AXP_PERMISSION_LOADER = new InjectionToken('AXP_PERMISSION_LOADER', {
|
|
|
180
191
|
}
|
|
181
192
|
});
|
|
182
193
|
class AXPPermissionDefaultLoader {
|
|
183
|
-
getList(context) {
|
|
184
|
-
return
|
|
194
|
+
async getList(context) {
|
|
195
|
+
return [];
|
|
185
196
|
}
|
|
186
197
|
}
|
|
187
198
|
|
|
199
|
+
/**
|
|
200
|
+
* Optional injection token for permission checker.
|
|
201
|
+
* If provided, the checker will be called to potentially override
|
|
202
|
+
* authorization results based on custom logic.
|
|
203
|
+
*/
|
|
204
|
+
const AXP_PERMISSION_CHECKER = new InjectionToken('AXP_PERMISSION_CHECKER');
|
|
205
|
+
|
|
188
206
|
class AXPSessionContext {
|
|
189
207
|
get user() {
|
|
190
208
|
return this._user;
|
|
@@ -218,10 +236,13 @@ class AXPSessionService {
|
|
|
218
236
|
constructor() {
|
|
219
237
|
this.eventService = inject(AXPBroadcastEventService);
|
|
220
238
|
this.authStrategyRegistry = inject(AXPAuthStrategyRegistryService);
|
|
239
|
+
this.injector = inject(Injector);
|
|
221
240
|
this.permissionLoader = inject(AXP_PERMISSION_LOADER);
|
|
222
241
|
this.featureLoader = inject(AXP_FEATURE_LOADER);
|
|
223
242
|
this.tenantLoader = inject(AXP_TENANT_LOADER);
|
|
224
243
|
this.applicationLoader = inject(AXP_APPLICATION_LOADER);
|
|
244
|
+
this.permissionChecker = inject(AXP_PERMISSION_CHECKER, { optional: true });
|
|
245
|
+
this.featureChecker = inject(AXP_FEATURE_CHECKER, { optional: true });
|
|
225
246
|
this.status = new BehaviorSubject(AXPSessionStatus.Unauthenticated);
|
|
226
247
|
this.status$ = this.status.asObservable().pipe(shareReplay(1));
|
|
227
248
|
// Add loading state to prevent premature redirects
|
|
@@ -244,13 +265,22 @@ class AXPSessionService {
|
|
|
244
265
|
// Add a new observable that considers loading state
|
|
245
266
|
this.isAuthenticatedWithLoading$ = this.isLoading$.pipe(switchMap((loading) => {
|
|
246
267
|
if (loading) {
|
|
268
|
+
// Wait for loading to complete, then return authentication status
|
|
247
269
|
return this.isLoading$.pipe(filter((isLoading) => !isLoading), switchMap(() => this.isAuthenticated$));
|
|
248
270
|
}
|
|
271
|
+
// If not loading, return current authentication status
|
|
249
272
|
return this.isAuthenticated$;
|
|
250
273
|
}), shareReplay(1));
|
|
251
274
|
this.isAuthorized$ = this.status$.pipe(map((status) => status === AXPSessionStatus.Authorized), shareReplay(1));
|
|
252
275
|
}
|
|
253
276
|
static { this.SESSION_KEY = 'AXP_SESSION'; }
|
|
277
|
+
/**
|
|
278
|
+
* Get module provider loader lazily to avoid circular dependency.
|
|
279
|
+
* ModuleProviderLoader depends on AXPSessionService, which is this service.
|
|
280
|
+
*/
|
|
281
|
+
getModuleProviderLoader() {
|
|
282
|
+
return this.injector.get(AXP_MODULE_PROVIDER_LOADER);
|
|
283
|
+
}
|
|
254
284
|
get user() {
|
|
255
285
|
const session = this.getSessionData();
|
|
256
286
|
if (session?.user && !this.currentUserSubject.value) {
|
|
@@ -266,7 +296,7 @@ class AXPSessionService {
|
|
|
266
296
|
return this.currentTenantSubject.value;
|
|
267
297
|
}
|
|
268
298
|
get tenants$() {
|
|
269
|
-
return this.tenantLoader.getList(this.getContext());
|
|
299
|
+
return from(this.tenantLoader.getList(this.getContext()));
|
|
270
300
|
}
|
|
271
301
|
get application() {
|
|
272
302
|
const session = this.getSessionData();
|
|
@@ -276,7 +306,7 @@ class AXPSessionService {
|
|
|
276
306
|
return this.currentApplicationSubject.value;
|
|
277
307
|
}
|
|
278
308
|
get applications$() {
|
|
279
|
-
return this.applicationLoader.getList(this.getContext());
|
|
309
|
+
return from(this.applicationLoader.getList(this.getContext()));
|
|
280
310
|
}
|
|
281
311
|
get permissions() {
|
|
282
312
|
return this.permissionsSubject.value ?? [];
|
|
@@ -291,7 +321,18 @@ class AXPSessionService {
|
|
|
291
321
|
if (sessionData) {
|
|
292
322
|
if (sessionData.user) {
|
|
293
323
|
this.currentUserSubject.next(sessionData.user);
|
|
324
|
+
// Restore tenant and application from session data
|
|
325
|
+
if (sessionData.tenant) {
|
|
326
|
+
this.currentTenantSubject.next(sessionData.tenant);
|
|
327
|
+
}
|
|
328
|
+
if (sessionData.application) {
|
|
329
|
+
this.currentApplicationSubject.next(sessionData.application);
|
|
330
|
+
}
|
|
294
331
|
this.status.next(AXPSessionStatus.Authenticated);
|
|
332
|
+
// Load required modules first to ensure entities are available for loaders
|
|
333
|
+
// This is critical because loaders (like application.loader) need entities
|
|
334
|
+
// from required modules (ApplicationManagement, TenantManagement, SecurityManagement)
|
|
335
|
+
await this.getModuleProviderLoader().loadRequiredModules();
|
|
295
336
|
await this.loadPermissions();
|
|
296
337
|
await this.loadFeatures();
|
|
297
338
|
await this.signInComplete();
|
|
@@ -409,6 +450,9 @@ class AXPSessionService {
|
|
|
409
450
|
}
|
|
410
451
|
//
|
|
411
452
|
const userId = this.user?.id;
|
|
453
|
+
// Clear module provider loader cache before clearing session
|
|
454
|
+
// This ensures modules and providers from previous user are not cached
|
|
455
|
+
await this.getModuleProviderLoader().clear();
|
|
412
456
|
this.clearSession();
|
|
413
457
|
this.eventService.publish(AXPSessionStatus.SignedOut, { id: userId });
|
|
414
458
|
this.isLoading.next(false);
|
|
@@ -457,7 +501,7 @@ class AXPSessionService {
|
|
|
457
501
|
}
|
|
458
502
|
async loadPermissions() {
|
|
459
503
|
try {
|
|
460
|
-
const permissions = await
|
|
504
|
+
const permissions = await this.permissionLoader.getList(this.getContext());
|
|
461
505
|
this.permissionsSubject.next(permissions ?? []);
|
|
462
506
|
}
|
|
463
507
|
catch (error) {
|
|
@@ -467,7 +511,7 @@ class AXPSessionService {
|
|
|
467
511
|
}
|
|
468
512
|
async loadFeatures() {
|
|
469
513
|
try {
|
|
470
|
-
const features = await
|
|
514
|
+
const features = await this.featureLoader.getList(this.getContext());
|
|
471
515
|
this.featuresSubject.next(features ?? []);
|
|
472
516
|
}
|
|
473
517
|
catch (error) {
|
|
@@ -547,26 +591,39 @@ class AXPSessionService {
|
|
|
547
591
|
localStorage.removeItem(AXPSessionService.SESSION_KEY);
|
|
548
592
|
}
|
|
549
593
|
authorize(...keys) {
|
|
550
|
-
|
|
594
|
+
// Calculate base result
|
|
595
|
+
const baseResult = keys.every((k) => {
|
|
596
|
+
if (isEmpty(k))
|
|
597
|
+
return true;
|
|
598
|
+
// Check if user has the permission
|
|
599
|
+
const hasPermission = this.permissions.indexOf(k) > -1;
|
|
600
|
+
if (!hasPermission)
|
|
601
|
+
return false;
|
|
602
|
+
// Check if permission has required features (if permission definition service is available)
|
|
603
|
+
// Note: This is a lightweight check. Full feature validation happens in permission definition service.
|
|
604
|
+
return true;
|
|
605
|
+
});
|
|
606
|
+
// If permission checker is provided, use it to potentially override the result
|
|
607
|
+
if (this.permissionChecker) {
|
|
608
|
+
const context = this.getContext();
|
|
609
|
+
return this.permissionChecker.check(keys, context, baseResult);
|
|
610
|
+
}
|
|
611
|
+
return baseResult;
|
|
551
612
|
}
|
|
552
613
|
isFeatureEnabled(...keys) {
|
|
553
|
-
|
|
614
|
+
// Calculate base result
|
|
615
|
+
const baseResult = keys.every((k) => isEmpty(k) || this.features.some((c) => c.name == k && c.value == true));
|
|
616
|
+
// If feature checker is provided, use it to potentially override the result
|
|
617
|
+
if (this.featureChecker) {
|
|
618
|
+
const context = this.getContext();
|
|
619
|
+
return this.featureChecker.check(keys, context, baseResult);
|
|
620
|
+
}
|
|
621
|
+
return baseResult;
|
|
554
622
|
}
|
|
555
623
|
getToken() {
|
|
556
624
|
const sessionData = this.getSessionData();
|
|
557
625
|
return sessionData?.accessToken;
|
|
558
626
|
}
|
|
559
|
-
checkTokenValidation() {
|
|
560
|
-
let sessionData = this.getSessionData();
|
|
561
|
-
if (sessionData && sessionData?.accessToken && sessionData.expiresIn) {
|
|
562
|
-
const expiresInDate = new Date(sessionData.expiresIn);
|
|
563
|
-
if (expiresInDate > new Date()) {
|
|
564
|
-
// Token is still valid
|
|
565
|
-
return true;
|
|
566
|
-
}
|
|
567
|
-
}
|
|
568
|
-
return false;
|
|
569
|
-
}
|
|
570
627
|
getContext() {
|
|
571
628
|
return new AXPSessionContext({
|
|
572
629
|
user: this.user,
|
|
@@ -687,12 +744,13 @@ class AXPPermissionDefinitionGroupBuilder {
|
|
|
687
744
|
this.context = context;
|
|
688
745
|
this._group = group;
|
|
689
746
|
}
|
|
690
|
-
addPermission(name, title, description) {
|
|
747
|
+
addPermission(name, title, description, requiredFeatures) {
|
|
691
748
|
const permission = {
|
|
692
749
|
name,
|
|
693
750
|
title,
|
|
694
751
|
description,
|
|
695
|
-
children: []
|
|
752
|
+
children: [],
|
|
753
|
+
requiredFeatures
|
|
696
754
|
};
|
|
697
755
|
this._group.permissions.push(permission);
|
|
698
756
|
return new AXPPermissionDefinitionBuilder(this, permission);
|
|
@@ -712,16 +770,25 @@ class AXPPermissionDefinitionBuilder {
|
|
|
712
770
|
this.groupBuilder = groupBuilder;
|
|
713
771
|
this.permission = permission;
|
|
714
772
|
}
|
|
715
|
-
addChild(name, title, description) {
|
|
773
|
+
addChild(name, title, description, requiredFeatures) {
|
|
716
774
|
const permission = {
|
|
717
775
|
name,
|
|
718
776
|
title,
|
|
719
777
|
description,
|
|
720
|
-
children: []
|
|
778
|
+
children: [],
|
|
779
|
+
requiredFeatures
|
|
721
780
|
};
|
|
722
781
|
this.permission.children.push(permission);
|
|
723
782
|
return this;
|
|
724
783
|
}
|
|
784
|
+
/**
|
|
785
|
+
* Set required features for this permission.
|
|
786
|
+
* @param features - Array of feature names (e.g., ['PlatformManagement.menu-customization'])
|
|
787
|
+
*/
|
|
788
|
+
requireFeatures(...features) {
|
|
789
|
+
this.permission.requiredFeatures = features;
|
|
790
|
+
return this;
|
|
791
|
+
}
|
|
725
792
|
endPermission() {
|
|
726
793
|
return this.groupBuilder;
|
|
727
794
|
}
|
|
@@ -736,6 +803,8 @@ const AXP_PERMISSION_DEFINITION_PROVIDER = new InjectionToken('AXP_PERMISSION_DE
|
|
|
736
803
|
class AXPPermissionDefinitionService {
|
|
737
804
|
constructor() {
|
|
738
805
|
this.providers = inject(AXP_PERMISSION_DEFINITION_PROVIDER, { optional: true });
|
|
806
|
+
this.providerRegistry = inject(AXPModuleProviderRegistry);
|
|
807
|
+
this.sessionService = inject(AXPSessionService);
|
|
739
808
|
this.cache = null;
|
|
740
809
|
}
|
|
741
810
|
async load() {
|
|
@@ -743,6 +812,7 @@ class AXPPermissionDefinitionService {
|
|
|
743
812
|
return;
|
|
744
813
|
}
|
|
745
814
|
const context = new AXPPermissionDefinitionProviderContext();
|
|
815
|
+
// Load providers from DI tokens (backward compatibility)
|
|
746
816
|
if (Array.isArray(this.providers)) {
|
|
747
817
|
for (const provider of this.providers) {
|
|
748
818
|
if (provider instanceof Promise) {
|
|
@@ -756,7 +826,49 @@ class AXPPermissionDefinitionService {
|
|
|
756
826
|
}
|
|
757
827
|
}
|
|
758
828
|
}
|
|
759
|
-
|
|
829
|
+
// Load providers from registry (manifest-based, conditionally loaded)
|
|
830
|
+
const registryProviders = this.providerRegistry.getPermissionProviders();
|
|
831
|
+
for (const provider of registryProviders) {
|
|
832
|
+
await provider.define(context);
|
|
833
|
+
}
|
|
834
|
+
// Filter permissions based on required features
|
|
835
|
+
const allGroups = context.getGroupDefinitions();
|
|
836
|
+
this.cache = this.filterByFeatures(allGroups);
|
|
837
|
+
}
|
|
838
|
+
/**
|
|
839
|
+
* Filter permissions based on required features.
|
|
840
|
+
* Removes permissions that have required features that are not enabled.
|
|
841
|
+
*/
|
|
842
|
+
filterByFeatures(groups) {
|
|
843
|
+
return groups.map(group => ({
|
|
844
|
+
...group,
|
|
845
|
+
permissions: this.filterPermissions(group.permissions)
|
|
846
|
+
})).filter(group => group.permissions.length > 0);
|
|
847
|
+
}
|
|
848
|
+
/**
|
|
849
|
+
* Recursively filter permissions and their children based on required features.
|
|
850
|
+
*/
|
|
851
|
+
filterPermissions(permissions) {
|
|
852
|
+
return permissions
|
|
853
|
+
.filter(permission => {
|
|
854
|
+
// Check if required features are enabled
|
|
855
|
+
if (permission.requiredFeatures && permission.requiredFeatures.length > 0) {
|
|
856
|
+
const allFeaturesEnabled = permission.requiredFeatures.every(featureName => this.sessionService.isFeatureEnabled(featureName));
|
|
857
|
+
if (!allFeaturesEnabled) {
|
|
858
|
+
return false; // Filter out this permission
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
return true;
|
|
862
|
+
})
|
|
863
|
+
.map(permission => ({
|
|
864
|
+
...permission,
|
|
865
|
+
children: this.filterPermissions(permission.children)
|
|
866
|
+
}))
|
|
867
|
+
.filter(permission => {
|
|
868
|
+
// Remove permissions that have no children if they were only containers
|
|
869
|
+
// (This is optional - you might want to keep parent permissions even without children)
|
|
870
|
+
return true;
|
|
871
|
+
});
|
|
760
872
|
}
|
|
761
873
|
async reload() {
|
|
762
874
|
this.cache = null;
|
|
@@ -882,6 +994,10 @@ class AXPAuthModule {
|
|
|
882
994
|
const initializerFn = initializeAppState(inject(AXPSessionService));
|
|
883
995
|
return initializerFn();
|
|
884
996
|
}),
|
|
997
|
+
{
|
|
998
|
+
provide: AXP_SESSION_SERVICE,
|
|
999
|
+
useExisting: AXPSessionService,
|
|
1000
|
+
},
|
|
885
1001
|
] }); }
|
|
886
1002
|
}
|
|
887
1003
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPAuthModule, decorators: [{
|
|
@@ -895,6 +1011,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
|
|
|
895
1011
|
const initializerFn = initializeAppState(inject(AXPSessionService));
|
|
896
1012
|
return initializerFn();
|
|
897
1013
|
}),
|
|
1014
|
+
{
|
|
1015
|
+
provide: AXP_SESSION_SERVICE,
|
|
1016
|
+
useExisting: AXPSessionService,
|
|
1017
|
+
},
|
|
898
1018
|
],
|
|
899
1019
|
}]
|
|
900
1020
|
}], ctorParameters: () => [{ type: undefined, decorators: [{
|
|
@@ -984,169 +1104,9 @@ class TimeUtil {
|
|
|
984
1104
|
}
|
|
985
1105
|
//#endregion
|
|
986
1106
|
|
|
987
|
-
//#region ---- Challenge Data Types ----
|
|
988
|
-
//#endregion
|
|
989
|
-
|
|
990
|
-
//#region ---- Abstract Challenge Provider ----
|
|
991
|
-
/**
|
|
992
|
-
* Abstract base class for login challenge providers
|
|
993
|
-
*
|
|
994
|
-
* Implement this class to create custom challenge mechanisms like:
|
|
995
|
-
* - Image CAPTCHA
|
|
996
|
-
* - reCAPTCHA
|
|
997
|
-
* - SMS verification
|
|
998
|
-
* - Email verification
|
|
999
|
-
*
|
|
1000
|
-
* @example
|
|
1001
|
-
* ```typescript
|
|
1002
|
-
* @Injectable()
|
|
1003
|
-
* export class MyImageCaptchaProvider extends AXPLoginChallengeProvider {
|
|
1004
|
-
* readonly name = 'image-captcha';
|
|
1005
|
-
*
|
|
1006
|
-
* checkResponse(error: unknown): AXPChallengeCheckResult | null {
|
|
1007
|
-
* if (error instanceof HttpErrorResponse) {
|
|
1008
|
-
* if (error.error?.requiresCaptcha) {
|
|
1009
|
-
* return { required: true };
|
|
1010
|
-
* }
|
|
1011
|
-
* }
|
|
1012
|
-
* return null;
|
|
1013
|
-
* }
|
|
1014
|
-
*
|
|
1015
|
-
* async getChallenge(): Promise<AXPLoginChallengeData> {
|
|
1016
|
-
* const response = await this.http.get('/api/captcha').toPromise();
|
|
1017
|
-
* return {
|
|
1018
|
-
* id: response.id,
|
|
1019
|
-
* content: response.image,
|
|
1020
|
-
* contentType: 'image-base64'
|
|
1021
|
-
* };
|
|
1022
|
-
* }
|
|
1023
|
-
*
|
|
1024
|
-
* async refreshChallenge(): Promise<AXPLoginChallengeData> {
|
|
1025
|
-
* return this.getChallenge();
|
|
1026
|
-
* }
|
|
1027
|
-
*
|
|
1028
|
-
* getChallengeComponent(): Type<AXPLoginChallengeComponentBase> {
|
|
1029
|
-
* return MyCaptchaChallengeComponent;
|
|
1030
|
-
* }
|
|
1031
|
-
* }
|
|
1032
|
-
* ```
|
|
1033
|
-
*/
|
|
1034
|
-
class AXPLoginChallengeProvider {
|
|
1035
|
-
/**
|
|
1036
|
-
* Returns the component type for rendering the challenge UI
|
|
1037
|
-
*
|
|
1038
|
-
* Override this method to provide a custom challenge UI component.
|
|
1039
|
-
* If not overridden (returns null), the login component will use
|
|
1040
|
-
* a default built-in UI.
|
|
1041
|
-
*
|
|
1042
|
-
* @returns Component type extending AXPLoginChallengeComponentBase, or null for default UI
|
|
1043
|
-
*/
|
|
1044
|
-
getChallengeComponent() {
|
|
1045
|
-
return null;
|
|
1046
|
-
}
|
|
1047
|
-
}
|
|
1048
|
-
//#endregion
|
|
1049
|
-
|
|
1050
|
-
//#region ---- Injection Token ----
|
|
1051
|
-
/**
|
|
1052
|
-
* Injection token for the login challenge provider
|
|
1053
|
-
*
|
|
1054
|
-
* This token is optional - if not provided, no challenge mechanism will be used.
|
|
1055
|
-
*
|
|
1056
|
-
* @example
|
|
1057
|
-
* ```typescript
|
|
1058
|
-
* // In your app module or provider configuration:
|
|
1059
|
-
* providers: [
|
|
1060
|
-
* {
|
|
1061
|
-
* provide: AXP_LOGIN_CHALLENGE_PROVIDER,
|
|
1062
|
-
* useClass: MyImageCaptchaProvider
|
|
1063
|
-
* }
|
|
1064
|
-
* ]
|
|
1065
|
-
*
|
|
1066
|
-
* // In a component:
|
|
1067
|
-
* private challengeProvider = inject(AXP_LOGIN_CHALLENGE_PROVIDER, { optional: true });
|
|
1068
|
-
* ```
|
|
1069
|
-
*/
|
|
1070
|
-
const AXP_LOGIN_CHALLENGE_PROVIDER = new InjectionToken('AXP_LOGIN_CHALLENGE_PROVIDER');
|
|
1071
|
-
//#endregion
|
|
1072
|
-
|
|
1073
|
-
//#region ---- Base Challenge Component ----
|
|
1074
|
-
/**
|
|
1075
|
-
* Base class for login challenge UI components
|
|
1076
|
-
*
|
|
1077
|
-
* Providers can extend this class to create custom challenge UIs.
|
|
1078
|
-
* The login component will render this component and listen to its outputs.
|
|
1079
|
-
*
|
|
1080
|
-
* @example
|
|
1081
|
-
* ```typescript
|
|
1082
|
-
* @Component({
|
|
1083
|
-
* selector: 'my-captcha-challenge',
|
|
1084
|
-
* template: `
|
|
1085
|
-
* <div class="captcha-container">
|
|
1086
|
-
* <img [src]="'data:image/png;base64,' + challengeData().content" />
|
|
1087
|
-
* <input
|
|
1088
|
-
* type="text"
|
|
1089
|
-
* [value]="response()"
|
|
1090
|
-
* (input)="onResponseChange($event)"
|
|
1091
|
-
* />
|
|
1092
|
-
* <button (click)="onRefreshClick()">Refresh</button>
|
|
1093
|
-
* </div>
|
|
1094
|
-
* `
|
|
1095
|
-
* })
|
|
1096
|
-
* export class MyCaptchaChallengeComponent extends AXPLoginChallengeComponentBase {
|
|
1097
|
-
* response = signal('');
|
|
1098
|
-
*
|
|
1099
|
-
* onResponseChange(event: Event) {
|
|
1100
|
-
* const value = (event.target as HTMLInputElement).value;
|
|
1101
|
-
* this.response.set(value);
|
|
1102
|
-
* this.responseChange.emit(value);
|
|
1103
|
-
* }
|
|
1104
|
-
*
|
|
1105
|
-
* onRefreshClick() {
|
|
1106
|
-
* this.refreshRequest.emit();
|
|
1107
|
-
* }
|
|
1108
|
-
* }
|
|
1109
|
-
* ```
|
|
1110
|
-
*/
|
|
1111
|
-
class AXPLoginChallengeComponentBase {
|
|
1112
|
-
constructor() {
|
|
1113
|
-
//#region ---- Inputs ----
|
|
1114
|
-
/**
|
|
1115
|
-
* Challenge data to display
|
|
1116
|
-
* Contains the image/content and metadata from the server
|
|
1117
|
-
*/
|
|
1118
|
-
this.challengeData = input.required(...(ngDevMode ? [{ debugName: "challengeData" }] : []));
|
|
1119
|
-
/**
|
|
1120
|
-
* Whether the challenge is currently loading (e.g., refreshing)
|
|
1121
|
-
*/
|
|
1122
|
-
this.isLoading = input(false, ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
|
|
1123
|
-
//#endregion
|
|
1124
|
-
//#region ---- Outputs ----
|
|
1125
|
-
/**
|
|
1126
|
-
* Emits when the user enters or changes their response
|
|
1127
|
-
* The login component will capture this value and include it in credentials
|
|
1128
|
-
*/
|
|
1129
|
-
this.responseChange = output();
|
|
1130
|
-
/**
|
|
1131
|
-
* Emits when the user requests a new challenge (e.g., clicks refresh button)
|
|
1132
|
-
* The login component will call provider.refreshChallenge()
|
|
1133
|
-
*/
|
|
1134
|
-
this.refreshRequest = output();
|
|
1135
|
-
}
|
|
1136
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPLoginChallengeComponentBase, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1137
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.3.12", type: AXPLoginChallengeComponentBase, isStandalone: true, selector: "ng-component", inputs: { challengeData: { classPropertyName: "challengeData", publicName: "challengeData", isSignal: true, isRequired: true, transformFunction: null }, isLoading: { classPropertyName: "isLoading", publicName: "isLoading", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { responseChange: "responseChange", refreshRequest: "refreshRequest" }, ngImport: i0, template: '', isInline: true }); }
|
|
1138
|
-
}
|
|
1139
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPLoginChallengeComponentBase, decorators: [{
|
|
1140
|
-
type: Component,
|
|
1141
|
-
args: [{
|
|
1142
|
-
template: '',
|
|
1143
|
-
standalone: true,
|
|
1144
|
-
}]
|
|
1145
|
-
}], propDecorators: { challengeData: [{ type: i0.Input, args: [{ isSignal: true, alias: "challengeData", required: true }] }], isLoading: [{ type: i0.Input, args: [{ isSignal: true, alias: "isLoading", required: false }] }], responseChange: [{ type: i0.Output, args: ["responseChange"] }], refreshRequest: [{ type: i0.Output, args: ["refreshRequest"] }] } });
|
|
1146
|
-
|
|
1147
1107
|
/**
|
|
1148
1108
|
* Generated bundle index. Do not edit.
|
|
1149
1109
|
*/
|
|
1150
1110
|
|
|
1151
|
-
export { AXPAuthGuard, AXPAuthModule, AXPAuthStrategy, AXPAuthStrategyRegistryService, AXPFeatureDirective, AXPFeatureGuard,
|
|
1111
|
+
export { AXPAuthGuard, AXPAuthModule, AXPAuthStrategy, AXPAuthStrategyRegistryService, AXPFeatureDirective, AXPFeatureGuard, AXPPermissionDefinitionBuilder, AXPPermissionDefinitionGroupBuilder, AXPPermissionDefinitionProviderContext, AXPPermissionDefinitionService, AXPPermissionDirective, AXPPermissionEvaluatorScopeProvider, AXPPermissionGuard, AXPSessionContext, AXPSessionService, AXPSessionStatus, AXPUnauthenticatedError, AXPUnauthorizedError, AXP_APPLICATION_LOADER, AXP_FEATURE_CHECKER, AXP_FEATURE_LOADER, AXP_PERMISSION_CHECKER, AXP_PERMISSION_DEFINITION_PROVIDER, AXP_PERMISSION_LOADER, AXP_TENANT_LOADER, JwtUtil, PkceUtil, TimeUtil, initializeAppState };
|
|
1152
1112
|
//# sourceMappingURL=acorex-platform-auth.mjs.map
|