feat: add token for tenant key and resolve it from url

pull/9576/head
bnymncoskuner 4 years ago
parent c137353976
commit 553a9578e0

@ -33,6 +33,7 @@ import { coreOptionsFactory, CORE_OPTIONS } from './tokens/options.token';
import { noop } from './utils/common-utils'; import { noop } from './utils/common-utils';
import './utils/date-extensions'; import './utils/date-extensions';
import { getInitialData, localeInitializer } from './utils/initial-utils'; import { getInitialData, localeInitializer } from './utils/initial-utils';
import { TENANT_KEY } from './tokens/tenant-key.token';
export function storageFactory(): OAuthStorage { export function storageFactory(): OAuthStorage {
return oAuthStorage; return oAuthStorage;
@ -178,6 +179,7 @@ export class CoreModule {
useFactory: noop, useFactory: noop,
}, },
{ provide: OAuthStorage, useFactory: storageFactory }, { provide: OAuthStorage, useFactory: storageFactory },
{ provide: TENANT_KEY, useValue: options.tenantKey || '__tenant' },
], ],
}; };
} }

@ -1,9 +1,10 @@
import { HttpHandler, HttpHeaders, HttpInterceptor, HttpRequest } from '@angular/common/http'; 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 { OAuthService } from 'angular-oauth2-oidc';
import { finalize } from 'rxjs/operators'; import { finalize } from 'rxjs/operators';
import { SessionStateService } from '../services/session-state.service'; import { SessionStateService } from '../services/session-state.service';
import { HttpWaitService } from '../services/http-wait.service'; import { HttpWaitService } from '../services/http-wait.service';
import { TENANT_KEY } from '../tokens/tenant-key.token';
@Injectable({ @Injectable({
providedIn: 'root', providedIn: 'root',
@ -13,6 +14,7 @@ export class ApiInterceptor implements HttpInterceptor {
private oAuthService: OAuthService, private oAuthService: OAuthService,
private sessionState: SessionStateService, private sessionState: SessionStateService,
private httpWaitService: HttpWaitService, private httpWaitService: HttpWaitService,
@Inject(TENANT_KEY) private tenantKey: string,
) {} ) {}
intercept(request: HttpRequest<any>, next: HttpHandler) { intercept(request: HttpRequest<any>, next: HttpHandler) {
@ -40,8 +42,8 @@ export class ApiInterceptor implements HttpInterceptor {
} }
const tenant = this.sessionState.getTenant(); const tenant = this.sessionState.getTenant();
if (!existingHeaders?.has('__tenant') && tenant?.id) { if (!existingHeaders?.has(this.tenantKey) && tenant?.id) {
headers['__tenant'] = tenant.id; headers[this.tenantKey] = tenant.id;
} }
return headers; return headers;

@ -10,6 +10,7 @@ export namespace ABP {
registerLocaleFn: (locale: string) => Promise<any>; registerLocaleFn: (locale: string) => Promise<any>;
skipGetAppConfiguration?: boolean; skipGetAppConfiguration?: boolean;
sendNullsAsQueryParam?: boolean; sendNullsAsQueryParam?: boolean;
tenantKey?: string;
} }
export interface HasPolicy { export interface HasPolicy {
@ -74,7 +75,6 @@ export namespace ABP {
[key: string]: T; [key: string]: T;
} }
export type ExtractFromOutput< export type ExtractFromOutput<T extends EventEmitter<any> | Subject<any>> =
T extends EventEmitter<any> | Subject<any> T extends EventEmitter<infer X> ? X : T extends Subject<infer Y> ? Y : never;
> = T extends EventEmitter<infer X> ? X : T extends Subject<infer Y> ? Y : never;
} }

@ -8,11 +8,12 @@ import type { FindTenantResultDto } from '../../../volo/abp/asp-net-core/mvc/mul
export class AbpTenantService { export class AbpTenantService {
apiName = 'abp'; apiName = 'abp';
findTenantById = (id: string) => findTenantById = (id: string, headers: Record<string, string>) =>
this.restService.request<any, FindTenantResultDto>( this.restService.request<any, FindTenantResultDto>(
{ {
method: 'GET', method: 'GET',
url: `/api/abp/multi-tenancy/tenants/by-id/${id}`, url: `/api/abp/multi-tenancy/tenants/by-id/${id}`,
headers,
}, },
{ apiName: this.apiName }, { apiName: this.apiName },
); );

@ -1,8 +1,9 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Observable } from 'rxjs'; 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 { ApplicationConfigurationDto } from '../proxy/volo/abp/asp-net-core/mvc/application-configurations/models';
import { InternalStore } from '../utils/internal-store-utils'; import { InternalStore } from '../utils/internal-store-utils';
import { AbpApplicationConfigurationService } from '../proxy/volo/abp/asp-net-core/mvc/application-configurations/abp-application-configuration.service';
@Injectable({ @Injectable({
providedIn: 'root', providedIn: 'root',
@ -14,10 +15,16 @@ export class ConfigStateService {
return this.store.sliceUpdate; return this.store.sliceUpdate;
} }
constructor(private abpConfigService: AbpApplicationConfigurationService) {}
setState(state: ApplicationConfigurationDto) { setState(state: ApplicationConfigurationDto) {
this.store.set(state); this.store.set(state);
} }
refreshAppState() {
return this.abpConfigService.get().pipe(tap(res => this.setState(res)));
}
getOne$(key: string) { getOne$(key: string) {
return this.store.sliceState(state => state[key]); return this.store.sliceState(state => state[key]);
} }

@ -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 { Observable } from 'rxjs';
import { ABP } from '../models/common'; import { ABP } from '../models/common';
import { import { FindTenantResultDto } from '../proxy/volo/abp/asp-net-core/mvc/multi-tenancy/models';
CurrentTenantDto,
FindTenantResultDto,
} from '../proxy/volo/abp/asp-net-core/mvc/multi-tenancy/models';
import { RestService } from './rest.service'; 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 { SessionStateService } from './session-state.service';
import { TENANT_KEY } from '../tokens/tenant-key.token';
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class MultiTenancyService { 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; isTenantBoxVisible = true;
apiName = 'abp'; 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. * @deprecated Use AbpTenantService.findTenantByName method instead. To be deleted in v5.0.
@ -50,4 +51,16 @@ export class MultiTenancyService {
{ apiName: this.apiName }, { 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));
}
} }

@ -19,6 +19,7 @@ import { EnvironmentService } from '../services/environment.service';
import { SessionStateService } from '../services/session-state.service'; import { SessionStateService } from '../services/session-state.service';
import { removeRememberMe, setRememberMe } from '../utils/auth-utils'; import { removeRememberMe, setRememberMe } from '../utils/auth-utils';
import { noop } from '../utils/common-utils'; import { noop } from '../utils/common-utils';
import { TENANT_KEY } from '../tokens/tenant-key.token';
export const oAuthStorage = localStorage; export const oAuthStorage = localStorage;
@ -32,6 +33,7 @@ export abstract class AuthFlowStrategy {
protected oAuthConfig: AuthConfig; protected oAuthConfig: AuthConfig;
protected sessionState: SessionStateService; protected sessionState: SessionStateService;
protected appConfigService: AbpApplicationConfigurationService; protected appConfigService: AbpApplicationConfigurationService;
protected tenantKey: string;
abstract checkIfInternalAuth(queryParams?: Params): boolean; abstract checkIfInternalAuth(queryParams?: Params): boolean;
abstract navigateToLogin(queryParams?: Params): void; abstract navigateToLogin(queryParams?: Params): void;
@ -48,6 +50,7 @@ export abstract class AuthFlowStrategy {
this.appConfigService = injector.get(AbpApplicationConfigurationService); this.appConfigService = injector.get(AbpApplicationConfigurationService);
this.sessionState = injector.get(SessionStateService); this.sessionState = injector.get(SessionStateService);
this.oAuthConfig = this.environment.getEnvironment().oAuthConfig; this.oAuthConfig = this.environment.getEnvironment().oAuthConfig;
this.tenantKey = injector.get(TENANT_KEY);
this.listenToOauthErrors(); this.listenToOauthErrors();
} }
@ -176,7 +179,7 @@ export class AuthPasswordFlowStrategy extends AuthFlowStrategy {
this.oAuthService.fetchTokenUsingPasswordFlow( this.oAuthService.fetchTokenUsingPasswordFlow(
params.username, params.username,
params.password, params.password,
new HttpHeaders({ ...(tenant && tenant.id && { __tenant: tenant.id }) }), new HttpHeaders({ ...(tenant && tenant.id && { [this.tenantKey]: tenant.id }) }),
), ),
).pipe(this.pipeToLogin(params)); ).pipe(this.pipeToLogin(params));
} }

@ -3,3 +3,4 @@ export * from './lodaer-delay.token';
export * from './manage-profile.token'; export * from './manage-profile.token';
export * from './options.token'; export * from './options.token';
export * from './app-config.token'; export * from './app-config.token';
export * from './tenant-key.token';

@ -0,0 +1,3 @@
import { InjectionToken } from '@angular/core';
export const TENANT_KEY = new InjectionToken<string>('TENANT_KEY');

@ -1,10 +1,10 @@
import { Injector } from '@angular/core'; import { Injector } from '@angular/core';
import clone from 'just-clone'; 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 { 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 { EnvironmentService } from '../services/environment.service';
import { MultiTenancyService } from '../services/multi-tenancy.service'; import { MultiTenancyService } from '../services/multi-tenancy.service';
import { createTokenParser } from './string-utils'; import { createTokenParser } from './string-utils';
@ -19,39 +19,52 @@ function getCurrentTenancyName(appBaseUrl: string): string {
return parseTokens(window.location.href)[token]?.[0]; 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) { export async function parseTenantFromUrl(injector: Injector) {
const environmentService = injector.get(EnvironmentService); const environmentService = injector.get(EnvironmentService);
const multiTenancyService = injector.get(MultiTenancyService); const multiTenancyService = injector.get(MultiTenancyService);
const abpTenantService = injector.get(AbpTenantService);
const baseUrl = environmentService.getEnvironment()?.application?.baseUrl || ''; const baseUrl = environmentService.getEnvironment()?.application?.baseUrl || '';
const tenancyName = getCurrentTenancyName(baseUrl); const tenancyName = getCurrentTenancyName(baseUrl);
if (tenancyName) { const hideTenantBox = () => {
multiTenancyService.isTenantBoxVisible = false; multiTenancyService.isTenantBoxVisible = false;
setEnvironment(injector, tenancyName); };
return of(null) const setEnvironmentWithTenant = (tenant: FindTenantResultDto) => {
.pipe( hideTenantBox();
switchMap(() => abpTenantService.findTenantByName(tenancyName, { __tenant: '' })), replaceTenantNameWithinEnvironment(injector, tenant.name);
tap(res => { };
multiTenancyService.domainTenant = res.success
? ({ id: res.tenantId, name: res.name } as CurrentTenantDto) if (tenancyName) {
: null; return multiTenancyService
}), .setTenantByName(tenancyName)
) .pipe(tap(setEnvironmentWithTenant))
.toPromise(); .toPromise();
} else { } else {
/** /**
* If there is no tenant, we still have to clean up {0}. from baseUrl to avoid incorrect http requests. * 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(); 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 environmentService = injector.get(EnvironmentService);
const environment = clone(environmentService.getEnvironment()) as Environment; const environment = clone(environmentService.getEnvironment()) as Environment;

Loading…
Cancel
Save