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 './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' },
],
};
}

@ -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<any>, 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;

@ -10,6 +10,7 @@ export namespace ABP {
registerLocaleFn: (locale: string) => Promise<any>;
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<any> | Subject<any>
> = T extends EventEmitter<infer X> ? X : T extends Subject<infer Y> ? Y : never;
export type ExtractFromOutput<T extends EventEmitter<any> | Subject<any>> =
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 {
apiName = 'abp';
findTenantById = (id: string) =>
findTenantById = (id: string, headers: Record<string, string>) =>
this.restService.request<any, FindTenantResultDto>(
{
method: 'GET',
url: `/api/abp/multi-tenancy/tenants/by-id/${id}`,
headers,
},
{ apiName: this.apiName },
);

@ -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]);
}

@ -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));
}
}

@ -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));
}

@ -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';

@ -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 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;

Loading…
Cancel
Save