From 553a9578e0094c2265401f726d29ccd4752dd258 Mon Sep 17 00:00:00 2001 From: bnymncoskuner Date: Mon, 12 Jul 2021 18:47:12 +0300 Subject: [PATCH] feat: add token for tenant key and resolve it from url --- .../packages/core/src/lib/core.module.ts | 2 + .../src/lib/interceptors/api.interceptor.ts | 8 +-- .../packages/core/src/lib/models/common.ts | 6 +-- .../abp/multi-tenancy/abp-tenant.service.ts | 3 +- .../src/lib/services/config-state.service.ts | 9 +++- .../src/lib/services/multi-tenancy.service.ts | 47 ++++++++++------- .../src/lib/strategies/auth-flow.strategy.ts | 5 +- .../packages/core/src/lib/tokens/index.ts | 1 + .../core/src/lib/tokens/tenant-key.token.ts | 3 ++ .../core/src/lib/utils/multi-tenancy-utils.ts | 51 ++++++++++++------- 10 files changed, 90 insertions(+), 45 deletions(-) create mode 100644 npm/ng-packs/packages/core/src/lib/tokens/tenant-key.token.ts diff --git a/npm/ng-packs/packages/core/src/lib/core.module.ts b/npm/ng-packs/packages/core/src/lib/core.module.ts index 986cb98f3b..5f986789f3 100644 --- a/npm/ng-packs/packages/core/src/lib/core.module.ts +++ b/npm/ng-packs/packages/core/src/lib/core.module.ts @@ -33,6 +33,7 @@ import { coreOptionsFactory, CORE_OPTIONS } from './tokens/options.token'; import { noop } from './utils/common-utils'; import './utils/date-extensions'; import { getInitialData, localeInitializer } from './utils/initial-utils'; +import { TENANT_KEY } from './tokens/tenant-key.token'; export function storageFactory(): OAuthStorage { return oAuthStorage; @@ -178,6 +179,7 @@ export class CoreModule { useFactory: noop, }, { provide: OAuthStorage, useFactory: storageFactory }, + { provide: TENANT_KEY, useValue: options.tenantKey || '__tenant' }, ], }; } diff --git a/npm/ng-packs/packages/core/src/lib/interceptors/api.interceptor.ts b/npm/ng-packs/packages/core/src/lib/interceptors/api.interceptor.ts index ad60178a98..da73b1ba55 100644 --- a/npm/ng-packs/packages/core/src/lib/interceptors/api.interceptor.ts +++ b/npm/ng-packs/packages/core/src/lib/interceptors/api.interceptor.ts @@ -1,9 +1,10 @@ import { HttpHandler, HttpHeaders, HttpInterceptor, HttpRequest } from '@angular/common/http'; -import { Injectable } from '@angular/core'; +import { Injectable, Inject } from '@angular/core'; import { OAuthService } from 'angular-oauth2-oidc'; import { finalize } from 'rxjs/operators'; import { SessionStateService } from '../services/session-state.service'; import { HttpWaitService } from '../services/http-wait.service'; +import { TENANT_KEY } from '../tokens/tenant-key.token'; @Injectable({ providedIn: 'root', @@ -13,6 +14,7 @@ export class ApiInterceptor implements HttpInterceptor { private oAuthService: OAuthService, private sessionState: SessionStateService, private httpWaitService: HttpWaitService, + @Inject(TENANT_KEY) private tenantKey: string, ) {} intercept(request: HttpRequest, next: HttpHandler) { @@ -40,8 +42,8 @@ export class ApiInterceptor implements HttpInterceptor { } const tenant = this.sessionState.getTenant(); - if (!existingHeaders?.has('__tenant') && tenant?.id) { - headers['__tenant'] = tenant.id; + if (!existingHeaders?.has(this.tenantKey) && tenant?.id) { + headers[this.tenantKey] = tenant.id; } return headers; diff --git a/npm/ng-packs/packages/core/src/lib/models/common.ts b/npm/ng-packs/packages/core/src/lib/models/common.ts index db03b1cea7..d99d591ad4 100644 --- a/npm/ng-packs/packages/core/src/lib/models/common.ts +++ b/npm/ng-packs/packages/core/src/lib/models/common.ts @@ -10,6 +10,7 @@ export namespace ABP { registerLocaleFn: (locale: string) => Promise; skipGetAppConfiguration?: boolean; sendNullsAsQueryParam?: boolean; + tenantKey?: string; } export interface HasPolicy { @@ -74,7 +75,6 @@ export namespace ABP { [key: string]: T; } - export type ExtractFromOutput< - T extends EventEmitter | Subject - > = T extends EventEmitter ? X : T extends Subject ? Y : never; + export type ExtractFromOutput | Subject> = + T extends EventEmitter ? X : T extends Subject ? Y : never; } diff --git a/npm/ng-packs/packages/core/src/lib/proxy/pages/abp/multi-tenancy/abp-tenant.service.ts b/npm/ng-packs/packages/core/src/lib/proxy/pages/abp/multi-tenancy/abp-tenant.service.ts index a1c4d4bb9b..99aa704ed1 100644 --- a/npm/ng-packs/packages/core/src/lib/proxy/pages/abp/multi-tenancy/abp-tenant.service.ts +++ b/npm/ng-packs/packages/core/src/lib/proxy/pages/abp/multi-tenancy/abp-tenant.service.ts @@ -8,11 +8,12 @@ import type { FindTenantResultDto } from '../../../volo/abp/asp-net-core/mvc/mul export class AbpTenantService { apiName = 'abp'; - findTenantById = (id: string) => + findTenantById = (id: string, headers: Record) => this.restService.request( { method: 'GET', url: `/api/abp/multi-tenancy/tenants/by-id/${id}`, + headers, }, { apiName: this.apiName }, ); diff --git a/npm/ng-packs/packages/core/src/lib/services/config-state.service.ts b/npm/ng-packs/packages/core/src/lib/services/config-state.service.ts index 0ab66059a8..eb2a0d30b6 100644 --- a/npm/ng-packs/packages/core/src/lib/services/config-state.service.ts +++ b/npm/ng-packs/packages/core/src/lib/services/config-state.service.ts @@ -1,8 +1,9 @@ import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; -import { map } from 'rxjs/operators'; +import { map, tap } from 'rxjs/operators'; import { ApplicationConfigurationDto } from '../proxy/volo/abp/asp-net-core/mvc/application-configurations/models'; import { InternalStore } from '../utils/internal-store-utils'; +import { AbpApplicationConfigurationService } from '../proxy/volo/abp/asp-net-core/mvc/application-configurations/abp-application-configuration.service'; @Injectable({ providedIn: 'root', @@ -14,10 +15,16 @@ export class ConfigStateService { return this.store.sliceUpdate; } + constructor(private abpConfigService: AbpApplicationConfigurationService) {} + setState(state: ApplicationConfigurationDto) { this.store.set(state); } + refreshAppState() { + return this.abpConfigService.get().pipe(tap(res => this.setState(res))); + } + getOne$(key: string) { return this.store.sliceState(state => state[key]); } diff --git a/npm/ng-packs/packages/core/src/lib/services/multi-tenancy.service.ts b/npm/ng-packs/packages/core/src/lib/services/multi-tenancy.service.ts index 3789eb0847..7724106cfe 100644 --- a/npm/ng-packs/packages/core/src/lib/services/multi-tenancy.service.ts +++ b/npm/ng-packs/packages/core/src/lib/services/multi-tenancy.service.ts @@ -1,31 +1,32 @@ -import { Injectable } from '@angular/core'; +import { Injectable, Inject } from '@angular/core'; +import { switchMap, map } from 'rxjs/operators'; import { Observable } from 'rxjs'; import { ABP } from '../models/common'; -import { - CurrentTenantDto, - FindTenantResultDto, -} from '../proxy/volo/abp/asp-net-core/mvc/multi-tenancy/models'; +import { FindTenantResultDto } from '../proxy/volo/abp/asp-net-core/mvc/multi-tenancy/models'; import { RestService } from './rest.service'; +import { AbpTenantService } from '../proxy/pages/abp/multi-tenancy'; +import { ConfigStateService } from './config-state.service'; import { SessionStateService } from './session-state.service'; +import { TENANT_KEY } from '../tokens/tenant-key.token'; @Injectable({ providedIn: 'root' }) export class MultiTenancyService { - private _domainTenant: CurrentTenantDto = null; - - set domainTenant(value: CurrentTenantDto) { - this._domainTenant = value; - this.sessionState.setTenant(value); - } - - get domainTenant() { - return this._domainTenant; - } - isTenantBoxVisible = true; apiName = 'abp'; - constructor(private restService: RestService, private sessionState: SessionStateService) {} + private setTenantToState = (tenant: FindTenantResultDto) => { + this.sessionState.setTenant({ id: tenant.tenantId, name: tenant.name, isAvailable: true }); + return this.configStateService.refreshAppState().pipe(map(_ => tenant)); + }; + + constructor( + private restService: RestService, + private sessionState: SessionStateService, + private tenantService: AbpTenantService, + private configStateService: ConfigStateService, + @Inject(TENANT_KEY) public tenantKey: string, + ) {} /** * @deprecated Use AbpTenantService.findTenantByName method instead. To be deleted in v5.0. @@ -50,4 +51,16 @@ export class MultiTenancyService { { apiName: this.apiName }, ); } + + setTenantByName(tenantName: string) { + return this.tenantService + .findTenantByName(tenantName, { [this.tenantKey]: '' }) + .pipe(switchMap(this.setTenantToState)); + } + + setTenantById(tenantId: string) { + return this.tenantService + .findTenantById(tenantId, { [this.tenantKey]: '' }) + .pipe(switchMap(this.setTenantToState)); + } } diff --git a/npm/ng-packs/packages/core/src/lib/strategies/auth-flow.strategy.ts b/npm/ng-packs/packages/core/src/lib/strategies/auth-flow.strategy.ts index b9de17bf50..4cb959ac45 100644 --- a/npm/ng-packs/packages/core/src/lib/strategies/auth-flow.strategy.ts +++ b/npm/ng-packs/packages/core/src/lib/strategies/auth-flow.strategy.ts @@ -19,6 +19,7 @@ import { EnvironmentService } from '../services/environment.service'; import { SessionStateService } from '../services/session-state.service'; import { removeRememberMe, setRememberMe } from '../utils/auth-utils'; import { noop } from '../utils/common-utils'; +import { TENANT_KEY } from '../tokens/tenant-key.token'; export const oAuthStorage = localStorage; @@ -32,6 +33,7 @@ export abstract class AuthFlowStrategy { protected oAuthConfig: AuthConfig; protected sessionState: SessionStateService; protected appConfigService: AbpApplicationConfigurationService; + protected tenantKey: string; abstract checkIfInternalAuth(queryParams?: Params): boolean; abstract navigateToLogin(queryParams?: Params): void; @@ -48,6 +50,7 @@ export abstract class AuthFlowStrategy { this.appConfigService = injector.get(AbpApplicationConfigurationService); this.sessionState = injector.get(SessionStateService); this.oAuthConfig = this.environment.getEnvironment().oAuthConfig; + this.tenantKey = injector.get(TENANT_KEY); this.listenToOauthErrors(); } @@ -176,7 +179,7 @@ export class AuthPasswordFlowStrategy extends AuthFlowStrategy { this.oAuthService.fetchTokenUsingPasswordFlow( params.username, params.password, - new HttpHeaders({ ...(tenant && tenant.id && { __tenant: tenant.id }) }), + new HttpHeaders({ ...(tenant && tenant.id && { [this.tenantKey]: tenant.id }) }), ), ).pipe(this.pipeToLogin(params)); } diff --git a/npm/ng-packs/packages/core/src/lib/tokens/index.ts b/npm/ng-packs/packages/core/src/lib/tokens/index.ts index c40fbc1bc0..4d102ee8b4 100644 --- a/npm/ng-packs/packages/core/src/lib/tokens/index.ts +++ b/npm/ng-packs/packages/core/src/lib/tokens/index.ts @@ -3,3 +3,4 @@ export * from './lodaer-delay.token'; export * from './manage-profile.token'; export * from './options.token'; export * from './app-config.token'; +export * from './tenant-key.token'; diff --git a/npm/ng-packs/packages/core/src/lib/tokens/tenant-key.token.ts b/npm/ng-packs/packages/core/src/lib/tokens/tenant-key.token.ts new file mode 100644 index 0000000000..1999b3b9f7 --- /dev/null +++ b/npm/ng-packs/packages/core/src/lib/tokens/tenant-key.token.ts @@ -0,0 +1,3 @@ +import { InjectionToken } from '@angular/core'; + +export const TENANT_KEY = new InjectionToken('TENANT_KEY'); diff --git a/npm/ng-packs/packages/core/src/lib/utils/multi-tenancy-utils.ts b/npm/ng-packs/packages/core/src/lib/utils/multi-tenancy-utils.ts index dbcb2c8517..87e4d68c9e 100644 --- a/npm/ng-packs/packages/core/src/lib/utils/multi-tenancy-utils.ts +++ b/npm/ng-packs/packages/core/src/lib/utils/multi-tenancy-utils.ts @@ -1,10 +1,10 @@ import { Injector } from '@angular/core'; import clone from 'just-clone'; -import { of } from 'rxjs'; -import { switchMap, tap } from 'rxjs/operators'; + +import { tap } from 'rxjs/operators'; import { Environment } from '../models/environment'; -import { AbpTenantService } from '../proxy/pages/abp/multi-tenancy/abp-tenant.service'; -import { CurrentTenantDto } from '../proxy/volo/abp/asp-net-core/mvc/multi-tenancy/models'; + +import { FindTenantResultDto } from '../proxy/volo/abp/asp-net-core/mvc/multi-tenancy/models'; import { EnvironmentService } from '../services/environment.service'; import { MultiTenancyService } from '../services/multi-tenancy.service'; import { createTokenParser } from './string-utils'; @@ -19,39 +19,52 @@ function getCurrentTenancyName(appBaseUrl: string): string { return parseTokens(window.location.href)[token]?.[0]; } +function getCurrentTenancyNameFromUrl(tenantKey: string) { + const urlParams = new URLSearchParams(window.location.search); + return urlParams.get(tenantKey); +} + export async function parseTenantFromUrl(injector: Injector) { const environmentService = injector.get(EnvironmentService); const multiTenancyService = injector.get(MultiTenancyService); - const abpTenantService = injector.get(AbpTenantService); const baseUrl = environmentService.getEnvironment()?.application?.baseUrl || ''; const tenancyName = getCurrentTenancyName(baseUrl); - if (tenancyName) { + const hideTenantBox = () => { multiTenancyService.isTenantBoxVisible = false; - setEnvironment(injector, tenancyName); - - return of(null) - .pipe( - switchMap(() => abpTenantService.findTenantByName(tenancyName, { __tenant: '' })), - tap(res => { - multiTenancyService.domainTenant = res.success - ? ({ id: res.tenantId, name: res.name } as CurrentTenantDto) - : null; - }), - ) + }; + + const setEnvironmentWithTenant = (tenant: FindTenantResultDto) => { + hideTenantBox(); + replaceTenantNameWithinEnvironment(injector, tenant.name); + }; + + if (tenancyName) { + return multiTenancyService + .setTenantByName(tenancyName) + .pipe(tap(setEnvironmentWithTenant)) .toPromise(); } else { /** * If there is no tenant, we still have to clean up {0}. from baseUrl to avoid incorrect http requests. */ - setEnvironment(injector, '', tenancyPlaceholder + '.'); + replaceTenantNameWithinEnvironment(injector, '', tenancyPlaceholder + '.'); + + const tenantIdFromQueryParams = getCurrentTenancyNameFromUrl(multiTenancyService.tenantKey); + if (tenantIdFromQueryParams) { + return multiTenancyService.setTenantById(tenantIdFromQueryParams).toPromise(); + } } return Promise.resolve(); } -function setEnvironment(injector: Injector, tenancyName: string, placeholder = tenancyPlaceholder) { +function replaceTenantNameWithinEnvironment( + injector: Injector, + tenancyName: string, + placeholder = tenancyPlaceholder, +) { const environmentService = injector.get(EnvironmentService); const environment = clone(environmentService.getEnvironment()) as Environment;