RED-6713 remove tenants context holder
This commit is contained in:
parent
dbfde290c8
commit
d0551742ec
@ -17,7 +17,7 @@ import { IqserPermissionsService } from './permissions.service';
|
|||||||
import { IqserRolesService } from './roles.service';
|
import { IqserRolesService } from './roles.service';
|
||||||
import { isArray, isFunction, isRedirectWithParameters, isString, transformPermission } from '../utils';
|
import { isArray, isFunction, isRedirectWithParameters, isString, transformPermission } from '../utils';
|
||||||
import { List } from '../../utils';
|
import { List } from '../../utils';
|
||||||
import { TenantContextHolder } from '../../tenants';
|
import { TenantsService } from '../../tenants';
|
||||||
|
|
||||||
export interface IqserPermissionsData {
|
export interface IqserPermissionsData {
|
||||||
readonly allow: string | List;
|
readonly allow: string | List;
|
||||||
@ -30,7 +30,7 @@ export interface IqserPermissionsData {
|
|||||||
export class IqserPermissionsGuard implements CanActivate, CanMatch, CanActivateChild {
|
export class IqserPermissionsGuard implements CanActivate, CanMatch, CanActivateChild {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _permissionsService: IqserPermissionsService,
|
private readonly _permissionsService: IqserPermissionsService,
|
||||||
private readonly _tenantContextHolder: TenantContextHolder,
|
private readonly _tenantsService: TenantsService,
|
||||||
private readonly _rolesService: IqserRolesService,
|
private readonly _rolesService: IqserRolesService,
|
||||||
private readonly _router: Router,
|
private readonly _router: Router,
|
||||||
) {}
|
) {}
|
||||||
@ -85,10 +85,10 @@ export class IqserPermissionsGuard implements CanActivate, CanMatch, CanActivate
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (Array.isArray(_redirectTo)) {
|
if (Array.isArray(_redirectTo)) {
|
||||||
return this._router.navigate([this._tenantContextHolder.currentTenant, ..._redirectTo]);
|
return this._router.navigate([this._tenantsService.currentTenant, ..._redirectTo]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this._router.navigate([`${this._tenantContextHolder.currentTenant}${_redirectTo}`]);
|
return this._router.navigate([`${this._tenantsService.currentTenant}${_redirectTo}`]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#checkRedirect(permissions: IqserPermissionsData, route: ActivatedRouteSnapshot | Route, state?: RouterStateSnapshot) {
|
#checkRedirect(permissions: IqserPermissionsData, route: ActivatedRouteSnapshot | Route, state?: RouterStateSnapshot) {
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
export * from './tenant-context-holder';
|
|
||||||
export * from './tenants.service';
|
export * from './tenants.service';
|
||||||
export * from './tenant-id-interceptor';
|
export * from './tenant-id-interceptor';
|
||||||
export * from './tenant-id-response-interceptor';
|
export * from './tenant-id-response-interceptor';
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { inject, Injectable } from '@angular/core';
|
import { inject, Injectable } from '@angular/core';
|
||||||
import { KeycloakService } from 'keycloak-angular';
|
import { KeycloakService } from 'keycloak-angular';
|
||||||
import { getConfig } from '../../services';
|
import { getConfig } from '../../services';
|
||||||
import { TenantContextHolder } from '../index';
|
import { TenantsService } from '../index';
|
||||||
import { BASE_HREF } from '../../utils';
|
import { BASE_HREF } from '../../utils';
|
||||||
import { NGXLogger } from 'ngx-logger';
|
import { NGXLogger } from 'ngx-logger';
|
||||||
|
|
||||||
@ -9,7 +9,7 @@ import { NGXLogger } from 'ngx-logger';
|
|||||||
export class KeycloakStatusService {
|
export class KeycloakStatusService {
|
||||||
readonly #keycloakService = inject(KeycloakService);
|
readonly #keycloakService = inject(KeycloakService);
|
||||||
readonly #config = getConfig();
|
readonly #config = getConfig();
|
||||||
readonly #tenantContextHolder = inject(TenantContextHolder);
|
readonly #tenantsService = inject(TenantsService);
|
||||||
readonly #baseHref = inject(BASE_HREF);
|
readonly #baseHref = inject(BASE_HREF);
|
||||||
readonly #logger = inject(NGXLogger);
|
readonly #logger = inject(NGXLogger);
|
||||||
|
|
||||||
@ -30,7 +30,7 @@ export class KeycloakStatusService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
createLoginUrl() {
|
createLoginUrl() {
|
||||||
const currentTenant = this.#tenantContextHolder.currentTenant;
|
const currentTenant = this.#tenantsService.currentTenant;
|
||||||
if (currentTenant && window.location.href.indexOf('/' + currentTenant) > 0) {
|
if (currentTenant && window.location.href.indexOf('/' + currentTenant) > 0) {
|
||||||
return window.location.href;
|
return window.location.href;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +0,0 @@
|
|||||||
import { inject, Injectable } from '@angular/core';
|
|
||||||
import { TenantsService } from './tenants.service';
|
|
||||||
|
|
||||||
@Injectable({ providedIn: 'root' })
|
|
||||||
export class TenantContextHolder {
|
|
||||||
readonly #tenantsService = inject(TenantsService);
|
|
||||||
|
|
||||||
get currentTenant() {
|
|
||||||
return this.#tenantsService.currentTenant;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,16 +1,16 @@
|
|||||||
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
|
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
|
||||||
import { inject, Injectable } from '@angular/core';
|
import { inject, Injectable } from '@angular/core';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { TenantContextHolder } from './tenant-context-holder';
|
import { TenantsService } from './tenants.service';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class TenantIdInterceptor implements HttpInterceptor {
|
export class TenantIdInterceptor implements HttpInterceptor {
|
||||||
protected readonly _tenantContext = inject(TenantContextHolder);
|
protected readonly _tenantsService = inject(TenantsService);
|
||||||
|
|
||||||
intercept(req: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
|
intercept(req: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
|
||||||
if (this._tenantContext.currentTenant) {
|
if (this._tenantsService.currentTenant) {
|
||||||
const updatedRequest = req.clone({
|
const updatedRequest = req.clone({
|
||||||
setHeaders: { 'X-TENANT-ID': this._tenantContext.currentTenant },
|
setHeaders: { 'X-TENANT-ID': this._tenantsService.currentTenant },
|
||||||
});
|
});
|
||||||
|
|
||||||
return next.handle(updatedRequest);
|
return next.handle(updatedRequest);
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { inject, Injectable } from '@angular/core';
|
import { computed, inject, Injectable, signal } from '@angular/core';
|
||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
import { BehaviorSubject, firstValueFrom } from 'rxjs';
|
import { firstValueFrom } from 'rxjs';
|
||||||
import { NGXLogger } from 'ngx-logger';
|
import { NGXLogger } from 'ngx-logger';
|
||||||
import { BASE_HREF } from '../../utils';
|
import { BASE_HREF } from '../../utils';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
@ -13,8 +13,11 @@ export interface IBaseTenant {
|
|||||||
|
|
||||||
@Injectable({ providedIn: 'root' })
|
@Injectable({ providedIn: 'root' })
|
||||||
export class TenantsService {
|
export class TenantsService {
|
||||||
hasMultipleTenants = false;
|
readonly tenants = signal<IBaseTenant[] | undefined>(undefined);
|
||||||
readonly tenantData$ = new BehaviorSubject<IBaseTenant[] | undefined>(undefined);
|
readonly hasMultiple = computed(() => {
|
||||||
|
const tenants = this.tenants();
|
||||||
|
return tenants ? tenants.length > 1 : false;
|
||||||
|
});
|
||||||
readonly #http = inject(HttpClient);
|
readonly #http = inject(HttpClient);
|
||||||
readonly #router = inject(Router);
|
readonly #router = inject(Router);
|
||||||
readonly #logger = inject(NGXLogger);
|
readonly #logger = inject(NGXLogger);
|
||||||
@ -27,17 +30,16 @@ export class TenantsService {
|
|||||||
removeItem: localStorage.removeItem.bind(localStorage),
|
removeItem: localStorage.removeItem.bind(localStorage),
|
||||||
key: localStorage.key.bind(localStorage),
|
key: localStorage.key.bind(localStorage),
|
||||||
};
|
};
|
||||||
readonly #activeTenantId$ = new BehaviorSubject<string>('');
|
readonly #activeTenantId = signal<string>('');
|
||||||
|
|
||||||
get currentTenant() {
|
get currentTenant() {
|
||||||
return this.#activeTenantId$.value;
|
return this.#activeTenantId();
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadTenants() {
|
async loadTenants() {
|
||||||
this.#logger.info('[TENANTS] Loading tenants...');
|
this.#logger.info('[TENANTS] Loading tenants...');
|
||||||
const tenants = await firstValueFrom(this.#http.get<IBaseTenant[]>('/tenants/simple'));
|
const tenants = await firstValueFrom(this.#http.get<IBaseTenant[]>('/tenants/simple'));
|
||||||
this.hasMultipleTenants = tenants.length > 1;
|
this.tenants.set(tenants);
|
||||||
this.tenantData$.next(tenants);
|
|
||||||
|
|
||||||
const tenant = this.getTenantFromRoute();
|
const tenant = this.getTenantFromRoute();
|
||||||
if (tenant) {
|
if (tenant) {
|
||||||
@ -50,7 +52,7 @@ export class TenantsService {
|
|||||||
|
|
||||||
this.#logger.info('[TENANTS] No tenant in route');
|
this.#logger.info('[TENANTS] No tenant in route');
|
||||||
|
|
||||||
if (!this.hasMultipleTenants) {
|
if (!this.hasMultiple) {
|
||||||
this.#logger.info('[TENANTS] Only one tenant loaded, auto-select it and redirect to login page');
|
this.#logger.info('[TENANTS] Only one tenant loaded, auto-select it and redirect to login page');
|
||||||
const tenant = tenants[0].tenantId;
|
const tenant = tenants[0].tenantId;
|
||||||
if (await this.selectTenant(tenant)) {
|
if (await this.selectTenant(tenant)) {
|
||||||
@ -66,7 +68,7 @@ export class TenantsService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async selectTenant(tenantId: string) {
|
async selectTenant(tenantId: string) {
|
||||||
const tenants = this.tenantData$.value;
|
const tenants = this.tenants();
|
||||||
if (!tenants) {
|
if (!tenants) {
|
||||||
throw new Error('Tenants not loaded!');
|
throw new Error('Tenants not loaded!');
|
||||||
}
|
}
|
||||||
@ -80,13 +82,13 @@ export class TenantsService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.#mutateStorage(tenantId);
|
this.#mutateStorage(tenantId);
|
||||||
this.setCurrentTenantId(tenantId);
|
this.#setCurrentTenantId(tenantId);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
setCurrentTenantId(tenantId: string) {
|
#setCurrentTenantId(tenantId: string) {
|
||||||
this.#logger.info('[TENANTS] Set current tenant id: ', tenantId);
|
this.#logger.info('[TENANTS] Set current tenant id: ', tenantId);
|
||||||
this.#activeTenantId$.next(tenantId);
|
this.#activeTenantId.set(tenantId);
|
||||||
}
|
}
|
||||||
|
|
||||||
#mutateStorage(tenant: string) {
|
#mutateStorage(tenant: string) {
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { inject, Pipe, PipeTransform } from '@angular/core';
|
import { inject, Pipe, PipeTransform } from '@angular/core';
|
||||||
import { TenantContextHolder } from './services';
|
import { TenantsService } from './services';
|
||||||
|
|
||||||
@Pipe({
|
@Pipe({
|
||||||
name: 'tenant',
|
name: 'tenant',
|
||||||
@ -7,13 +7,13 @@ import { TenantContextHolder } from './services';
|
|||||||
standalone: true,
|
standalone: true,
|
||||||
})
|
})
|
||||||
export class TenantPipe implements PipeTransform {
|
export class TenantPipe implements PipeTransform {
|
||||||
readonly #tenant = inject(TenantContextHolder);
|
readonly #tenantsService = inject(TenantsService);
|
||||||
|
|
||||||
transform(value: string | string[]): string | undefined {
|
transform(value: string | string[]): string | undefined {
|
||||||
if (!value) {
|
if (!value) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
const _value = Array.isArray(value) ? value.join('/') : value;
|
const _value = Array.isArray(value) ? value.join('/') : value;
|
||||||
return '/' + this.#tenant.currentTenant + _value;
|
return '/' + this.#tenantsService.currentTenant + _value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,18 +1,20 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { ActivatedRouteSnapshot, Router } from '@angular/router';
|
import { ActivatedRouteSnapshot, Router } from '@angular/router';
|
||||||
import { KeycloakAuthGuard, KeycloakService } from 'keycloak-angular';
|
import { KeycloakAuthGuard, KeycloakService } from 'keycloak-angular';
|
||||||
import { IqserConfigService } from '../../services';
|
import { getConfig } from '../../services';
|
||||||
import { IqserUserService } from '../services/iqser-user.service';
|
import { IqserUserService } from '../services/iqser-user.service';
|
||||||
import { TenantContextHolder } from '../../tenants';
|
import { TenantsService } from '../../tenants';
|
||||||
|
import { KeycloakLoginOptions } from 'keycloak-js';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class IqserAuthGuard extends KeycloakAuthGuard {
|
export class IqserAuthGuard extends KeycloakAuthGuard {
|
||||||
|
readonly #config = getConfig();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected readonly _router: Router,
|
protected readonly _router: Router,
|
||||||
protected readonly _keycloak: KeycloakService,
|
protected readonly _keycloak: KeycloakService,
|
||||||
private readonly _configService: IqserConfigService,
|
|
||||||
private readonly _userService: IqserUserService,
|
private readonly _userService: IqserUserService,
|
||||||
private readonly _tenantContextHolder: TenantContextHolder,
|
private readonly _tenantsService: TenantsService,
|
||||||
) {
|
) {
|
||||||
super(_router, _keycloak);
|
super(_router, _keycloak);
|
||||||
}
|
}
|
||||||
@ -20,11 +22,12 @@ export class IqserAuthGuard extends KeycloakAuthGuard {
|
|||||||
async isAccessAllowed(route: ActivatedRouteSnapshot): Promise<boolean> {
|
async isAccessAllowed(route: ActivatedRouteSnapshot): Promise<boolean> {
|
||||||
if (!this.authenticated) {
|
if (!this.authenticated) {
|
||||||
const kcIdpHint = route.queryParamMap.get('kc_idp_hint');
|
const kcIdpHint = route.queryParamMap.get('kc_idp_hint');
|
||||||
const options: any = {
|
const options: KeycloakLoginOptions = {
|
||||||
redirectUri: window.location.href,
|
redirectUri: window.location.href,
|
||||||
};
|
};
|
||||||
if (kcIdpHint ?? this._configService.values.OAUTH_IDP_HINT) {
|
|
||||||
options.idpHint = kcIdpHint ?? this._configService.values.OAUTH_IDP_HINT;
|
if (kcIdpHint ?? this.#config.OAUTH_IDP_HINT) {
|
||||||
|
options.idpHint = kcIdpHint ?? this.#config.OAUTH_IDP_HINT;
|
||||||
}
|
}
|
||||||
await this._keycloak.login(options);
|
await this._keycloak.login(options);
|
||||||
return false;
|
return false;
|
||||||
@ -32,7 +35,7 @@ export class IqserAuthGuard extends KeycloakAuthGuard {
|
|||||||
|
|
||||||
const user = await this._userService.loadCurrentUser();
|
const user = await this._userService.loadCurrentUser();
|
||||||
if (user?.hasAnyRole && route.routeConfig?.path === 'auth-error') {
|
if (user?.hasAnyRole && route.routeConfig?.path === 'auth-error') {
|
||||||
await this._router.navigate([`/${this._tenantContextHolder.currentTenant}/main`]);
|
await this._router.navigate([`/${this._tenantsService.currentTenant}/main`]);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -2,19 +2,19 @@ import { inject, Injectable } from '@angular/core';
|
|||||||
import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router';
|
import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router';
|
||||||
import { LoadingService } from '../../loading';
|
import { LoadingService } from '../../loading';
|
||||||
import { IqserUserService } from '../services/iqser-user.service';
|
import { IqserUserService } from '../services/iqser-user.service';
|
||||||
import { TenantContextHolder } from '../../tenants';
|
import { TenantsService } from '../../tenants';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class IqserRoleGuard implements CanActivate {
|
export class IqserRoleGuard implements CanActivate {
|
||||||
protected readonly _router = inject(Router);
|
protected readonly _router = inject(Router);
|
||||||
protected readonly _tenantContextHolder = inject(TenantContextHolder);
|
protected readonly _tenantsService = inject(TenantsService);
|
||||||
protected readonly _loadingService = inject(LoadingService);
|
protected readonly _loadingService = inject(LoadingService);
|
||||||
protected readonly _userService = inject(IqserUserService);
|
protected readonly _userService = inject(IqserUserService);
|
||||||
|
|
||||||
async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
|
async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
|
||||||
const currentUser = this._userService.currentUser;
|
const currentUser = this._userService.currentUser;
|
||||||
if (!currentUser || !currentUser.hasAnyRole) {
|
if (!currentUser || !currentUser.hasAnyRole) {
|
||||||
await this._router.navigate([`/${this._tenantContextHolder.currentTenant}/auth-error`]);
|
await this._router.navigate([`/${this._tenantsService.currentTenant}/auth-error`]);
|
||||||
this._loadingService.stop();
|
this._loadingService.stop();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user