diff --git a/src/app/app.component.ts b/src/app/app.component.ts index c1fdcba5bfeb01956aeecd1e3fbb0f5d673670a8..291ff642af34defd923129774ad4856c972e53dc 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -5,6 +5,7 @@ import { NotificationService } from './services/util/notification.service'; import { MatIconRegistry } from '@angular/material'; import { DomSanitizer } from '@angular/platform-browser'; import { Rescale } from './models/rescale'; +import { ApiConfigService } from './services/http/api-config.service'; @Component({ selector: 'app-root', @@ -24,7 +25,8 @@ export class AppComponent implements OnInit { private update: SwUpdate, public notification: NotificationService, private matIconRegistry: MatIconRegistry, - private domSanitizer: DomSanitizer) { + private domSanitizer: DomSanitizer, + private apiConfigService: ApiConfigService) { translationService.setDefaultLang(this.translationService.getBrowserLang()); sessionStorage.setItem('currentLang', this.translationService.getBrowserLang()); for (const icon of this.icons) { @@ -54,6 +56,7 @@ export class AppComponent implements OnInit { } }); }); + this.apiConfigService.load(); } public getRescale(): Rescale { diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 75d38da8e968e9955baa865528047cc149bf2a2f..baf5a4cf53166fdedb1f5890d55b7c126dc7fb30 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -43,6 +43,7 @@ import { HomeParticipantPageComponent } from './components/home/home-participant import { CommentSettingsService } from './services/http/comment-settings.service'; import { ModeratorModule } from './components/moderator/moderator.module'; import { ImprintComponent } from './components/home/_dialogs/imprint/imprint.component'; +import { ApiConfigService } from './services/http/api-config.service'; import { DataProtectionComponent } from './components/home/_dialogs/data-protection/data-protection.component'; import { HelpPageComponent } from './components/shared/_dialogs/help-page/help-page.component'; import { CookiesComponent } from './components/home/_dialogs/cookies/cookies.component'; @@ -151,6 +152,7 @@ export function initializeApp(appConfig: AppConfig) { ModeratorService, CommentSettingsService, WsConnectorService, + ApiConfigService, { provide: MatDialogRef, useValue: { diff --git a/src/app/models/api-config.ts b/src/app/models/api-config.ts new file mode 100644 index 0000000000000000000000000000000000000000..0776be295bc587da46d0bc2c97d36cc239209092 --- /dev/null +++ b/src/app/models/api-config.ts @@ -0,0 +1,34 @@ +export enum AuthenticationProviderType { + ANONYMOUS = 'ANONYMOUS', + USERNAME_PASSWORD = 'USERNAME_PASSWORD', + SSO = 'SSO' +} + +export enum AuthenticationProviderRole { + MODERATOR = 'MODERATOR', + PARTICIPANT = 'PARTICIPANT' +} + +export interface AuthenticationProvider { + id: string; + title: string; + type: AuthenticationProviderType; + order: number; + allowedRoles: AuthenticationProviderRole[]; +} + +export interface Feature { + enabled: boolean; +} + +export interface UiConfig { + [configName: string]: any; +} + +export class ApiConfig { + constructor( + public authenticationProviders: AuthenticationProvider[], + public features: { [featureName: string]: Feature }, + public ui: UiConfig) { + } +} diff --git a/src/app/services/http/api-config.service.ts b/src/app/services/http/api-config.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..63c21a7869ad9847e57b0c51f9937433228bd776 --- /dev/null +++ b/src/app/services/http/api-config.service.ts @@ -0,0 +1,70 @@ +import { Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { Observable } from 'rxjs'; +import { BaseHttpService } from './base-http.service'; +import { ApiConfig, AuthenticationProvider, Feature, UiConfig } from '../../models/api-config'; + +@Injectable() +export class ApiConfigService extends BaseHttpService { + private readonly apiUris = { + /* TODO: API base URI should be injected. */ + base: '/api', + configPart: '/configuration', + get config() { + return this.base + this.configPart; + } + }; + private config$: Observable<ApiConfig>; + private config: ApiConfig; + + constructor(private http: HttpClient) { + super(); + this.config$ = this.http.get<ApiConfig>(this.apiUris.config); + this.config = new ApiConfig([], {}, {}); + this.freezeRecursively(this.config); + } + + load() { + console.log('Loading API configuration...'); + this.config$.subscribe((config) => { + config.authenticationProviders.sort((p1, p2) => { + return p1.order < p2.order ? -1 : p1.order > p2.order ? 1 : 0; + }); + this.freezeRecursively(config); + this.config = config; + console.log('API configuration loaded.'); + }); + } + + get loaded() { + return !!this.config; + } + + getAuthProviders(): AuthenticationProvider[] { + return this.config.authenticationProviders; + } + + getFeatureConfig(feature: string): Feature { + return this.config.features[feature]; + } + + getUiConfig(): UiConfig { + return this.config.ui; + } + + private freezeRecursively(obj: object) { + /* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze */ + if (Object.freeze) { + const propNames = Object.getOwnPropertyNames(obj); + + /* Freeze properties before freezing self */ + for (const name of propNames) { + const value = obj[name]; + obj[name] = value && typeof value === 'object' + ? this.freezeRecursively(value) : value; + } + } + + return Object.freeze(obj); + } +}