Browse Source

UI: Added support multiple domains is OAuth2 settings

pull/3557/head
Vladyslav_Prykhodko 6 years ago
parent
commit
fe00a9bd45
  1. 19
      ui-ngx/src/app/core/http/admin.service.ts
  2. 4
      ui-ngx/src/app/modules/home/pages/admin/admin.module.ts
  3. 49
      ui-ngx/src/app/modules/home/pages/admin/edit-redirect-uri-panel.component.html
  4. 18
      ui-ngx/src/app/modules/home/pages/admin/edit-redirect-uri-panel.component.scss
  5. 76
      ui-ngx/src/app/modules/home/pages/admin/edit-redirect-uri-panel.component.ts
  6. 130
      ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html
  7. 22
      ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.scss
  8. 271
      ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts
  9. 47
      ui-ngx/src/app/shared/models/settings.models.ts
  10. 20
      ui-ngx/src/assets/locale/locale.constant-en_US.json

19
ui-ngx/src/app/core/http/admin.service.ts

@ -60,27 +60,16 @@ export class AdminService {
defaultHttpOptionsFromConfig(config)); defaultHttpOptionsFromConfig(config));
} }
public getOAuth2Settings(config?: RequestConfig): Observable<Array<OAuth2Settings>> { public getOAuth2Settings(config?: RequestConfig): Observable<OAuth2Settings> {
return this.http.get<Array<OAuth2Settings>>(`/api/oauth2/config`, defaultHttpOptionsFromConfig(config)); return this.http.get<OAuth2Settings>(`/api/oauth2/config`, defaultHttpOptionsFromConfig(config));
} }
public getOAuth2Template(config?: RequestConfig): Observable<Array<ClientProviderTemplated>> { public getOAuth2Template(config?: RequestConfig): Observable<Array<ClientProviderTemplated>> {
return this.http.get<Array<ClientProviderTemplated>>(`/api/oauth2/config/template`, defaultHttpOptionsFromConfig(config)); return this.http.get<Array<ClientProviderTemplated>>(`/api/oauth2/config/template`, defaultHttpOptionsFromConfig(config));
} }
public saveOAuth2Settings(OAuth2Setting: OAuth2Settings[], public saveOAuth2Settings(OAuth2Setting: OAuth2Settings, config?: RequestConfig): Observable<OAuth2Settings> {
config?: RequestConfig): Observable<Array<OAuth2Settings>> { return this.http.post<OAuth2Settings>('/api/oauth2/config', OAuth2Setting,
return this.http.post<Array<OAuth2Settings>>('/api/oauth2/config', OAuth2Setting,
defaultHttpOptionsFromConfig(config));
}
public deleteOAuth2Domain(OAuth2Domain: string, config?: RequestConfig) {
return this.http.delete(`/api/oauth2/config/domain/${OAuth2Domain}`,
defaultHttpOptionsFromConfig(config));
}
public deleteOAuthCclientRegistrationId(clientRegistrationId: string, config?: RequestConfig) {
return this.http.delete(`/api/oauth2/config/${clientRegistrationId}`,
defaultHttpOptionsFromConfig(config)); defaultHttpOptionsFromConfig(config));
} }

4
ui-ngx/src/app/modules/home/pages/admin/admin.module.ts

@ -24,7 +24,6 @@ import { GeneralSettingsComponent } from '@modules/home/pages/admin/general-sett
import { SecuritySettingsComponent } from '@modules/home/pages/admin/security-settings.component'; import { SecuritySettingsComponent } from '@modules/home/pages/admin/security-settings.component';
import { HomeComponentsModule } from '@modules/home/components/home-components.module'; import { HomeComponentsModule } from '@modules/home/components/home-components.module';
import { OAuth2SettingsComponent } from '@modules/home/pages/admin/oauth2-settings.component'; import { OAuth2SettingsComponent } from '@modules/home/pages/admin/oauth2-settings.component';
import { EditRedirectUriPanelComponent } from './edit-redirect-uri-panel.component';
@NgModule({ @NgModule({
declarations: declarations:
@ -32,8 +31,7 @@ import { EditRedirectUriPanelComponent } from './edit-redirect-uri-panel.compone
GeneralSettingsComponent, GeneralSettingsComponent,
MailServerComponent, MailServerComponent,
SecuritySettingsComponent, SecuritySettingsComponent,
OAuth2SettingsComponent, OAuth2SettingsComponent
EditRedirectUriPanelComponent
], ],
imports: [ imports: [
CommonModule, CommonModule,

49
ui-ngx/src/app/modules/home/pages/admin/edit-redirect-uri-panel.component.html

@ -1,49 +0,0 @@
<!--
Copyright © 2016-2020 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<form class="mat-elevation-z4"
[formGroup]="redirectURIFormGroup" (ngSubmit)="update()" style="width: 350px; padding: 12px 8px 0;">
<fieldset [disabled]="isLoading$ | async">
<mat-form-field fxFlex class="mat-block">
<mat-label translate>admin.oauth2.redirect-uri-template</mat-label>
<input matInput formControlName="value" required>
<mat-error
*ngIf="redirectURIFormGroup.get('value').hasError('required')">
{{ 'admin.oauth2.redirect-uri-template-required' | translate }}
</mat-error>
<mat-error
*ngIf="redirectURIFormGroup.get('value').hasError('pattern')">
{{ 'admin.oauth2.uri-pattern-error' | translate }}
</mat-error>
</mat-form-field>
</fieldset>
<div fxLayout="row" class="tb-panel-actions">
<span fxFlex></span>
<button mat-button color="primary"
style="margin-right: 20px;"
type="button"
[disabled]="(isLoading$ | async)"
(click)="cancel()" cdkFocusInitial>
{{ 'action.cancel' | translate }}
</button>
<button mat-button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || redirectURIFormGroup.invalid || !redirectURIFormGroup.dirty">
{{ 'action.update' | translate }}
</button>
</div>
</form>

18
ui-ngx/src/app/modules/home/pages/admin/edit-redirect-uri-panel.component.scss

@ -1,18 +0,0 @@
/**
* Copyright © 2016-2020 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
:host {
background-color: #f9f9f9;
}

76
ui-ngx/src/app/modules/home/pages/admin/edit-redirect-uri-panel.component.ts

@ -1,76 +0,0 @@
///
/// Copyright © 2016-2020 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import { Component, Inject, InjectionToken, OnInit, SkipSelf } from '@angular/core';
import { ErrorStateMatcher } from '@angular/material/core';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { FormBuilder, FormControl, FormGroup, FormGroupDirective, NgForm, Validators } from '@angular/forms';
import { PageComponent } from '@shared/components/page.component';
import { OverlayRef } from '@angular/cdk/overlay';
export const EDIT_REDIRECT_URI_PANEL_DATA = new InjectionToken<any>('EditRedirectUriPanelData');
export interface EditRedirectUriPanelData {
redirectURI: any;
}
@Component({
selector: 'tb-edit-redirect-uri-panel',
templateUrl: './edit-redirect-uri-panel.component.html',
providers: [{provide: ErrorStateMatcher, useExisting: EditRedirectUriPanelComponent}],
styleUrls: ['./edit-redirect-uri-panel.component.scss']
})
export class EditRedirectUriPanelComponent extends PageComponent implements OnInit, ErrorStateMatcher {
private URL_REGEXP = /^[A-Za-z][A-Za-z\d.+-]*:\/*(?:\w+(?::\w+)?@)?[^\s/]+(?::\d+)?(?:\/[\w#!:.?+=&%@\-/]*)?$/;
redirectURIFormGroup: FormGroup;
result: any = null;
submitted = false;
constructor(protected store: Store<AppState>,
@Inject(EDIT_REDIRECT_URI_PANEL_DATA) public data: EditRedirectUriPanelData,
@SkipSelf() private errorStateMatcher: ErrorStateMatcher,
public overlayRef: OverlayRef,
public fb: FormBuilder) {
super(store);
}
ngOnInit(): void {
this.redirectURIFormGroup = this.fb.group({
value: [this.data.redirectURI, [Validators.required, Validators.pattern(this.URL_REGEXP)]]
});
}
isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
const originalErrorState = this.errorStateMatcher.isErrorState(control, form);
const customErrorState = !!(control && control.invalid && this.submitted);
return originalErrorState || customErrorState;
}
cancel(): void {
this.overlayRef.dispose();
}
update(): void {
this.submitted = true;
this.result = this.redirectURIFormGroup.get('value').value;
this.overlayRef.dispose();
}
}

130
ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html

@ -30,23 +30,20 @@
<mat-card-content style="padding-top: 16px;"> <mat-card-content style="padding-top: 16px;">
<form [formGroup]="oauth2SettingsForm" (ngSubmit)="save()"> <form [formGroup]="oauth2SettingsForm" (ngSubmit)="save()">
<fieldset [disabled]="isLoading$ | async"> <fieldset [disabled]="isLoading$ | async">
<ng-container formArrayName="clientDomains"> <mat-checkbox formControlName="enabled">
{{ 'admin.oauth2.enable' | translate }}
</mat-checkbox>
<section *ngIf="oauth2SettingsForm.get('enabled').value && !(isLoading$ | async)" style="margin-top: 1em;">
<ng-container formArrayName="domainsParams">
<div class="container"> <div class="container">
<mat-accordion multi> <mat-accordion multi>
<ng-container *ngFor="let domain of clientDomains.controls; let i = index; let last = last;"> <ng-container *ngFor="let domain of domainsParams.controls; let i = index; trackBy: trackByParams">
<mat-expansion-panel [formGroupName]="i" [expanded]="last"> <mat-expansion-panel [formGroupName]="i">
<mat-expansion-panel-header> <mat-expansion-panel-header>
<mat-panel-title fxLayoutAlign="start center"> <mat-panel-title fxLayoutAlign="start center">
{{ domain.get('domainName').value ? domain.get('domainName').value : ("admin.oauth2.new-domain" | translate) }} {{ domainListTittle(domain) }}
</mat-panel-title> </mat-panel-title>
<mat-panel-description fxLayoutAlign="end center"> <mat-panel-description fxLayoutAlign="end center">
<button mat-icon-button
type="button"
(click)="editRedirectURI($event, i)"
matTooltip="{{ 'admin.oauth2.redirect-uri-template' | translate }}"
matTooltipPosition="above">
<mat-icon>link</mat-icon>
</button>
<button mat-icon-button <button mat-icon-button
type="button" type="button"
(click)="deleteDomain($event, i)" (click)="deleteDomain($event, i)"
@ -58,20 +55,84 @@
</mat-expansion-panel-header> </mat-expansion-panel-header>
<ng-template matExpansionPanelContent> <ng-template matExpansionPanelContent>
<mat-form-field class="mat-block"> <ng-container formArrayName="domainInfos">
<section *ngFor="let domainInfo of clientDomainInfos(domain).controls; let n = index; trackBy: trackByParams"
class="domains-list">
<div [formGroupName]="n" fxLayout="row" fxLayoutGap="8px">
<div fxLayout="row" fxLayout.xs="column" fxFlex fxLayoutGap="8px">
<div fxLayout="row" fxLayout.xs="column" fxLayoutGap="8px" fxFlex.gt-xs="50">
<mat-form-field fxFlex="30" fxFlex.xs class="mat-block">
<mat-label translate>admin.oauth2.protocol</mat-label>
<mat-select formControlName="scheme">
<mat-option *ngFor="let protocol of protocols" [value]="protocol">
{{ domainSchemaTranslations.get(protocol) | translate | uppercase }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field fxFlex class="mat-block">
<mat-label translate>admin.domain-name</mat-label> <mat-label translate>admin.domain-name</mat-label>
<input matInput formControlName="domainName" required> <input matInput formControlName="name" required>
<mat-error *ngIf="domain.get('domainName').hasError('pattern')"> <mat-error *ngIf="domainInfo.get('name').hasError('pattern')">
{{ 'admin.error-verification-url' | translate }} {{ 'admin.error-verification-url' | translate }}
</mat-error> </mat-error>
<mat-error *ngIf="domain.get('domainName').hasError('unique')"> <mat-error *ngIf="domainInfo.get('name').hasError('unique')">
{{ 'admin.domain-name-unique' | translate }} {{ 'admin.domain-name-unique' | translate }}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
</div>
<div fxFlex>
<mat-form-field fxFlex class="mat-block">
<mat-label translate>admin.oauth2.redirect-uri-template</mat-label>
<input matInput [value]="redirectURI(domainInfo)" readonly>
<button mat-icon-button color="primary" matSuffix type="button"
ngxClipboard cbContent="{{ redirectURI(domainInfo) }}"
matTooltip="{{ 'admin.oauth2.copy-redirect-uri' | translate }}"
matTooltipPosition="above">
<mat-icon class="material-icons" svgIcon="mdi:clipboard-arrow-left"></mat-icon>
</button>
</mat-form-field>
<mat-form-field fxFlex *ngIf="domainInfo.get('scheme').value === 'MIXED'"
class="mat-block">
<mat-label></mat-label>
<input matInput [value]="redirectURIMixed(domainInfo)" readonly>
<button mat-icon-button color="primary" matSuffix type="button"
ngxClipboard cbContent="{{ redirectURIMixed(domainInfo) }}"
matTooltip="{{ 'admin.oauth2.copy-redirect-uri' | translate }}"
matTooltipPosition="above">
<mat-icon class="material-icons" svgIcon="mdi:clipboard-arrow-left"></mat-icon>
</button>
</mat-form-field>
</div>
</div>
<div fxLayout="column" fxLayoutAlign="center start">
<button type="button" mat-icon-button color="primary"
(click)="removeDomain($event, domain, n)"
[disabled]="clientDomainInfos(domain).controls.length < 2"
matTooltip="{{ 'admin.oauth2.delete-domain' | translate }}"
matTooltipPosition="above">
<mat-icon>close</mat-icon>
</button>
</div>
</div>
</section>
<div fxLayout="row" fxLayoutAlign="end center" style="margin-bottom: 1.25em">
<button mat-button mat-raised-button color="primary"
[disabled]="(isLoading$ | async)"
(click)="addDomainInfo(domain)"
type="button">
{{'admin.oauth2.add-domain' | translate}}
</button>
</div>
</ng-container>
<ng-container formArrayName="clientRegistrations"> <ng-container formArrayName="clientRegistrations">
<div class="container"> <div class="container">
<mat-expansion-panel *ngFor="let registration of clientDomainRegistrations(domain).controls; let j = index;" <mat-expansion-panel *ngFor="let registration of clientDomainProviders(domain).controls; let j = index; trackBy: trackByParams"
class="registration-card mat-elevation-z0"> class="registration-card mat-elevation-z0">
<mat-expansion-panel-header> <mat-expansion-panel-header>
<mat-panel-title fxLayoutAlign="start center"> <mat-panel-title fxLayoutAlign="start center">
@ -80,8 +141,9 @@
<mat-panel-description fxLayoutAlign="end center"> <mat-panel-description fxLayoutAlign="end center">
<button mat-icon-button <button mat-icon-button
type="button" type="button"
(click)="deleteRegistration($event, domain, j)" [disabled]="clientDomainProviders(domain).controls.length < 2"
matTooltip="{{ 'action.delete' | translate }}" (click)="deleteProvider($event, domain, j)"
matTooltip="{{ 'admin.oauth2.delete-provider' | translate }}"
matTooltipPosition="above"> matTooltipPosition="above">
<mat-icon>delete</mat-icon> <mat-icon>delete</mat-icon>
</button> </button>
@ -213,9 +275,10 @@
<div fxLayout="row" fxLayout.xs="column" fxLayoutGap.gt-xs="8px" *ngIf="getProviderName(registration) === 'Custom'"> <div fxLayout="row" fxLayout.xs="column" fxLayoutGap.gt-xs="8px" *ngIf="getProviderName(registration) === 'Custom'">
<mat-form-field fxFlex class="mat-block" floatLabel="always"> <mat-form-field fxFlex class="mat-block" floatLabel="always">
<mat-label translate>admin.oauth2.login-button-label</mat-label> <mat-label translate>admin.oauth2.login-button-label</mat-label>
<input matInput formControlName="loginButtonLabel" placeholder="{{ 'admin.oauth2.login-button-label-1' | translate }}" required> <input matInput formControlName="loginButtonLabel"
<mat-error placeholder="{{ 'admin.oauth2.login-button-label-1' | translate }}"
*ngIf="registration.get('loginButtonLabel').hasError('required')"> required>
<mat-error *ngIf="registration.get('loginButtonLabel').hasError('required')">
{{ 'admin.oauth2.login-button-label-required' | translate }} {{ 'admin.oauth2.login-button-label-required' | translate }}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
@ -240,7 +303,7 @@
<mat-form-field fxFlex class="mat-block"> <mat-form-field fxFlex class="mat-block">
<mat-label translate>admin.oauth2.scope</mat-label> <mat-label translate>admin.oauth2.scope</mat-label>
<mat-chip-list #scopeList> <mat-chip-list #scopeList>
<mat-chip *ngFor="let scope of registration.get('scope').value; let k = index;" <mat-chip *ngFor="let scope of registration.get('scope').value; let k = index; trackBy: trackByParams"
removable (removed)="removeScope(k, registration)"> removable (removed)="removeScope(k, registration)">
{{scope}} {{scope}}
<mat-icon matChipRemove>cancel</mat-icon> <mat-icon matChipRemove>cancel</mat-icon>
@ -250,6 +313,9 @@
matChipInputAddOnBlur matChipInputAddOnBlur
(matChipInputTokenEnd)="addScope($event, registration)"> (matChipInputTokenEnd)="addScope($event, registration)">
</mat-chip-list> </mat-chip-list>
<mat-error *ngIf="registration.get('scope').hasError('required')">
{{ 'admin.oauth2.scope-required' | translate }}
</mat-error>
</mat-form-field> </mat-form-field>
</mat-tab> </mat-tab>
@ -257,7 +323,8 @@
<mat-form-field class="mat-block" style="margin-top: 16px;"> <mat-form-field class="mat-block" style="margin-top: 16px;">
<mat-label translate>admin.oauth2.user-name-attribute-name</mat-label> <mat-label translate>admin.oauth2.user-name-attribute-name</mat-label>
<input matInput formControlName="userNameAttributeName" required> <input matInput formControlName="userNameAttributeName" required>
<mat-error *ngIf="registration.get('userNameAttributeName').hasError('required')"> <mat-error
*ngIf="registration.get('userNameAttributeName').hasError('required')">
{{ 'admin.oauth2.user-name-attribute-name-required' | translate }} {{ 'admin.oauth2.user-name-attribute-name-required' | translate }}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
@ -378,7 +445,7 @@
<div fxLayout="row" fxLayoutAlign="end center" fxLayoutGap="8px"> <div fxLayout="row" fxLayoutAlign="end center" fxLayoutGap="8px">
<button mat-button mat-raised-button color="primary" <button mat-button mat-raised-button color="primary"
[disabled]="(isLoading$ | async)" [disabled]="(isLoading$ | async)"
(click)="addRegistration(domain)" (click)="addProvider(domain)"
type="button"> type="button">
{{'admin.oauth2.add-provider' | translate}} {{'admin.oauth2.add-provider' | translate}}
</button> </button>
@ -390,13 +457,15 @@
</mat-accordion> </mat-accordion>
</div> </div>
</ng-container> </ng-container>
</section>
</fieldset>
<div fxLayout="row" fxLayoutAlign="end center" fxLayoutGap="8px"> <div fxLayout="row" fxLayoutAlign="end center" fxLayoutGap="8px">
<button mat-button mat-raised-button color="primary" <button type="button" mat-raised-button color="primary"
[disabled]="(isLoading$ | async)" [disabled]="isLoading$ | async"
(click)="addDomain()" *ngIf="oauth2SettingsForm.get('enabled').value"
type="button"> (click)="addDomain()">
{{'admin.oauth2.add-domain' | translate}} <mat-icon>add</mat-icon>
<span translate>action.add</span>
</button> </button>
<button mat-button mat-raised-button color="primary" <button mat-button mat-raised-button color="primary"
[disabled]="(isLoading$ | async) || oauth2SettingsForm.invalid || !oauth2SettingsForm.dirty" [disabled]="(isLoading$ | async) || oauth2SettingsForm.invalid || !oauth2SettingsForm.dirty"
@ -404,7 +473,6 @@
{{'action.save' | translate}} {{'action.save' | translate}}
</button> </button>
</div> </div>
</fieldset>
</form> </form>
</mat-card-content> </mat-card-content>
</mat-card> </mat-card>

22
ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.scss

@ -15,11 +15,11 @@
*/ */
:host{ :host{
.checkbox-row { .checkbox-row {
margin-top: 16px; margin-top: 1em;
} }
.registration-card{ .registration-card{
margin-bottom: 8px; margin-bottom: 0.5em;
border: 1px solid rgba(0, 0, 0, 0.2); border: 1px solid rgba(0, 0, 0, 0.2);
.custom-settings{ .custom-settings{
@ -31,7 +31,15 @@
} }
.container{ .container{
margin-bottom: 16px; margin-bottom: 1em;
.tb-highlight{
margin: 0;
}
.tb-hint{
padding-bottom: 0;
}
} }
} }
@ -39,7 +47,7 @@
.registration-card{ .registration-card{
.custom-settings{ .custom-settings{
.mat-expansion-panel-body{ .mat-expansion-panel-body{
padding: 0 2px 16px; padding: 0 2px 1em;
} }
.mat-tab-label{ .mat-tab-label{
text-transform: none; text-transform: none;
@ -49,4 +57,10 @@
} }
} }
} }
.domains-list{
margin-bottom: 1.5em;
.mat-form-field-suffix .mat-icon-button .mat-icon{
font-size: 24px;
}
}
} }

271
ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts

@ -14,12 +14,16 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { ChangeDetectorRef, Component, Inject, OnDestroy, OnInit, ViewContainerRef } from '@angular/core'; import { Component, Inject, OnDestroy, OnInit } from '@angular/core';
import { AbstractControl, FormArray, FormBuilder, FormGroup, Validators } from '@angular/forms'; import { AbstractControl, FormArray, FormBuilder, FormGroup, Validators } from '@angular/forms';
import { import {
ClientAuthenticationMethod, ClientAuthenticationMethod,
ClientProviderTemplated, ClientProviderTemplated,
ClientRegistration, ClientRegistration,
DomainInfo,
DomainSchema,
domainSchemaTranslations,
DomainsParam,
MapperConfig, MapperConfig,
MapperConfigBasic, MapperConfigBasic,
MapperConfigCustom, MapperConfigCustom,
@ -38,13 +42,7 @@ import { WINDOW } from '@core/services/window.service';
import { forkJoin, Subscription } from 'rxjs'; import { forkJoin, Subscription } from 'rxjs';
import { DialogService } from '@core/services/dialog.service'; import { DialogService } from '@core/services/dialog.service';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { ConnectedPosition, Overlay, OverlayConfig, OverlayRef } from '@angular/cdk/overlay'; import { isDefined } from '@core/utils';
import { ComponentPortal, PortalInjector } from '@angular/cdk/portal';
import {
EDIT_REDIRECT_URI_PANEL_DATA,
EditRedirectUriPanelComponent,
EditRedirectUriPanelData
} from './edit-redirect-uri-panel.component';
@Component({ @Component({
selector: 'tb-oauth2-settings', selector: 'tb-oauth2-settings',
@ -60,15 +58,15 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha
additionalInfo: { additionalInfo: {
providerName: 'Custom' providerName: 'Custom'
}, },
clientAuthenticationMethod: 'POST', clientAuthenticationMethod: ClientAuthenticationMethod.POST,
userNameAttributeName: 'email', userNameAttributeName: 'email',
mapperConfig: { mapperConfig: {
allowUserCreation: true, allowUserCreation: true,
activateUser: false, activateUser: false,
type: 'BASIC', type: MapperConfigType.BASIC,
basic: { basic: {
emailAttributeKey: 'email', emailAttributeKey: 'email',
tenantNameStrategy: 'DOMAIN', tenantNameStrategy: TenantNameStrategy.DOMAIN,
alwaysFullScreen: false alwaysFullScreen: false
} }
} }
@ -77,19 +75,18 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha
readonly separatorKeysCodes: number[] = [ENTER, COMMA]; readonly separatorKeysCodes: number[] = [ENTER, COMMA];
oauth2SettingsForm: FormGroup; oauth2SettingsForm: FormGroup;
oauth2Settings: OAuth2Settings[]; oauth2Settings: OAuth2Settings;
clientAuthenticationMethods: ClientAuthenticationMethod[] = ['BASIC', 'POST']; clientAuthenticationMethods = Object.keys(ClientAuthenticationMethod);
converterTypesExternalUser: MapperConfigType[] = ['BASIC', 'CUSTOM']; converterTypesExternalUser = Object.keys(MapperConfigType);
tenantNameStrategies: TenantNameStrategy[] = ['DOMAIN', 'EMAIL', 'CUSTOM']; tenantNameStrategies = Object.keys(TenantNameStrategy);
protocols = Object.keys(DomainSchema);
domainSchemaTranslations = domainSchemaTranslations;
templateProvider = ['Custom']; templateProvider = ['Custom'];
constructor(protected store: Store<AppState>, constructor(protected store: Store<AppState>,
private adminService: AdminService, private adminService: AdminService,
private overlay: Overlay,
private viewContainerRef: ViewContainerRef,
private cd: ChangeDetectorRef,
private fb: FormBuilder, private fb: FormBuilder,
private dialogService: DialogService, private dialogService: DialogService,
private translate: TranslateService, private translate: TranslateService,
@ -124,8 +121,8 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha
this.templateProvider.sort(); this.templateProvider.sort();
} }
get clientDomains(): FormArray { get domainsParams(): FormArray {
return this.oauth2SettingsForm.get('clientDomains') as FormArray; return this.oauth2SettingsForm.get('domainsParams') as FormArray;
} }
private formBasicGroup(mapperConfigBasic?: MapperConfigBasic): FormGroup { private formBasicGroup(mapperConfigBasic?: MapperConfigBasic): FormGroup {
@ -139,7 +136,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha
emailAttributeKey: [mapperConfigBasic?.emailAttributeKey ? mapperConfigBasic.emailAttributeKey : 'email', Validators.required], emailAttributeKey: [mapperConfigBasic?.emailAttributeKey ? mapperConfigBasic.emailAttributeKey : 'email', Validators.required],
firstNameAttributeKey: [mapperConfigBasic?.firstNameAttributeKey ? mapperConfigBasic.firstNameAttributeKey : ''], firstNameAttributeKey: [mapperConfigBasic?.firstNameAttributeKey ? mapperConfigBasic.firstNameAttributeKey : ''],
lastNameAttributeKey: [mapperConfigBasic?.lastNameAttributeKey ? mapperConfigBasic.lastNameAttributeKey : ''], lastNameAttributeKey: [mapperConfigBasic?.lastNameAttributeKey ? mapperConfigBasic.lastNameAttributeKey : ''],
tenantNameStrategy: [mapperConfigBasic?.tenantNameStrategy ? mapperConfigBasic.tenantNameStrategy : 'DOMAIN'], tenantNameStrategy: [mapperConfigBasic?.tenantNameStrategy ? mapperConfigBasic.tenantNameStrategy : TenantNameStrategy.DOMAIN],
tenantNamePattern: [tenantNamePattern, Validators.required], tenantNamePattern: [tenantNamePattern, Validators.required],
customerNamePattern: [mapperConfigBasic?.customerNamePattern ? mapperConfigBasic.customerNamePattern : null], customerNamePattern: [mapperConfigBasic?.customerNamePattern ? mapperConfigBasic.customerNamePattern : null],
defaultDashboardName: [mapperConfigBasic?.defaultDashboardName ? mapperConfigBasic.defaultDashboardName : null], defaultDashboardName: [mapperConfigBasic?.defaultDashboardName ? mapperConfigBasic.defaultDashboardName : null],
@ -167,69 +164,78 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha
private buildOAuth2SettingsForm(): void { private buildOAuth2SettingsForm(): void {
this.oauth2SettingsForm = this.fb.group({ this.oauth2SettingsForm = this.fb.group({
clientDomains: this.fb.array([]) domainsParams: this.fb.array([]),
enabled: [false]
}); });
} }
private initOAuth2Settings(oauth2Settings: OAuth2Settings[]): void { private initOAuth2Settings(oauth2Settings: OAuth2Settings): void {
if (oauth2Settings) { if (oauth2Settings) {
oauth2Settings.forEach((domain) => { this.oauth2SettingsForm.patchValue({enabled: oauth2Settings.enabled}, {emitEvent: false});
this.clientDomains.push(this.buildSettingsDomain(domain)); oauth2Settings.domainsParams.forEach((domain) => {
this.domainsParams.push(this.buildDomainsForm(domain));
}); });
} }
} }
private uniqueDomainValidator(control: AbstractControl): { [key: string]: boolean } | null { private uniqueDomainValidator(control: AbstractControl): { [key: string]: boolean } | null {
if (control.root.value.clientDomains?.length > 1) { if (control.parent?.parent?.value) {
const listDomainName = []; const domain = control.value;
control.root.value.clientDomains.forEach((domain) => { const listProtocols = control.parent.parent.value
listDomainName.push(domain.domainName); .filter((domainInfo) => domainInfo.name === domain)
}); .map((domainInfo) => domainInfo.scheme);
if (listDomainName.indexOf(control.value) > -1) { if (listProtocols.length > 1 && listProtocols.indexOf(DomainSchema.MIXED) > -1) {
return {unique: true}; return {unique: true};
} }
} }
return null; return null;
} }
private buildSettingsDomain(domainParams?: OAuth2Settings): FormGroup { public domainListTittle(control: AbstractControl): string {
let url = this.window.location.protocol + '//' + this.window.location.hostname; const domainInfos = control.get('domainInfos').value as DomainInfo[];
const port = this.window.location.port; if (domainInfos.length) {
if (port !== '80' && port !== '443') { const domainList = new Set<string>();
url += ':' + port; domainInfos.forEach((domain) => {
domainList.add(domain.name);
});
return Array.from(domainList).join(', ');
}
return this.translate.instant('admin.oauth2.new-domain');
} }
url += '/login/oauth2/code/';
private buildDomainsForm(domainParams?: DomainsParam): FormGroup {
const formDomain = this.fb.group({ const formDomain = this.fb.group({
domainName: [domainParams?.domainName ? domainParams.domainName : this.window.location.hostname, [ domainInfos: this.fb.array([], Validators.required),
Validators.required,
Validators.pattern('((?![:/]).)*$'),
this.uniqueDomainValidator]],
redirectUriTemplate: [domainParams?.redirectUriTemplate ? domainParams.redirectUriTemplate : url, [
Validators.required,
Validators.pattern(this.URL_REGEXP)]],
clientRegistrations: this.fb.array([], Validators.required) clientRegistrations: this.fb.array([], Validators.required)
}); });
this.subscriptions.push(formDomain.get('domainName').valueChanges.subscribe((domain) => {
if (!domain) {
domain = this.window.location.hostname;
}
const uri = this.window.location.protocol + `//${domain}/login/oauth2/code/`;
formDomain.get('redirectUriTemplate').patchValue(uri, {emitEvent: false});
}));
if (domainParams) { if (domainParams) {
domainParams.domainInfos.forEach((domain) => {
this.clientDomainInfos(formDomain).push(this.buildDomainForm(domain));
});
domainParams.clientRegistrations.forEach((registration) => { domainParams.clientRegistrations.forEach((registration) => {
this.clientDomainRegistrations(formDomain).push(this.buildSettingsRegistration(registration)); this.clientDomainProviders(formDomain).push(this.buildProviderForm(registration));
}); });
} else { } else {
this.clientDomainRegistrations(formDomain).push(this.buildSettingsRegistration()); this.clientDomainProviders(formDomain).push(this.buildProviderForm());
this.clientDomainInfos(formDomain).push(this.buildDomainForm());
} }
return formDomain; return formDomain;
} }
private buildSettingsRegistration(registrationData?: ClientRegistration): FormGroup { private buildDomainForm(domainInfo?: DomainInfo): FormGroup {
const domain = this.fb.group({
name: [domainInfo ? domainInfo.name : this.window.location.hostname, [
Validators.required,
Validators.pattern('((?![:/]).)*$'),
this.uniqueDomainValidator]],
scheme: [domainInfo?.scheme ? domainInfo.scheme : DomainSchema.HTTPS, Validators.required]
});
return domain;
}
private buildProviderForm(registrationData?: ClientRegistration): FormGroup {
let additionalInfo = null; let additionalInfo = null;
if (registrationData?.additionalInfo) { if (registrationData?.additionalInfo) {
additionalInfo = JSON.parse(registrationData.additionalInfo); additionalInfo = JSON.parse(registrationData.additionalInfo);
@ -237,13 +243,18 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha
additionalInfo.providerName = 'Custom'; additionalInfo.providerName = 'Custom';
} }
} }
let defaultProviderName = 'Custom';
if (this.templateProvider.indexOf('Google')) {
defaultProviderName = 'Google';
}
const clientRegistration = this.fb.group({ const clientRegistration = this.fb.group({
id: this.fb.group({ id: this.fb.group({
id: [registrationData?.id?.id ? registrationData.id.id : null], id: [registrationData?.id?.id ? registrationData.id.id : null],
entityType: [registrationData?.id?.entityType ? registrationData.id.entityType : null] entityType: [registrationData?.id?.entityType ? registrationData.id.entityType : null]
}), }),
additionalInfo: this.fb.group({ additionalInfo: this.fb.group({
providerName: [additionalInfo?.providerName ? additionalInfo?.providerName : 'Custom', Validators.required] providerName: [additionalInfo?.providerName ? additionalInfo?.providerName : defaultProviderName, Validators.required]
}), }),
loginButtonLabel: [registrationData?.loginButtonLabel ? registrationData.loginButtonLabel : null, Validators.required], loginButtonLabel: [registrationData?.loginButtonLabel ? registrationData.loginButtonLabel : null, Validators.required],
loginButtonIcon: [registrationData?.loginButtonIcon ? registrationData.loginButtonIcon : null], loginButtonIcon: [registrationData?.loginButtonIcon ? registrationData.loginButtonIcon : null],
@ -255,19 +266,20 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha
authorizationUri: [registrationData?.authorizationUri ? registrationData.authorizationUri : '', authorizationUri: [registrationData?.authorizationUri ? registrationData.authorizationUri : '',
[Validators.required, [Validators.required,
Validators.pattern(this.URL_REGEXP)]], Validators.pattern(this.URL_REGEXP)]],
scope: this.fb.array(registrationData?.scope ? registrationData.scope : []), scope: this.fb.array(registrationData?.scope ? registrationData.scope : [], Validators.required),
jwkSetUri: [registrationData?.jwkSetUri ? registrationData.jwkSetUri : '', Validators.pattern(this.URL_REGEXP)], jwkSetUri: [registrationData?.jwkSetUri ? registrationData.jwkSetUri : '', Validators.pattern(this.URL_REGEXP)],
userInfoUri: [registrationData?.userInfoUri ? registrationData.userInfoUri : '', userInfoUri: [registrationData?.userInfoUri ? registrationData.userInfoUri : '',
[Validators.required, [Validators.required,
Validators.pattern(this.URL_REGEXP)]], Validators.pattern(this.URL_REGEXP)]],
clientAuthenticationMethod: [ clientAuthenticationMethod: [
registrationData?.clientAuthenticationMethod ? registrationData.clientAuthenticationMethod : 'POST', Validators.required], registrationData?.clientAuthenticationMethod ? registrationData.clientAuthenticationMethod : ClientAuthenticationMethod.POST,
Validators.required],
userNameAttributeName: [ userNameAttributeName: [
registrationData?.userNameAttributeName ? registrationData.userNameAttributeName : 'email', Validators.required], registrationData?.userNameAttributeName ? registrationData.userNameAttributeName : 'email', Validators.required],
mapperConfig: this.fb.group({ mapperConfig: this.fb.group({
allowUserCreation: [registrationData?.mapperConfig?.allowUserCreation ? registrationData.mapperConfig.allowUserCreation : true], allowUserCreation: [registrationData?.mapperConfig?.allowUserCreation ? registrationData.mapperConfig.allowUserCreation : true],
activateUser: [registrationData?.mapperConfig?.activateUser ? registrationData.mapperConfig.activateUser : false], activateUser: [registrationData?.mapperConfig?.activateUser ? registrationData.mapperConfig.activateUser : false],
type: [registrationData?.mapperConfig?.type ? registrationData.mapperConfig.type : 'BASIC', Validators.required] type: [registrationData?.mapperConfig?.type ? registrationData.mapperConfig.type : MapperConfigType.BASIC, Validators.required]
} }
) )
}); });
@ -275,7 +287,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha
if (registrationData) { if (registrationData) {
this.changeMapperConfigType(clientRegistration, registrationData.mapperConfig.type, registrationData.mapperConfig); this.changeMapperConfigType(clientRegistration, registrationData.mapperConfig.type, registrationData.mapperConfig);
} else { } else {
this.changeMapperConfigType(clientRegistration, 'BASIC'); this.changeMapperConfigType(clientRegistration, MapperConfigType.BASIC);
} }
this.subscriptions.push(clientRegistration.get('mapperConfig.type').valueChanges.subscribe((value) => { this.subscriptions.push(clientRegistration.get('mapperConfig.type').valueChanges.subscribe((value) => {
@ -313,7 +325,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha
private changeMapperConfigType(control: AbstractControl, type: MapperConfigType, predefinedValue?: MapperConfig) { private changeMapperConfigType(control: AbstractControl, type: MapperConfigType, predefinedValue?: MapperConfig) {
const mapperConfig = control.get('mapperConfig') as FormGroup; const mapperConfig = control.get('mapperConfig') as FormGroup;
if (type === 'BASIC') { if (type === MapperConfigType.BASIC) {
mapperConfig.removeControl('custom'); mapperConfig.removeControl('custom');
mapperConfig.addControl('basic', this.formBasicGroup(predefinedValue?.basic)); mapperConfig.addControl('basic', this.formBasicGroup(predefinedValue?.basic));
} else { } else {
@ -323,7 +335,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha
} }
save(): void { save(): void {
const setting = this.prepareFormValue(this.oauth2SettingsForm.getRawValue().clientDomains); const setting = this.prepareFormValue(this.oauth2SettingsForm.getRawValue());
this.adminService.saveOAuth2Settings(setting).subscribe( this.adminService.saveOAuth2Settings(setting).subscribe(
(oauth2Settings) => { (oauth2Settings) => {
this.oauth2Settings = oauth2Settings; this.oauth2Settings = oauth2Settings;
@ -333,20 +345,16 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha
); );
} }
private prepareFormValue(formValue: OAuth2Settings[]): OAuth2Settings[]{ private prepareFormValue(formValue: OAuth2Settings): OAuth2Settings{
const prepereValue = []; formValue.domainsParams.forEach((setting, index) => {
formValue.forEach((setting, index) => {
if (this.clientDomains.at(index).dirty) {
setting.clientRegistrations.forEach((registration) => { setting.clientRegistrations.forEach((registration) => {
registration.additionalInfo = JSON.stringify(registration.additionalInfo); registration.additionalInfo = JSON.stringify(registration.additionalInfo);
if (registration.id.id === null) { if (registration.id.id === null) {
delete registration.id; delete registration.id;
} }
}); });
prepereValue.push(setting);
}
}); });
return prepereValue; return formValue;
} }
confirmForm(): FormGroup { confirmForm(): FormGroup {
@ -359,6 +367,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha
const controller = control.get('scope') as FormArray; const controller = control.get('scope') as FormArray;
if ((value.trim() !== '')) { if ((value.trim() !== '')) {
controller.push(this.fb.control(value.trim())); controller.push(this.fb.control(value.trim()));
controller.markAsDirty();
} }
if (input) { if (input) {
@ -369,10 +378,12 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha
removeScope(i: number, control: AbstractControl): void { removeScope(i: number, control: AbstractControl): void {
const controller = control.get('scope') as FormArray; const controller = control.get('scope') as FormArray;
controller.removeAt(i); controller.removeAt(i);
controller.markAsTouched();
controller.markAsDirty();
} }
addDomain(): void { addDomain(): void {
this.clientDomains.push(this.buildSettingsDomain()); this.domainsParams.push(this.buildDomainsForm());
} }
deleteDomain($event: Event, index: number): void { deleteDomain($event: Event, index: number): void {
@ -381,97 +392,48 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha
$event.preventDefault(); $event.preventDefault();
} }
const domainName = this.clientDomains.at(index).get('domainName').value; const domainName = this.domainListTittle(this.domainsParams.at(index));
this.dialogService.confirm( this.dialogService.confirm(
this.translate.instant('admin.oauth2.delete-domain-title', {domainName: domainName || ''}), this.translate.instant('admin.oauth2.delete-domain-title', {domainName: domainName || ''}),
this.translate.instant('admin.oauth2.delete-domain-text'), null, this.translate.instant('admin.oauth2.delete-domain-text'), null,
this.translate.instant('action.delete') this.translate.instant('action.delete')
).subscribe((data) => { ).subscribe((data) => {
if (data) { if (data) {
if (index < this.oauth2Settings.length) { this.domainsParams.removeAt(index);
this.adminService.deleteOAuth2Domain(this.oauth2Settings[index].domainName).subscribe(() => { this.domainsParams.markAsTouched();
this.oauth2Settings.splice(index, 1); this.domainsParams.markAsDirty();
this.clientDomains.removeAt(index);
});
} else {
this.clientDomains.removeAt(index);
}
} }
}); });
} }
clientDomainRegistrations(control: AbstractControl): FormArray { clientDomainProviders(control: AbstractControl): FormArray {
return control.get('clientRegistrations') as FormArray; return control.get('clientRegistrations') as FormArray;
} }
addRegistration(control: AbstractControl): void { clientDomainInfos(control: AbstractControl): FormArray {
this.clientDomainRegistrations(control).push(this.buildSettingsRegistration()); return control.get('domainInfos') as FormArray;
}
addProvider(control: AbstractControl): void {
this.clientDomainProviders(control).push(this.buildProviderForm());
} }
deleteRegistration($event: Event, control: AbstractControl, index: number): void { deleteProvider($event: Event, control: AbstractControl, index: number): void {
if ($event) { if ($event) {
$event.stopPropagation(); $event.stopPropagation();
$event.preventDefault(); $event.preventDefault();
} }
const providerName = this.clientDomainRegistrations(control).at(index).get('additionalInfo.providerName').value; const providerName = this.clientDomainProviders(control).at(index).get('additionalInfo.providerName').value;
this.dialogService.confirm( this.dialogService.confirm(
this.translate.instant('admin.oauth2.delete-registration-title', {name: providerName || ''}), this.translate.instant('admin.oauth2.delete-registration-title', {name: providerName || ''}),
this.translate.instant('admin.oauth2.delete-registration-text'), null, this.translate.instant('admin.oauth2.delete-registration-text'), null,
this.translate.instant('action.delete') this.translate.instant('action.delete')
).subscribe((data) => { ).subscribe((data) => {
if (data) { if (data) {
const registrationId = this.clientDomainRegistrations(control).at(index).get('id.id').value; this.clientDomainProviders(control).removeAt(index);
if (registrationId) { this.clientDomainProviders(control).markAsTouched();
this.adminService.deleteOAuthCclientRegistrationId(registrationId).subscribe(() => { this.clientDomainProviders(control).markAsDirty();
this.clientDomainRegistrations(control).removeAt(index);
});
} else {
this.clientDomainRegistrations(control).removeAt(index);
}
}
});
}
editRedirectURI($event: MouseEvent, index: number) {
if ($event) {
$event.stopPropagation();
$event.preventDefault();
}
const target = $event.target || $event.currentTarget;
const config = new OverlayConfig();
config.backdropClass = 'cdk-overlay-transparent-backdrop';
config.hasBackdrop = true;
const connectedPosition: ConnectedPosition = {
originX: 'end',
originY: 'bottom',
overlayX: 'end',
overlayY: 'top'
};
config.positionStrategy = this.overlay.position().flexibleConnectedTo(target as HTMLElement)
.withPositions([connectedPosition]);
const overlayRef = this.overlay.create(config);
overlayRef.backdropClick().subscribe(() => {
overlayRef.dispose();
});
const redirectURI = this.clientDomains.at(index).get('redirectUriTemplate').value;
const injectionTokens = new WeakMap<any, any>([
[EDIT_REDIRECT_URI_PANEL_DATA, {
redirectURI
} as EditRedirectUriPanelData],
[OverlayRef, overlayRef]
]);
const injector = new PortalInjector(this.viewContainerRef.injector, injectionTokens);
const componentRef = overlayRef.attach(new ComponentPortal(EditRedirectUriPanelComponent,
this.viewContainerRef, injector));
componentRef.onDestroy(() => {
if (componentRef.instance.result !== null) {
const attributeValue = componentRef.instance.result;
this.clientDomains.at(index).get('redirectUriTemplate').patchValue(attributeValue);
this.clientDomains.at(index).get('redirectUriTemplate').markAsDirty();
} }
}); });
} }
@ -491,4 +453,43 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha
} }
return this.templates.get(provider).helpLink; return this.templates.get(provider).helpLink;
} }
addDomainInfo(control: AbstractControl): void {
this.clientDomainInfos(control).push(this.buildDomainForm({
name: '',
scheme: DomainSchema.HTTPS
}));
}
removeDomain($event: Event, control: AbstractControl, index: number): void {
if ($event) {
$event.stopPropagation();
$event.preventDefault();
}
this.clientDomainInfos(control).removeAt(index);
this.clientDomainInfos(control).markAsTouched();
this.clientDomainInfos(control).markAsDirty();
}
redirectURI(control: AbstractControl, schema?: DomainSchema): string {
const domainInfo = control.value as DomainInfo;
if (domainInfo.name !== '') {
let protocol;
if (isDefined(schema)) {
protocol = schema.toLowerCase();
} else {
protocol = domainInfo.scheme === DomainSchema.MIXED ? DomainSchema.HTTPS.toLowerCase() : domainInfo.scheme.toLowerCase();
}
return `${protocol}://${domainInfo.name}/login/oauth2/code/`;
}
return '';
}
redirectURIMixed(control: AbstractControl): string {
return this.redirectURI(control, DomainSchema.HTTP);
}
trackByParams(index: number): number {
return index;
}
} }

47
ui-ngx/src/app/shared/models/settings.models.ts

@ -26,10 +26,6 @@ export interface AdminSettings<T> {
export declare type SmtpProtocol = 'smtp' | 'smtps'; export declare type SmtpProtocol = 'smtp' | 'smtps';
export declare type ClientAuthenticationMethod = 'BASIC' | 'POST';
export declare type MapperConfigType = 'BASIC' | 'CUSTOM';
export declare type TenantNameStrategy = 'DOMAIN' | 'EMAIL' | 'CUSTOM';
export interface MailServerSettings { export interface MailServerSettings {
mailFrom: string; mailFrom: string;
smtpProtocol: SmtpProtocol; smtpProtocol: SmtpProtocol;
@ -69,9 +65,43 @@ export interface UpdateMessage {
} }
export interface OAuth2Settings { export interface OAuth2Settings {
domainName: string; enabled: boolean;
redirectUriTemplate: string; domainsParams: DomainsParam[];
}
export interface DomainsParam {
clientRegistrations: ClientRegistration[]; clientRegistrations: ClientRegistration[];
domainInfos: DomainInfo[];
}
export interface DomainInfo {
name: string;
scheme: DomainSchema;
}
export enum DomainSchema{
HTTP = 'HTTP',
HTTPS = 'HTTPS',
MIXED = 'MIXED'
}
export const domainSchemaTranslations = new Map<DomainSchema, string>(
[
[DomainSchema.HTTP, 'admin.oauth2.domain-schema-http'],
[DomainSchema.HTTPS, 'admin.oauth2.domain-schema-https'],
[DomainSchema.MIXED, 'admin.oauth2.domain-schema-mixed']
]
);
export enum MapperConfigType{
BASIC = 'BASIC',
CUSTOM = 'CUSTOM'
}
export enum TenantNameStrategy{
DOMAIN = 'DOMAIN',
EMAIL = 'EMAIL',
CUSTOM = 'CUSTOM'
} }
export interface ClientProviderTemplated extends ClientRegistration{ export interface ClientProviderTemplated extends ClientRegistration{
@ -100,6 +130,11 @@ export interface ClientRegistration {
additionalInfo: string; additionalInfo: string;
} }
export enum ClientAuthenticationMethod {
BASIC = 'BASIC',
POST = 'POST'
}
export interface MapperConfig { export interface MapperConfig {
allowUserCreation: boolean; allowUserCreation: boolean;
activateUser: boolean; activateUser: boolean;

20
ui-ngx/src/assets/locale/locale.constant-en_US.json

@ -128,7 +128,9 @@
"access-token-uri-required": "Access token URI is required.", "access-token-uri-required": "Access token URI is required.",
"activate-user": "Activate user", "activate-user": "Activate user",
"add-domain": "Add domain", "add-domain": "Add domain",
"delete-domain": "Delete domain",
"add-provider": "Add provider", "add-provider": "Add provider",
"delete-provider": "Delete provider",
"allow-user-creation": "Allow user creation", "allow-user-creation": "Allow user creation",
"always-fullscreen": "Always fullscreen", "always-fullscreen": "Always fullscreen",
"authorization-uri": "Authorization URI", "authorization-uri": "Authorization URI",
@ -141,10 +143,10 @@
"custom-setting": "Custom settings", "custom-setting": "Custom settings",
"customer-name-pattern": "Customer name pattern", "customer-name-pattern": "Customer name pattern",
"default-dashboard-name": "Default dashboard name", "default-dashboard-name": "Default dashboard name",
"delete-domain-text": "Be careful, after the confirmation a domain and all registration data will be unavailable.", "delete-domain-text": "Be careful, after the confirmation a domain and all provider data will be unavailable.",
"delete-domain-title": "Are you sure you want to delete the domain '{{domainName}}'?", "delete-domain-title": "Are you sure you want to delete settings the domain '{{domainName}}'?",
"delete-registration-text": "Be careful, after the confirmation a registration data will be unavailable.", "delete-registration-text": "Be careful, after the confirmation a provider data will be unavailable.",
"delete-registration-title": "Are you sure you want to delete the registration '{{name}}'?", "delete-registration-title": "Are you sure you want to delete the provider '{{name}}'?",
"email-attribute-key": "Email attribute key", "email-attribute-key": "Email attribute key",
"email-attribute-key-required": "Email attribute key is required.", "email-attribute-key-required": "Email attribute key is required.",
"first-name-attribute-key": "First name attribute key", "first-name-attribute-key": "First name attribute key",
@ -160,11 +162,12 @@
"new-domain": "New domain", "new-domain": "New domain",
"oauth2": "OAuth2", "oauth2": "OAuth2",
"redirect-uri-template": "Redirect URI template", "redirect-uri-template": "Redirect URI template",
"redirect-uri-template-required": "Redirect URI template is required.", "copy-redirect-uri": "Copy redirect URI",
"registration-id": "Registration ID", "registration-id": "Registration ID",
"registration-id-required": "Registration ID is required.", "registration-id-required": "Registration ID is required.",
"registration-id-unique": "Registration ID need to unique for the system.", "registration-id-unique": "Registration ID need to unique for the system.",
"scope": "Scope", "scope": "Scope",
"scope-required": "Scope is required.",
"tenant-name-pattern": "Tenant name pattern", "tenant-name-pattern": "Tenant name pattern",
"tenant-name-pattern-required": "Tenant name pattern is required.", "tenant-name-pattern-required": "Tenant name pattern is required.",
"tenant-name-strategy": "Tenant name strategy", "tenant-name-strategy": "Tenant name strategy",
@ -176,7 +179,12 @@
"user-info-uri": "User info URI", "user-info-uri": "User info URI",
"user-info-uri-required": "User info URI is required.", "user-info-uri-required": "User info URI is required.",
"user-name-attribute-name": "User name attribute key", "user-name-attribute-name": "User name attribute key",
"user-name-attribute-name-required": "User name attribute key is required" "user-name-attribute-name-required": "User name attribute key is required",
"protocol": "Protocol",
"domain-schema-http": "HTTP",
"domain-schema-https": "HTTPS",
"domain-schema-mixed": "HTTP+HTTPS",
"enable": "Enable OAuth2 settings"
} }
}, },
"alarm": { "alarm": {

Loading…
Cancel
Save