Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 24 additions & 6 deletions radar-ui/libs/api/src/lib/services/api-path.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,30 +20,48 @@ export class ApiPathService {

private host = '';

private requestedHost = '';

readonly host$ = new BehaviorSubject(this.host);

readonly error$ = new Subject<string>();

initialize() {
// there are links which should switch cluster based on query params into email templates
const href = this.coreWindowService.location.href.substring(this.coreWindowService.location.href.indexOf('?'));
let value = this.coreWindowService.sessionStorage.getItem(API_CLUSTER_PATH_SESSION_KEY) || '';
if (href.includes(API_CLUSTER_URL_QUERY_PARAM_KEY)) {
value = decodeURIComponent(href.substring(API_CLUSTER_URL_QUERY_PARAM_KEY.length + 2));
const requested =
new URLSearchParams(this.coreWindowService.location.search).get(API_CLUSTER_URL_QUERY_PARAM_KEY) || '';
const stored = this.coreWindowService.sessionStorage.getItem(API_CLUSTER_PATH_SESSION_KEY) || '';

// such a link can carry any host, so it is applied only once matched against the registered clusters
if (FORM_VALIDATION_REG_EXP.IP_DOMAIN_SCHEME.test(requested)) {
this.requestedHost = requested;
}

if (FORM_VALIDATION_REG_EXP.IP_DOMAIN_SCHEME.test(value)) {
this.setHost(value);
if (FORM_VALIDATION_REG_EXP.IP_DOMAIN_SCHEME.test(stored)) {
this.setHost(stored);
}
}

/** Returns the yet unvalidated host from the query param and forgets it. */
takeRequestedHost(): string {
const value = this.requestedHost;
this.requestedHost = '';

return value;
}

get(path: string): string {
const segment = path.substring(0, path.indexOf('/')) || path;
const host = this.apiSingleTenantPaths.includes(segment) ? '' : this.host;

return `${host}${this.apiPath}${path}`;
}

/** True when url targets this app or the cluster the user switched to, and may carry the token. */
isOwnRequest(url: string): boolean {
return url.startsWith(this.apiPath) || (!!this.host && url.startsWith(`${this.host}${this.apiPath}`));
}

setHost(value: string) {
this.host = value;
this.host$.next(value);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
import { Injectable, inject } from '@angular/core';
import { Observable, catchError, filter, map, switchMap, tap, throwError } from 'rxjs';

import { API_PATH, ApiErrorCode, ApiUtilsService as apiUtils } from '@cs/api';
import { API_PATH, ApiErrorCode, ApiPathService, ApiUtilsService as apiUtils } from '@cs/api';

import { AuthJwtData } from '../interfaces/contract/auth-jwt-contract.interface';
import { AuthLocalStorageService } from '../services/auth-local-storage.services';
Expand All @@ -26,12 +26,14 @@ import { EXPIRE_AUTH_TOKENS_TODO_ACTION, EXPIRE_PASSWORD_TODO_ACTION } from '../
})
export class AuthHeadersInterceptor implements HttpInterceptor {
private readonly apiPath = inject(API_PATH);
private readonly apiPathService = inject(ApiPathService);
private readonly authLocalStorageService = inject(AuthLocalStorageService);
private readonly authRequestService = inject(AuthRequestService);
private readonly store = inject<Store<AuthState>>(Store);

intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
if (!request.url.includes(this.apiPath)) {
// matching the api path alone would also match it inside a foreign host, sending the token there
if (!request.url.includes(this.apiPath) || !this.apiPathService.isOwnRequest(request.url)) {
return next.handle(request);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,16 +108,26 @@ export class ClusterEffectStore {
switchMap(({ list }) =>
this.apiPathService.host$.pipe(
take(1),
map((host) => list.some((item) => item.own_cs_url === host))
map((host) => {
const target = this.apiPathService.takeRequestedHost() || host;

return { host, target, isHostExist: list.some((item) => item.own_cs_url === target) };
})
)
),
tap((isHostExist) => {
tap(({ host, target, isHostExist }) => {
if (!isHostExist) {
this.apiPathService.setHost('');
this.toastService.show({
style: KbqToastStyle.Warning,
title: this.i18nService.translate('Common.Pseudo.Notification.HostValidationFailed')
});

return;
}

if (target !== host) {
this.apiPathService.setHost(target);
}
}),
concatMap(() => NEVER)
Expand Down