@rolatech/angular-auth 20.3.0-beta.3 → 20.3.1-beta.2
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.
|
@@ -1,37 +1,53 @@
|
|
|
1
|
+
import { Location, isPlatformBrowser, CommonModule } from '@angular/common';
|
|
1
2
|
import * as i0 from '@angular/core';
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import { APP_CONFIG, AngularCommonModule } from '@rolatech/angular-common';
|
|
5
|
-
import { SnackBarService } from '@rolatech/angular-services';
|
|
6
|
-
import QRCode from 'qrcode';
|
|
3
|
+
import { signal, computed, Injectable, inject, Component, input, ChangeDetectionStrategy, output, model, PLATFORM_ID, InjectionToken, APP_INITIALIZER, makeEnvironmentProviders } from '@angular/core';
|
|
4
|
+
import * as i1 from '@angular/material/button';
|
|
7
5
|
import { MatButtonModule } from '@angular/material/button';
|
|
6
|
+
import * as i2 from '@angular/material/progress-spinner';
|
|
7
|
+
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
|
8
|
+
import { ActivatedRoute, Router, RouterLink, provideRouter, ROUTES } from '@angular/router';
|
|
9
|
+
import { APP_CONFIG, AngularCommonModule } from '@rolatech/angular-common';
|
|
10
|
+
import { of, forkJoin, throwError, catchError as catchError$1, switchMap as switchMap$1, map as map$1, BehaviorSubject, NEVER, firstValueFrom } from 'rxjs';
|
|
8
11
|
import { HttpClient, HttpErrorResponse, HTTP_INTERCEPTORS } from '@angular/common/http';
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
12
|
+
import { switchMap, catchError, map, finalize, shareReplay, tap } from 'rxjs/operators';
|
|
13
|
+
import { toSignal, toObservable } from '@angular/core/rxjs-interop';
|
|
14
|
+
import * as i2$1 from '@angular/material/icon';
|
|
15
|
+
import { MatIconModule } from '@angular/material/icon';
|
|
16
|
+
import * as i1$1 from '@angular/material/menu';
|
|
17
|
+
import { MatMenuModule } from '@angular/material/menu';
|
|
18
|
+
import { OnboardingApplicantService, SnackBarService } from '@rolatech/angular-services';
|
|
19
|
+
import { MatDialogRef, MAT_DIALOG_DATA, MatDialogModule, MatDialog } from '@angular/material/dialog';
|
|
20
|
+
import QRCode from 'qrcode';
|
|
11
21
|
import { SpinnerComponent, AngularComponentsModule } from '@rolatech/angular-components';
|
|
12
|
-
import * as i1 from '@angular/forms';
|
|
13
|
-
import * as i2 from '@angular/material/form-field';
|
|
22
|
+
import * as i1$2 from '@angular/forms';
|
|
23
|
+
import * as i2$2 from '@angular/material/form-field';
|
|
14
24
|
import * as i3 from '@angular/material/input';
|
|
15
|
-
import { isPlatformBrowser, CommonModule } from '@angular/common';
|
|
16
|
-
import { Router, provideRouter, ROUTES } from '@angular/router';
|
|
17
|
-
import { MatSnackBar } from '@angular/material/snack-bar';
|
|
18
25
|
|
|
19
|
-
class
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
class AuthContextStore {
|
|
27
|
+
state = signal({
|
|
28
|
+
appId: null,
|
|
29
|
+
orgId: null,
|
|
30
|
+
}, ...(ngDevMode ? [{ debugName: "state" }] : []));
|
|
31
|
+
appId = computed(() => this.state().appId, ...(ngDevMode ? [{ debugName: "appId" }] : []));
|
|
32
|
+
orgId = computed(() => this.state().orgId, ...(ngDevMode ? [{ debugName: "orgId" }] : []));
|
|
33
|
+
setContext(appId, orgId) {
|
|
34
|
+
this.state.set({ appId, orgId });
|
|
35
|
+
}
|
|
36
|
+
setAppId(appId) {
|
|
37
|
+
this.state.update((state) => ({ ...state, appId }));
|
|
38
|
+
}
|
|
39
|
+
setOrgId(orgId) {
|
|
40
|
+
this.state.update((state) => ({ ...state, orgId }));
|
|
41
|
+
}
|
|
42
|
+
clear() {
|
|
43
|
+
this.state.set({ appId: null, orgId: null });
|
|
44
|
+
}
|
|
45
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: AuthContextStore, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
46
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: AuthContextStore, providedIn: 'root' });
|
|
31
47
|
}
|
|
32
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type:
|
|
33
|
-
type:
|
|
34
|
-
args: [{
|
|
48
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: AuthContextStore, decorators: [{
|
|
49
|
+
type: Injectable,
|
|
50
|
+
args: [{ providedIn: 'root' }]
|
|
35
51
|
}] });
|
|
36
52
|
|
|
37
53
|
class AuthStore {
|
|
@@ -188,32 +204,59 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
|
|
|
188
204
|
args: [{ providedIn: 'root' }]
|
|
189
205
|
}] });
|
|
190
206
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
}, ...(ngDevMode ? [{ debugName: "state" }] : []));
|
|
196
|
-
appId = computed(() => this.state().appId, ...(ngDevMode ? [{ debugName: "appId" }] : []));
|
|
197
|
-
orgId = computed(() => this.state().orgId, ...(ngDevMode ? [{ debugName: "orgId" }] : []));
|
|
198
|
-
setContext(appId, orgId) {
|
|
199
|
-
this.state.set({ appId, orgId });
|
|
207
|
+
const LAST_ALLOWED_URL_KEY = 'rolatech.auth.last-allowed-url';
|
|
208
|
+
function normalizeUrl(url) {
|
|
209
|
+
if (!url) {
|
|
210
|
+
return null;
|
|
200
211
|
}
|
|
201
|
-
|
|
202
|
-
|
|
212
|
+
if (url.startsWith('http://') || url.startsWith('https://')) {
|
|
213
|
+
try {
|
|
214
|
+
const parsed = new URL(url);
|
|
215
|
+
return `${parsed.pathname}${parsed.search}${parsed.hash}` || '/';
|
|
216
|
+
}
|
|
217
|
+
catch {
|
|
218
|
+
return null;
|
|
219
|
+
}
|
|
203
220
|
}
|
|
204
|
-
|
|
205
|
-
|
|
221
|
+
return url.startsWith('/') ? url : `/${url}`;
|
|
222
|
+
}
|
|
223
|
+
function isAccessBoundaryUrl(url) {
|
|
224
|
+
return url.startsWith('/forbidden') || url.startsWith('/unauthorized');
|
|
225
|
+
}
|
|
226
|
+
function rememberAllowedUrl(url) {
|
|
227
|
+
if (typeof window === 'undefined') {
|
|
228
|
+
return;
|
|
206
229
|
}
|
|
207
|
-
|
|
208
|
-
|
|
230
|
+
const normalizedUrl = normalizeUrl(url);
|
|
231
|
+
if (!normalizedUrl || isAccessBoundaryUrl(normalizedUrl)) {
|
|
232
|
+
return;
|
|
209
233
|
}
|
|
210
|
-
|
|
211
|
-
|
|
234
|
+
window.sessionStorage.setItem(LAST_ALLOWED_URL_KEY, normalizedUrl);
|
|
235
|
+
}
|
|
236
|
+
function readLastAllowedUrl() {
|
|
237
|
+
if (typeof window === 'undefined') {
|
|
238
|
+
return null;
|
|
239
|
+
}
|
|
240
|
+
const value = normalizeUrl(window.sessionStorage.getItem(LAST_ALLOWED_URL_KEY));
|
|
241
|
+
if (!value || isAccessBoundaryUrl(value)) {
|
|
242
|
+
return null;
|
|
243
|
+
}
|
|
244
|
+
return value;
|
|
245
|
+
}
|
|
246
|
+
function createForbiddenUrlTree(router, deniedUrl, reason = 'forbidden') {
|
|
247
|
+
const queryParams = {
|
|
248
|
+
reason,
|
|
249
|
+
};
|
|
250
|
+
const normalizedDeniedUrl = normalizeUrl(deniedUrl);
|
|
251
|
+
if (normalizedDeniedUrl) {
|
|
252
|
+
queryParams['denied'] = normalizedDeniedUrl;
|
|
253
|
+
}
|
|
254
|
+
const fallbackUrl = readLastAllowedUrl();
|
|
255
|
+
if (fallbackUrl) {
|
|
256
|
+
queryParams['fallback'] = fallbackUrl;
|
|
257
|
+
}
|
|
258
|
+
return router.createUrlTree(['/forbidden'], { queryParams });
|
|
212
259
|
}
|
|
213
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: AuthContextStore, decorators: [{
|
|
214
|
-
type: Injectable,
|
|
215
|
-
args: [{ providedIn: 'root' }]
|
|
216
|
-
}] });
|
|
217
260
|
|
|
218
261
|
class AuthService {
|
|
219
262
|
environment = inject(APP_CONFIG);
|
|
@@ -456,6 +499,214 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
|
|
|
456
499
|
args: [{ providedIn: 'root' }]
|
|
457
500
|
}] });
|
|
458
501
|
|
|
502
|
+
class ForbiddenComponent {
|
|
503
|
+
route = inject(ActivatedRoute);
|
|
504
|
+
router = inject(Router);
|
|
505
|
+
location = inject(Location);
|
|
506
|
+
authService = inject(AuthService);
|
|
507
|
+
authStore = inject(AuthStore);
|
|
508
|
+
authContextStore = inject(AuthContextStore);
|
|
509
|
+
environment = inject(APP_CONFIG);
|
|
510
|
+
switchingAccount = signal(false, ...(ngDevMode ? [{ debugName: "switchingAccount" }] : []));
|
|
511
|
+
deniedUrl = this.normalizeRoute(this.route.snapshot.queryParamMap.get('denied'));
|
|
512
|
+
fallbackUrl = this.resolveFallbackUrl();
|
|
513
|
+
authenticated = this.authStore.authenticated;
|
|
514
|
+
accountLabel = computed(() => this.authStore.username() ?? this.authStore.user()?.email ?? 'this account', ...(ngDevMode ? [{ debugName: "accountLabel" }] : []));
|
|
515
|
+
goBackOrFallback() {
|
|
516
|
+
if (this.fallbackUrl) {
|
|
517
|
+
this.router.navigateByUrl(this.fallbackUrl);
|
|
518
|
+
return;
|
|
519
|
+
}
|
|
520
|
+
this.location.back();
|
|
521
|
+
}
|
|
522
|
+
signOutAndSwitchAccount() {
|
|
523
|
+
const signInUrl = this.buildSignInUrl();
|
|
524
|
+
this.switchingAccount.set(true);
|
|
525
|
+
this.authService
|
|
526
|
+
.logout()
|
|
527
|
+
.pipe(catchError$1(() => {
|
|
528
|
+
this.authStore.clear();
|
|
529
|
+
this.authContextStore.clear();
|
|
530
|
+
return of(null);
|
|
531
|
+
}))
|
|
532
|
+
.subscribe({
|
|
533
|
+
next: () => {
|
|
534
|
+
window.location.href = signInUrl;
|
|
535
|
+
},
|
|
536
|
+
error: () => {
|
|
537
|
+
window.location.href = signInUrl;
|
|
538
|
+
},
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
goToSignIn() {
|
|
542
|
+
window.location.href = this.buildSignInUrl();
|
|
543
|
+
}
|
|
544
|
+
buildSignInUrl() {
|
|
545
|
+
const continueUrl = this.buildContinueUrl();
|
|
546
|
+
return `${this.environment.accountsUrl}/signin?continue=${encodeURIComponent(continueUrl)}`;
|
|
547
|
+
}
|
|
548
|
+
buildContinueUrl() {
|
|
549
|
+
if (typeof window === 'undefined') {
|
|
550
|
+
return this.environment.myaccountUrl;
|
|
551
|
+
}
|
|
552
|
+
const target = this.deniedUrl || this.fallbackUrl || '/';
|
|
553
|
+
return new URL(target, window.location.origin).toString();
|
|
554
|
+
}
|
|
555
|
+
resolveFallbackUrl() {
|
|
556
|
+
const queryFallback = this.normalizeRoute(this.route.snapshot.queryParamMap.get('fallback'));
|
|
557
|
+
const storedFallback = this.normalizeRoute(readLastAllowedUrl());
|
|
558
|
+
const fallback = queryFallback || storedFallback;
|
|
559
|
+
if (!fallback || fallback === this.deniedUrl || fallback.startsWith('/forbidden')) {
|
|
560
|
+
return null;
|
|
561
|
+
}
|
|
562
|
+
return fallback;
|
|
563
|
+
}
|
|
564
|
+
normalizeRoute(url) {
|
|
565
|
+
if (!url) {
|
|
566
|
+
return null;
|
|
567
|
+
}
|
|
568
|
+
return url.startsWith('/') ? url : `/${url}`;
|
|
569
|
+
}
|
|
570
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: ForbiddenComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
571
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: ForbiddenComponent, isStandalone: true, selector: "rolatech-forbidden", ngImport: i0, template: "<section class=\"forbidden-page\">\n <article class=\"forbidden-page__card\">\n <span class=\"forbidden-page__eyebrow\">403</span>\n <h1 class=\"forbidden-page__title\">This account cannot open that page</h1>\n\n @if (authenticated()) {\n <p class=\"forbidden-page__description\">\n You are signed in as <strong>{{ accountLabel() }}</strong>, but that account does not have permission to view this route.\n </p>\n } @else {\n <p class=\"forbidden-page__description\">You do not have permission to open this route with the current session.</p>\n }\n\n @if (deniedUrl) {\n <div class=\"forbidden-page__route\">\n <span class=\"forbidden-page__route-label\">Attempted page</span>\n <code class=\"forbidden-page__route-value\">{{ deniedUrl }}</code>\n </div>\n }\n\n <p class=\"forbidden-page__hint\">\n Return to a page that still works for this account, or sign out and continue with the correct account to retry access.\n </p>\n\n <div class=\"forbidden-page__actions\">\n <button mat-stroked-button type=\"button\" (click)=\"goBackOrFallback()\">\n {{ fallbackUrl ? 'Back to allowed page' : 'Go back' }}\n </button>\n\n @if (authenticated()) {\n <button mat-flat-button type=\"button\" (click)=\"signOutAndSwitchAccount()\" [disabled]=\"switchingAccount()\">\n @if (switchingAccount()) {\n <mat-progress-spinner diameter=\"18\" mode=\"indeterminate\"></mat-progress-spinner>\n }\n <span>{{ switchingAccount() ? 'Switching account...' : 'Sign out and switch account' }}</span>\n </button>\n } @else {\n <button mat-flat-button type=\"button\" (click)=\"goToSignIn()\">Sign in</button>\n }\n </div>\n\n <p class=\"forbidden-page__support\">\n If this should be available for the current account, ask an administrator to review the role or permission assignment.\n </p>\n </article>\n</section>\n", styles: [":host{display:grid;min-height:100dvh;padding:1.5rem;place-items:center;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 14%,transparent),transparent 42%),var(--rt-base-background, #ffffff)}.forbidden-page{width:min(100%,42rem)}.forbidden-page__card{display:flex;flex-direction:column;gap:1rem;padding:1.5rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 24px 56px -42px color-mix(in srgb,var(--rt-text-primary) 24%,transparent)}.forbidden-page__eyebrow{display:inline-flex;align-self:flex-start;padding:.32rem .72rem;border-radius:9999px;background:color-mix(in srgb,var(--mat-sys-error, #b91c1c) 12%,transparent);color:var(--mat-sys-error, #b91c1c);font-size:.78rem;font-weight:800;letter-spacing:.08em;text-transform:uppercase}.forbidden-page__title{margin:0;color:var(--rt-text-primary);font-size:clamp(1.8rem,4vw,2.4rem);line-height:1.05}.forbidden-page__description,.forbidden-page__hint,.forbidden-page__support{margin:0;color:var(--rt-text-secondary);line-height:1.6}.forbidden-page__route{display:flex;flex-direction:column;gap:.45rem;padding:.95rem 1rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1rem;background:color-mix(in srgb,var(--rt-10-percent-layer, rgba(15, 23, 42, .08)) 72%,transparent)}.forbidden-page__route-label{font-size:.8rem;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--rt-text-secondary)}.forbidden-page__route-value{overflow-wrap:anywhere;color:var(--rt-text-primary);font-family:SFMono-Regular,ui-monospace,monospace;font-size:.95rem}.forbidden-page__actions{display:flex;flex-wrap:wrap;gap:.75rem;align-items:center}.forbidden-page__actions button{display:inline-flex;gap:.55rem;align-items:center}@media(max-width:640px){:host{padding:1rem}.forbidden-page__card{padding:1.2rem}.forbidden-page__actions{flex-direction:column;align-items:stretch}.forbidden-page__actions button{justify-content:center}}\n"], dependencies: [{ kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i2.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }] });
|
|
572
|
+
}
|
|
573
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: ForbiddenComponent, decorators: [{
|
|
574
|
+
type: Component,
|
|
575
|
+
args: [{ selector: 'rolatech-forbidden', imports: [MatButtonModule, MatProgressSpinnerModule], template: "<section class=\"forbidden-page\">\n <article class=\"forbidden-page__card\">\n <span class=\"forbidden-page__eyebrow\">403</span>\n <h1 class=\"forbidden-page__title\">This account cannot open that page</h1>\n\n @if (authenticated()) {\n <p class=\"forbidden-page__description\">\n You are signed in as <strong>{{ accountLabel() }}</strong>, but that account does not have permission to view this route.\n </p>\n } @else {\n <p class=\"forbidden-page__description\">You do not have permission to open this route with the current session.</p>\n }\n\n @if (deniedUrl) {\n <div class=\"forbidden-page__route\">\n <span class=\"forbidden-page__route-label\">Attempted page</span>\n <code class=\"forbidden-page__route-value\">{{ deniedUrl }}</code>\n </div>\n }\n\n <p class=\"forbidden-page__hint\">\n Return to a page that still works for this account, or sign out and continue with the correct account to retry access.\n </p>\n\n <div class=\"forbidden-page__actions\">\n <button mat-stroked-button type=\"button\" (click)=\"goBackOrFallback()\">\n {{ fallbackUrl ? 'Back to allowed page' : 'Go back' }}\n </button>\n\n @if (authenticated()) {\n <button mat-flat-button type=\"button\" (click)=\"signOutAndSwitchAccount()\" [disabled]=\"switchingAccount()\">\n @if (switchingAccount()) {\n <mat-progress-spinner diameter=\"18\" mode=\"indeterminate\"></mat-progress-spinner>\n }\n <span>{{ switchingAccount() ? 'Switching account...' : 'Sign out and switch account' }}</span>\n </button>\n } @else {\n <button mat-flat-button type=\"button\" (click)=\"goToSignIn()\">Sign in</button>\n }\n </div>\n\n <p class=\"forbidden-page__support\">\n If this should be available for the current account, ask an administrator to review the role or permission assignment.\n </p>\n </article>\n</section>\n", styles: [":host{display:grid;min-height:100dvh;padding:1.5rem;place-items:center;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 14%,transparent),transparent 42%),var(--rt-base-background, #ffffff)}.forbidden-page{width:min(100%,42rem)}.forbidden-page__card{display:flex;flex-direction:column;gap:1rem;padding:1.5rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 24px 56px -42px color-mix(in srgb,var(--rt-text-primary) 24%,transparent)}.forbidden-page__eyebrow{display:inline-flex;align-self:flex-start;padding:.32rem .72rem;border-radius:9999px;background:color-mix(in srgb,var(--mat-sys-error, #b91c1c) 12%,transparent);color:var(--mat-sys-error, #b91c1c);font-size:.78rem;font-weight:800;letter-spacing:.08em;text-transform:uppercase}.forbidden-page__title{margin:0;color:var(--rt-text-primary);font-size:clamp(1.8rem,4vw,2.4rem);line-height:1.05}.forbidden-page__description,.forbidden-page__hint,.forbidden-page__support{margin:0;color:var(--rt-text-secondary);line-height:1.6}.forbidden-page__route{display:flex;flex-direction:column;gap:.45rem;padding:.95rem 1rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1rem;background:color-mix(in srgb,var(--rt-10-percent-layer, rgba(15, 23, 42, .08)) 72%,transparent)}.forbidden-page__route-label{font-size:.8rem;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--rt-text-secondary)}.forbidden-page__route-value{overflow-wrap:anywhere;color:var(--rt-text-primary);font-family:SFMono-Regular,ui-monospace,monospace;font-size:.95rem}.forbidden-page__actions{display:flex;flex-wrap:wrap;gap:.75rem;align-items:center}.forbidden-page__actions button{display:inline-flex;gap:.55rem;align-items:center}@media(max-width:640px){:host{padding:1rem}.forbidden-page__card{padding:1.2rem}.forbidden-page__actions{flex-direction:column;align-items:stretch}.forbidden-page__actions button{justify-content:center}}\n"] }]
|
|
576
|
+
}] });
|
|
577
|
+
|
|
578
|
+
class UnauthorizedComponent {
|
|
579
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: UnauthorizedComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
580
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.1", type: UnauthorizedComponent, isStandalone: true, selector: "rolatech-unauthorized", ngImport: i0, template: "<div class=\"p-6 max-w-lg h-auto max-h-32\">\n <div class=\"flex flex-col\">\n <b>401.</b>\n <p>Unauthorized page.</p>\n </div>\n</div>\n", styles: [""] });
|
|
581
|
+
}
|
|
582
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: UnauthorizedComponent, decorators: [{
|
|
583
|
+
type: Component,
|
|
584
|
+
args: [{ selector: 'rolatech-unauthorized', template: "<div class=\"p-6 max-w-lg h-auto max-h-32\">\n <div class=\"flex flex-col\">\n <b>401.</b>\n <p>Unauthorized page.</p>\n </div>\n</div>\n" }]
|
|
585
|
+
}] });
|
|
586
|
+
|
|
587
|
+
const PLATFORM_ADMIN_ROLES = ['PLATFORM_ADMIN', 'ROLE_PLATFORM_ADMIN'];
|
|
588
|
+
const APPLICATION_OWNER_ROLES = ['APPLICATION_OWNER', 'APP_OWNER', 'ROLE_APPLICATION_OWNER', 'ROLE_APP_OWNER'];
|
|
589
|
+
const APPLICATION_ADMIN_ROLES = ['APPLICATION_ADMIN', 'APP_ADMIN', 'ROLE_APPLICATION_ADMIN', 'ROLE_APP_ADMIN'];
|
|
590
|
+
const ORGANIZATION_OWNER_ROLES = ['ORGANIZATION_OWNER', 'ORG_OWNER', 'ROLE_ORGANIZATION_OWNER', 'ROLE_ORG_OWNER'];
|
|
591
|
+
const ORGANIZATION_ADMIN_ROLES = ['ORGANIZATION_ADMIN', 'ORG_ADMIN', 'ROLE_ORGANIZATION_ADMIN', 'ROLE_ORG_ADMIN'];
|
|
592
|
+
const ORGANIZATION_MEMBER_ROLES = ['ORGANIZATION_MEMBER', 'ORG_MEMBER', 'ROLE_ORGANIZATION_MEMBER', 'ROLE_ORG_MEMBER'];
|
|
593
|
+
const ORGANIZATION_STAFF_ROLES = ['ORGANIZATION_STAFF', 'ORG_STAFF', 'ROLE_ORGANIZATION_STAFF', 'ROLE_ORG_STAFF'];
|
|
594
|
+
|
|
595
|
+
class AgentAccessMenuComponent {
|
|
596
|
+
applicationCode = input('primecasa', ...(ngDevMode ? [{ debugName: "applicationCode" }] : []));
|
|
597
|
+
applyRouterLink = input('/agents', ...(ngDevMode ? [{ debugName: "applyRouterLink" }] : []));
|
|
598
|
+
dashboardRouterLink = input('/organization', ...(ngDevMode ? [{ debugName: "dashboardRouterLink" }] : []));
|
|
599
|
+
showApplyAction = input(true, ...(ngDevMode ? [{ debugName: "showApplyAction" }] : []));
|
|
600
|
+
dashboardLabel = input('Agent dashboard', ...(ngDevMode ? [{ debugName: "dashboardLabel" }] : []));
|
|
601
|
+
authStore = inject(AuthStore);
|
|
602
|
+
authContextStore = inject(AuthContextStore);
|
|
603
|
+
onboardingApplicantService = inject(OnboardingApplicantService);
|
|
604
|
+
mine = toSignal(toObservable(computed(() => ({
|
|
605
|
+
authenticated: this.authStore.authenticated(),
|
|
606
|
+
loaded: this.authStore.loaded(),
|
|
607
|
+
applicationCode: this.applicationCode(),
|
|
608
|
+
}))).pipe(switchMap$1(({ authenticated, loaded, applicationCode }) => {
|
|
609
|
+
if (!loaded || !authenticated || !applicationCode) {
|
|
610
|
+
return of(null);
|
|
611
|
+
}
|
|
612
|
+
return this.onboardingApplicantService.listMine(applicationCode).pipe(catchError$1(() => of([])));
|
|
613
|
+
})), { initialValue: null });
|
|
614
|
+
latestApplication = computed(() => {
|
|
615
|
+
const applications = this.mine();
|
|
616
|
+
if (!applications?.length) {
|
|
617
|
+
return null;
|
|
618
|
+
}
|
|
619
|
+
return [...applications].sort((left, right) => this.toTimestamp(right) - this.toTimestamp(left))[0] ?? null;
|
|
620
|
+
}, ...(ngDevMode ? [{ debugName: "latestApplication" }] : []));
|
|
621
|
+
hasDashboardAccess = computed(() => {
|
|
622
|
+
this.authStore.loaded();
|
|
623
|
+
const appId = this.authContextStore.appId() ?? this.authStore.primaryApplication()?.appId ?? this.authStore.primaryOrganization()?.appId ?? null;
|
|
624
|
+
return this.authStore.hasOrganizationRole(appId, null, ...ORGANIZATION_OWNER_ROLES, ...ORGANIZATION_ADMIN_ROLES, ...ORGANIZATION_MEMBER_ROLES);
|
|
625
|
+
}, ...(ngDevMode ? [{ debugName: "hasDashboardAccess" }] : []));
|
|
626
|
+
entry = computed(() => {
|
|
627
|
+
if (this.hasDashboardAccess()) {
|
|
628
|
+
return {
|
|
629
|
+
icon: 'storefront',
|
|
630
|
+
label: this.dashboardLabel(),
|
|
631
|
+
routerLink: this.dashboardRouterLink(),
|
|
632
|
+
};
|
|
633
|
+
}
|
|
634
|
+
const application = this.latestApplication();
|
|
635
|
+
if (application) {
|
|
636
|
+
return {
|
|
637
|
+
icon: 'assignment',
|
|
638
|
+
label: this.applicationLabel(application.status),
|
|
639
|
+
routerLink: this.applicationRouterLink(application),
|
|
640
|
+
};
|
|
641
|
+
}
|
|
642
|
+
if (!this.showApplyAction()) {
|
|
643
|
+
return null;
|
|
644
|
+
}
|
|
645
|
+
return {
|
|
646
|
+
icon: 'badge',
|
|
647
|
+
label: 'Apply as agent',
|
|
648
|
+
routerLink: this.applyRouterLink(),
|
|
649
|
+
};
|
|
650
|
+
}, ...(ngDevMode ? [{ debugName: "entry" }] : []));
|
|
651
|
+
applicationLabel(status) {
|
|
652
|
+
switch (status) {
|
|
653
|
+
case 'APPROVED':
|
|
654
|
+
return 'Agent application approved';
|
|
655
|
+
case 'FAILED':
|
|
656
|
+
return 'Agent application result';
|
|
657
|
+
case 'NEED_MORE_INFO':
|
|
658
|
+
return 'Agent application updates';
|
|
659
|
+
case 'SUBMITTED':
|
|
660
|
+
case 'IN_REVIEW':
|
|
661
|
+
return 'Agent application review';
|
|
662
|
+
default:
|
|
663
|
+
return 'Continue agent application';
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
applicationRouterLink(application) {
|
|
667
|
+
const route = this.canOpenReview(application) ? 'review' : 'form';
|
|
668
|
+
return ['/agents', application.id, route];
|
|
669
|
+
}
|
|
670
|
+
canOpenReview(application) {
|
|
671
|
+
const progress = application.progress;
|
|
672
|
+
if (progress && !this.isFormComplete(progress)) {
|
|
673
|
+
return false;
|
|
674
|
+
}
|
|
675
|
+
return application.status !== 'DRAFT' || this.isFormComplete(progress);
|
|
676
|
+
}
|
|
677
|
+
isFormComplete(progress) {
|
|
678
|
+
return Boolean(progress?.profileCompleted && progress.qualificationCompleted && progress.financialCompleted && progress.bankingCompleted);
|
|
679
|
+
}
|
|
680
|
+
toTimestamp(application) {
|
|
681
|
+
return Date.parse(application.updatedAt ?? application.createdAt ?? '') || 0;
|
|
682
|
+
}
|
|
683
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: AgentAccessMenuComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
684
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: AgentAccessMenuComponent, isStandalone: true, selector: "rolatech-agent-access-menu", inputs: { applicationCode: { classPropertyName: "applicationCode", publicName: "applicationCode", isSignal: true, isRequired: false, transformFunction: null }, applyRouterLink: { classPropertyName: "applyRouterLink", publicName: "applyRouterLink", isSignal: true, isRequired: false, transformFunction: null }, dashboardRouterLink: { classPropertyName: "dashboardRouterLink", publicName: "dashboardRouterLink", isSignal: true, isRequired: false, transformFunction: null }, showApplyAction: { classPropertyName: "showApplyAction", publicName: "showApplyAction", isSignal: true, isRequired: false, transformFunction: null }, dashboardLabel: { classPropertyName: "dashboardLabel", publicName: "dashboardLabel", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
|
|
685
|
+
@if (entry(); as item) {
|
|
686
|
+
<a mat-menu-item [routerLink]="item.routerLink" class="px-6 flex items-center cursor-pointer">
|
|
687
|
+
<mat-icon>{{ item.icon }}</mat-icon>
|
|
688
|
+
<span class="flex items-center pl-1">{{ item.label }}</span>
|
|
689
|
+
</a>
|
|
690
|
+
}
|
|
691
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i1$1.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
692
|
+
}
|
|
693
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: AgentAccessMenuComponent, decorators: [{
|
|
694
|
+
type: Component,
|
|
695
|
+
args: [{
|
|
696
|
+
selector: 'rolatech-agent-access-menu',
|
|
697
|
+
imports: [MatMenuModule, MatIconModule, RouterLink],
|
|
698
|
+
template: `
|
|
699
|
+
@if (entry(); as item) {
|
|
700
|
+
<a mat-menu-item [routerLink]="item.routerLink" class="px-6 flex items-center cursor-pointer">
|
|
701
|
+
<mat-icon>{{ item.icon }}</mat-icon>
|
|
702
|
+
<span class="flex items-center pl-1">{{ item.label }}</span>
|
|
703
|
+
</a>
|
|
704
|
+
}
|
|
705
|
+
`,
|
|
706
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
707
|
+
}]
|
|
708
|
+
}], propDecorators: { applicationCode: [{ type: i0.Input, args: [{ isSignal: true, alias: "applicationCode", required: false }] }], applyRouterLink: [{ type: i0.Input, args: [{ isSignal: true, alias: "applyRouterLink", required: false }] }], dashboardRouterLink: [{ type: i0.Input, args: [{ isSignal: true, alias: "dashboardRouterLink", required: false }] }], showApplyAction: [{ type: i0.Input, args: [{ isSignal: true, alias: "showApplyAction", required: false }] }], dashboardLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "dashboardLabel", required: false }] }] } });
|
|
709
|
+
|
|
459
710
|
class FaceidDetectDialogComponent {
|
|
460
711
|
dialogRef = inject(MatDialogRef);
|
|
461
712
|
environment = inject(APP_CONFIG);
|
|
@@ -543,7 +794,7 @@ class AddressComponent {
|
|
|
543
794
|
this.output.emit(this.address());
|
|
544
795
|
}
|
|
545
796
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: AddressComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
546
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: AddressComponent, isStandalone: true, selector: "rolatech-address", inputs: { address: { classPropertyName: "address", publicName: "address", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { address: "addressChange", output: "output" }, ngImport: i0, template: "<div>\n <mat-form-field appearance=\"fill\">\n <mat-label i18n> Name </mat-label>\n <input matInput [(ngModel)]=\"address().name\" />\n </mat-form-field>\n @if (address().type === addressType.BILLING) {\n <mat-form-field appearance=\"fill\">\n <mat-label i18n> Email </mat-label>\n <input matInput [(ngModel)]=\"address().email\" />\n </mat-form-field>\n }\n <mat-form-field appearance=\"fill\">\n <mat-label i18n> Phone </mat-label>\n <input matInput [(ngModel)]=\"address().phone\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label i18n> province </mat-label>\n <input matInput [(ngModel)]=\"address().province\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label i18n> City </mat-label>\n <input matInput [(ngModel)]=\"address().city\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label i18n> District </mat-label>\n <input matInput [(ngModel)]=\"address().district\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label i18n> Street </mat-label>\n <input matInput [(ngModel)]=\"address().street\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label i18n> Detail </mat-label>\n <input matInput [(ngModel)]=\"address().detail\" />\n </mat-form-field>\n</div>\n", styles: ["mat-form-field{width:100%}\n"], dependencies: [{ kind: "ngmodule", type: AngularCommonModule }, { kind: "directive", type: i1.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.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: AngularComponentsModule }, { kind: "component", type: i2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }] });
|
|
797
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: AddressComponent, isStandalone: true, selector: "rolatech-address", inputs: { address: { classPropertyName: "address", publicName: "address", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { address: "addressChange", output: "output" }, ngImport: i0, template: "<div>\n <mat-form-field appearance=\"fill\">\n <mat-label i18n> Name </mat-label>\n <input matInput [(ngModel)]=\"address().name\" />\n </mat-form-field>\n @if (address().type === addressType.BILLING) {\n <mat-form-field appearance=\"fill\">\n <mat-label i18n> Email </mat-label>\n <input matInput [(ngModel)]=\"address().email\" />\n </mat-form-field>\n }\n <mat-form-field appearance=\"fill\">\n <mat-label i18n> Phone </mat-label>\n <input matInput [(ngModel)]=\"address().phone\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label i18n> province </mat-label>\n <input matInput [(ngModel)]=\"address().province\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label i18n> City </mat-label>\n <input matInput [(ngModel)]=\"address().city\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label i18n> District </mat-label>\n <input matInput [(ngModel)]=\"address().district\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label i18n> Street </mat-label>\n <input matInput [(ngModel)]=\"address().street\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label i18n> Detail </mat-label>\n <input matInput [(ngModel)]=\"address().detail\" />\n </mat-form-field>\n</div>\n", styles: ["mat-form-field{width:100%}\n"], dependencies: [{ kind: "ngmodule", type: AngularCommonModule }, { kind: "directive", type: i1$2.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$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: AngularComponentsModule }, { kind: "component", type: i2$2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$2.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }] });
|
|
547
798
|
}
|
|
548
799
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: AddressComponent, decorators: [{
|
|
549
800
|
type: Component,
|
|
@@ -565,8 +816,7 @@ const AuthGuard = (route, state) => {
|
|
|
565
816
|
const routeRoles = route.data['roles'];
|
|
566
817
|
const passRoled = routeRoles ? roles?.some((r) => routeRoles.indexOf(r) >= 0) : true;
|
|
567
818
|
if (authenticated && !passRoled) {
|
|
568
|
-
router.
|
|
569
|
-
return false;
|
|
819
|
+
return createForbiddenUrlTree(router, state.url, 'role');
|
|
570
820
|
}
|
|
571
821
|
if (authenticated) {
|
|
572
822
|
if (isSignInPage) {
|
|
@@ -584,6 +834,7 @@ const AuthGuard = (route, state) => {
|
|
|
584
834
|
return false;
|
|
585
835
|
}
|
|
586
836
|
// 3. Otherwise, if the user is already where they need to be, allow navigation
|
|
837
|
+
rememberAllowedUrl(state.url);
|
|
587
838
|
return true;
|
|
588
839
|
}
|
|
589
840
|
else {
|
|
@@ -671,32 +922,37 @@ function matchesRouteAccessPolicy(authStore, authContextStore, policy) {
|
|
|
671
922
|
}
|
|
672
923
|
|
|
673
924
|
function accessGuard(policy, fallbackUrl = '/forbidden') {
|
|
674
|
-
return () => {
|
|
925
|
+
return (_route, state) => {
|
|
675
926
|
const router = inject(Router);
|
|
676
927
|
const authService = inject(AuthService);
|
|
677
928
|
const authStore = inject(AuthStore);
|
|
678
929
|
const authContextStore = inject(AuthContextStore);
|
|
679
|
-
return authService.ensureLoaded().pipe(map(() =>
|
|
930
|
+
return authService.ensureLoaded().pipe(map(() => {
|
|
931
|
+
if (matchesRouteAccessPolicy(authStore, authContextStore, policy)) {
|
|
932
|
+
rememberAllowedUrl(state.url);
|
|
933
|
+
return true;
|
|
934
|
+
}
|
|
935
|
+
return fallbackUrl === '/forbidden' ? createForbiddenUrlTree(router, state.url, 'policy') : router.parseUrl(fallbackUrl);
|
|
936
|
+
}));
|
|
680
937
|
};
|
|
681
938
|
}
|
|
682
939
|
function accessMatchGuard(policy, fallbackUrl = '/forbidden') {
|
|
683
|
-
return () => {
|
|
940
|
+
return (_route, segments) => {
|
|
684
941
|
const router = inject(Router);
|
|
685
942
|
const authService = inject(AuthService);
|
|
686
943
|
const authStore = inject(AuthStore);
|
|
687
944
|
const authContextStore = inject(AuthContextStore);
|
|
688
|
-
|
|
945
|
+
const attemptedUrl = segments.length ? `/${segments.map((segment) => segment.path).join('/')}` : router.url;
|
|
946
|
+
return authService.ensureLoaded().pipe(map(() => {
|
|
947
|
+
if (matchesRouteAccessPolicy(authStore, authContextStore, policy)) {
|
|
948
|
+
rememberAllowedUrl(router.url);
|
|
949
|
+
return true;
|
|
950
|
+
}
|
|
951
|
+
return fallbackUrl === '/forbidden' ? createForbiddenUrlTree(router, attemptedUrl, 'policy') : router.parseUrl(fallbackUrl);
|
|
952
|
+
}));
|
|
689
953
|
};
|
|
690
954
|
}
|
|
691
955
|
|
|
692
|
-
const PLATFORM_ADMIN_ROLES = ['PLATFORM_ADMIN', 'ROLE_PLATFORM_ADMIN'];
|
|
693
|
-
const APPLICATION_OWNER_ROLES = ['APPLICATION_OWNER', 'APP_OWNER', 'ROLE_APPLICATION_OWNER', 'ROLE_APP_OWNER'];
|
|
694
|
-
const APPLICATION_ADMIN_ROLES = ['APPLICATION_ADMIN', 'APP_ADMIN', 'ROLE_APPLICATION_ADMIN', 'ROLE_APP_ADMIN'];
|
|
695
|
-
const ORGANIZATION_OWNER_ROLES = ['ORGANIZATION_OWNER', 'ORG_OWNER', 'ROLE_ORGANIZATION_OWNER', 'ROLE_ORG_OWNER'];
|
|
696
|
-
const ORGANIZATION_ADMIN_ROLES = ['ORGANIZATION_ADMIN', 'ORG_ADMIN', 'ROLE_ORGANIZATION_ADMIN', 'ROLE_ORG_ADMIN'];
|
|
697
|
-
const ORGANIZATION_MEMBER_ROLES = ['ORGANIZATION_MEMBER', 'ORG_MEMBER', 'ROLE_ORGANIZATION_MEMBER', 'ROLE_ORG_MEMBER'];
|
|
698
|
-
const ORGANIZATION_STAFF_ROLES = ['ORGANIZATION_STAFF', 'ORG_STAFF', 'ROLE_ORGANIZATION_STAFF', 'ROLE_ORG_STAFF'];
|
|
699
|
-
|
|
700
956
|
function landingRedirectGuard(targets = {}) {
|
|
701
957
|
const resolvedTargets = {
|
|
702
958
|
platform: targets.platform ?? '/platform',
|
|
@@ -728,25 +984,19 @@ function landingRedirectGuard(targets = {}) {
|
|
|
728
984
|
|
|
729
985
|
const RoleGuard = (route, state) => {
|
|
730
986
|
const authService = inject(AuthService);
|
|
731
|
-
const environment = inject(APP_CONFIG);
|
|
732
987
|
const platformId = inject(PLATFORM_ID);
|
|
733
|
-
const snackBar = inject(MatSnackBar);
|
|
734
988
|
const router = inject(Router);
|
|
735
|
-
const isContinue = state.url.includes('continue');
|
|
736
|
-
const continueUrl = route.queryParams['continue'];
|
|
737
989
|
if (!isPlatformBrowser(platformId)) {
|
|
738
990
|
return of(false);
|
|
739
991
|
}
|
|
740
992
|
const routeRoles = route.data['roles'];
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
if (!authed) {
|
|
747
|
-
router.navigate(['/forbidden']);
|
|
993
|
+
return authService.ensureLoaded().pipe(map$1(({ roles }) => {
|
|
994
|
+
const authed = routeRoles ? (roles ?? []).some((role) => routeRoles.indexOf(role) >= 0) : true;
|
|
995
|
+
if (authed) {
|
|
996
|
+
rememberAllowedUrl(state.url);
|
|
997
|
+
return true;
|
|
748
998
|
}
|
|
749
|
-
return
|
|
999
|
+
return createForbiddenUrlTree(router, state.url, 'role');
|
|
750
1000
|
}));
|
|
751
1001
|
};
|
|
752
1002
|
|
|
@@ -1279,20 +1529,23 @@ function provideRoleAwareShellRouter(config, ...features) {
|
|
|
1279
1529
|
];
|
|
1280
1530
|
return makeEnvironmentProviders(providers);
|
|
1281
1531
|
}
|
|
1282
|
-
function
|
|
1532
|
+
function provideRoleAwareShell(config, ...features) {
|
|
1283
1533
|
return makeEnvironmentProviders([
|
|
1284
|
-
provideRoleAwareShellRouter(
|
|
1285
|
-
...config,
|
|
1286
|
-
shellCanActivate: config.shellCanActivate ?? [AuthGuard],
|
|
1287
|
-
shellCanActivateChild: config.shellCanActivateChild ?? [AuthGuard],
|
|
1288
|
-
}, ...features),
|
|
1534
|
+
provideRoleAwareShellRouter(config, ...features),
|
|
1289
1535
|
...(config.navigation?.length ? [provideRoleNavigation(config.navigation)] : []),
|
|
1290
1536
|
]);
|
|
1291
1537
|
}
|
|
1538
|
+
function provideRoleAwareConsoleShell(config, ...features) {
|
|
1539
|
+
return provideRoleAwareShell({
|
|
1540
|
+
...config,
|
|
1541
|
+
shellCanActivate: config.shellCanActivate ?? [AuthGuard],
|
|
1542
|
+
shellCanActivateChild: config.shellCanActivateChild ?? [AuthGuard],
|
|
1543
|
+
}, ...features);
|
|
1544
|
+
}
|
|
1292
1545
|
|
|
1293
1546
|
/**
|
|
1294
1547
|
* Generated bundle index. Do not edit.
|
|
1295
1548
|
*/
|
|
1296
1549
|
|
|
1297
|
-
export { APPLICATION_ADMIN_ROLES, APPLICATION_OWNER_ROLES, AUTH_METHODS, AddressComponent, AddressType, AppNavigationService, AuthAgentService, AuthContextStore, AuthDialogGuard, AuthGuard, AuthInterceptor, AuthMethod, AuthService, AuthStore, AuthUserService, ErrorInterceptor, FaceidDetectDialogComponent, ForbiddenComponent, LocalStorageService, ORGANIZATION_ADMIN_ROLES, ORGANIZATION_MEMBER_ROLES, ORGANIZATION_OWNER_ROLES, ORGANIZATION_STAFF_ROLES, PLATFORM_ADMIN_ROLES, PermissionGuard, ROLE_NAVIGATION_LINKS, ROLE_SHELL_CHILD_ROUTES, RoleGuard, UnauthorizedComponent, UserStatus, accessGuard, accessMatchGuard, authRoutes, landingRedirectGuard, provideAngularAuth, provideRoleAwareConsoleShell, provideRoleAwareFeature, provideRoleAwareShellRouter, provideRoleNavigation, provideRoleShellRoutes };
|
|
1550
|
+
export { APPLICATION_ADMIN_ROLES, APPLICATION_OWNER_ROLES, AUTH_METHODS, AddressComponent, AddressType, AgentAccessMenuComponent, AppNavigationService, AuthAgentService, AuthContextStore, AuthDialogGuard, AuthGuard, AuthInterceptor, AuthMethod, AuthService, AuthStore, AuthUserService, ErrorInterceptor, FaceidDetectDialogComponent, ForbiddenComponent, LocalStorageService, ORGANIZATION_ADMIN_ROLES, ORGANIZATION_MEMBER_ROLES, ORGANIZATION_OWNER_ROLES, ORGANIZATION_STAFF_ROLES, PLATFORM_ADMIN_ROLES, PermissionGuard, ROLE_NAVIGATION_LINKS, ROLE_SHELL_CHILD_ROUTES, RoleGuard, UnauthorizedComponent, UserStatus, accessGuard, accessMatchGuard, authRoutes, landingRedirectGuard, provideAngularAuth, provideRoleAwareConsoleShell, provideRoleAwareFeature, provideRoleAwareShell, provideRoleAwareShellRouter, provideRoleNavigation, provideRoleShellRoutes };
|
|
1298
1551
|
//# sourceMappingURL=rolatech-angular-auth.mjs.map
|