Commit fe00a9bd45842c425b2ffe05d1ff61360b1dd849

Authored by Vladyslav_Prykhodko
1 parent cd7a7ce4

UI: Added support multiple domains is OAuth2 settings

... ... @@ -60,27 +60,16 @@ export class AdminService {
60 60 defaultHttpOptionsFromConfig(config));
61 61 }
62 62
63   - public getOAuth2Settings(config?: RequestConfig): Observable<Array<OAuth2Settings>> {
64   - return this.http.get<Array<OAuth2Settings>>(`/api/oauth2/config`, defaultHttpOptionsFromConfig(config));
  63 + public getOAuth2Settings(config?: RequestConfig): Observable<OAuth2Settings> {
  64 + return this.http.get<OAuth2Settings>(`/api/oauth2/config`, defaultHttpOptionsFromConfig(config));
65 65 }
66 66
67 67 public getOAuth2Template(config?: RequestConfig): Observable<Array<ClientProviderTemplated>> {
68 68 return this.http.get<Array<ClientProviderTemplated>>(`/api/oauth2/config/template`, defaultHttpOptionsFromConfig(config));
69 69 }
70 70
71   - public saveOAuth2Settings(OAuth2Setting: OAuth2Settings[],
72   - config?: RequestConfig): Observable<Array<OAuth2Settings>> {
73   - return this.http.post<Array<OAuth2Settings>>('/api/oauth2/config', OAuth2Setting,
74   - defaultHttpOptionsFromConfig(config));
75   - }
76   -
77   - public deleteOAuth2Domain(OAuth2Domain: string, config?: RequestConfig) {
78   - return this.http.delete(`/api/oauth2/config/domain/${OAuth2Domain}`,
79   - defaultHttpOptionsFromConfig(config));
80   - }
81   -
82   - public deleteOAuthCclientRegistrationId(clientRegistrationId: string, config?: RequestConfig) {
83   - return this.http.delete(`/api/oauth2/config/${clientRegistrationId}`,
  71 + public saveOAuth2Settings(OAuth2Setting: OAuth2Settings, config?: RequestConfig): Observable<OAuth2Settings> {
  72 + return this.http.post<OAuth2Settings>('/api/oauth2/config', OAuth2Setting,
84 73 defaultHttpOptionsFromConfig(config));
85 74 }
86 75
... ...
... ... @@ -24,7 +24,6 @@ import { GeneralSettingsComponent } from '@modules/home/pages/admin/general-sett
24 24 import { SecuritySettingsComponent } from '@modules/home/pages/admin/security-settings.component';
25 25 import { HomeComponentsModule } from '@modules/home/components/home-components.module';
26 26 import { OAuth2SettingsComponent } from '@modules/home/pages/admin/oauth2-settings.component';
27   -import { EditRedirectUriPanelComponent } from './edit-redirect-uri-panel.component';
28 27
29 28 @NgModule({
30 29 declarations:
... ... @@ -32,8 +31,7 @@ import { EditRedirectUriPanelComponent } from './edit-redirect-uri-panel.compone
32 31 GeneralSettingsComponent,
33 32 MailServerComponent,
34 33 SecuritySettingsComponent,
35   - OAuth2SettingsComponent,
36   - EditRedirectUriPanelComponent
  34 + OAuth2SettingsComponent
37 35 ],
38 36 imports: [
39 37 CommonModule,
... ...
1   -<!--
2   -
3   - Copyright © 2016-2020 The Thingsboard Authors
4   -
5   - Licensed under the Apache License, Version 2.0 (the "License");
6   - you may not use this file except in compliance with the License.
7   - You may obtain a copy of the License at
8   -
9   - http://www.apache.org/licenses/LICENSE-2.0
10   -
11   - Unless required by applicable law or agreed to in writing, software
12   - distributed under the License is distributed on an "AS IS" BASIS,
13   - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   - See the License for the specific language governing permissions and
15   - limitations under the License.
16   -
17   --->
18   -<form class="mat-elevation-z4"
19   - [formGroup]="redirectURIFormGroup" (ngSubmit)="update()" style="width: 350px; padding: 12px 8px 0;">
20   - <fieldset [disabled]="isLoading$ | async">
21   - <mat-form-field fxFlex class="mat-block">
22   - <mat-label translate>admin.oauth2.redirect-uri-template</mat-label>
23   - <input matInput formControlName="value" required>
24   - <mat-error
25   - *ngIf="redirectURIFormGroup.get('value').hasError('required')">
26   - {{ 'admin.oauth2.redirect-uri-template-required' | translate }}
27   - </mat-error>
28   - <mat-error
29   - *ngIf="redirectURIFormGroup.get('value').hasError('pattern')">
30   - {{ 'admin.oauth2.uri-pattern-error' | translate }}
31   - </mat-error>
32   - </mat-form-field>
33   - </fieldset>
34   - <div fxLayout="row" class="tb-panel-actions">
35   - <span fxFlex></span>
36   - <button mat-button color="primary"
37   - style="margin-right: 20px;"
38   - type="button"
39   - [disabled]="(isLoading$ | async)"
40   - (click)="cancel()" cdkFocusInitial>
41   - {{ 'action.cancel' | translate }}
42   - </button>
43   - <button mat-button mat-raised-button color="primary"
44   - type="submit"
45   - [disabled]="(isLoading$ | async) || redirectURIFormGroup.invalid || !redirectURIFormGroup.dirty">
46   - {{ 'action.update' | translate }}
47   - </button>
48   - </div>
49   -</form>
1   -/**
2   - * Copyright © 2016-2020 The Thingsboard Authors
3   - *
4   - * Licensed under the Apache License, Version 2.0 (the "License");
5   - * you may not use this file except in compliance with the License.
6   - * You may obtain a copy of the License at
7   - *
8   - * http://www.apache.org/licenses/LICENSE-2.0
9   - *
10   - * Unless required by applicable law or agreed to in writing, software
11   - * distributed under the License is distributed on an "AS IS" BASIS,
12   - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   - * See the License for the specific language governing permissions and
14   - * limitations under the License.
15   - */
16   -:host {
17   - background-color: #f9f9f9;
18   -}
1   -///
2   -/// Copyright © 2016-2020 The Thingsboard Authors
3   -///
4   -/// Licensed under the Apache License, Version 2.0 (the "License");
5   -/// you may not use this file except in compliance with the License.
6   -/// You may obtain a copy of the License at
7   -///
8   -/// http://www.apache.org/licenses/LICENSE-2.0
9   -///
10   -/// Unless required by applicable law or agreed to in writing, software
11   -/// distributed under the License is distributed on an "AS IS" BASIS,
12   -/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   -/// See the License for the specific language governing permissions and
14   -/// limitations under the License.
15   -///
16   -
17   -import { Component, Inject, InjectionToken, OnInit, SkipSelf } from '@angular/core';
18   -import { ErrorStateMatcher } from '@angular/material/core';
19   -import { Store } from '@ngrx/store';
20   -import { AppState } from '@core/core.state';
21   -import { FormBuilder, FormControl, FormGroup, FormGroupDirective, NgForm, Validators } from '@angular/forms';
22   -import { PageComponent } from '@shared/components/page.component';
23   -import { OverlayRef } from '@angular/cdk/overlay';
24   -
25   -export const EDIT_REDIRECT_URI_PANEL_DATA = new InjectionToken<any>('EditRedirectUriPanelData');
26   -
27   -export interface EditRedirectUriPanelData {
28   - redirectURI: any;
29   -}
30   -
31   -@Component({
32   - selector: 'tb-edit-redirect-uri-panel',
33   - templateUrl: './edit-redirect-uri-panel.component.html',
34   - providers: [{provide: ErrorStateMatcher, useExisting: EditRedirectUriPanelComponent}],
35   - styleUrls: ['./edit-redirect-uri-panel.component.scss']
36   -})
37   -export class EditRedirectUriPanelComponent extends PageComponent implements OnInit, ErrorStateMatcher {
38   -
39   - private URL_REGEXP = /^[A-Za-z][A-Za-z\d.+-]*:\/*(?:\w+(?::\w+)?@)?[^\s/]+(?::\d+)?(?:\/[\w#!:.?+=&%@\-/]*)?$/;
40   -
41   - redirectURIFormGroup: FormGroup;
42   -
43   - result: any = null;
44   -
45   - submitted = false;
46   -
47   - constructor(protected store: Store<AppState>,
48   - @Inject(EDIT_REDIRECT_URI_PANEL_DATA) public data: EditRedirectUriPanelData,
49   - @SkipSelf() private errorStateMatcher: ErrorStateMatcher,
50   - public overlayRef: OverlayRef,
51   - public fb: FormBuilder) {
52   - super(store);
53   - }
54   -
55   - ngOnInit(): void {
56   - this.redirectURIFormGroup = this.fb.group({
57   - value: [this.data.redirectURI, [Validators.required, Validators.pattern(this.URL_REGEXP)]]
58   - });
59   - }
60   -
61   - isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
62   - const originalErrorState = this.errorStateMatcher.isErrorState(control, form);
63   - const customErrorState = !!(control && control.invalid && this.submitted);
64   - return originalErrorState || customErrorState;
65   - }
66   -
67   - cancel(): void {
68   - this.overlayRef.dispose();
69   - }
70   -
71   - update(): void {
72   - this.submitted = true;
73   - this.result = this.redirectURIFormGroup.get('value').value;
74   - this.overlayRef.dispose();
75   - }
76   -}
... ... @@ -30,23 +30,20 @@
30 30 <mat-card-content style="padding-top: 16px;">
31 31 <form [formGroup]="oauth2SettingsForm" (ngSubmit)="save()">
32 32 <fieldset [disabled]="isLoading$ | async">
33   - <ng-container formArrayName="clientDomains">
34   - <div class="container">
35   - <mat-accordion multi>
36   - <ng-container *ngFor="let domain of clientDomains.controls; let i = index; let last = last;">
37   - <mat-expansion-panel [formGroupName]="i" [expanded]="last">
38   - <mat-expansion-panel-header>
39   - <mat-panel-title fxLayoutAlign="start center">
40   - {{ domain.get('domainName').value ? domain.get('domainName').value : ("admin.oauth2.new-domain" | translate) }}
41   - </mat-panel-title>
42   - <mat-panel-description fxLayoutAlign="end center">
43   - <button mat-icon-button
44   - type="button"
45   - (click)="editRedirectURI($event, i)"
46   - matTooltip="{{ 'admin.oauth2.redirect-uri-template' | translate }}"
47   - matTooltipPosition="above">
48   - <mat-icon>link</mat-icon>
49   - </button>
  33 + <mat-checkbox formControlName="enabled">
  34 + {{ 'admin.oauth2.enable' | translate }}
  35 + </mat-checkbox>
  36 + <section *ngIf="oauth2SettingsForm.get('enabled').value && !(isLoading$ | async)" style="margin-top: 1em;">
  37 + <ng-container formArrayName="domainsParams">
  38 + <div class="container">
  39 + <mat-accordion multi>
  40 + <ng-container *ngFor="let domain of domainsParams.controls; let i = index; trackBy: trackByParams">
  41 + <mat-expansion-panel [formGroupName]="i">
  42 + <mat-expansion-panel-header>
  43 + <mat-panel-title fxLayoutAlign="start center">
  44 + {{ domainListTittle(domain) }}
  45 + </mat-panel-title>
  46 + <mat-panel-description fxLayoutAlign="end center">
50 47 <button mat-icon-button
51 48 type="button"
52 49 (click)="deleteDomain($event, i)"
... ... @@ -54,357 +51,428 @@
54 51 matTooltipPosition="above">
55 52 <mat-icon>delete</mat-icon>
56 53 </button>
57   - </mat-panel-description>
58   - </mat-expansion-panel-header>
59   -
60   - <ng-template matExpansionPanelContent>
61   - <mat-form-field class="mat-block">
62   - <mat-label translate>admin.domain-name</mat-label>
63   - <input matInput formControlName="domainName" required>
64   - <mat-error *ngIf="domain.get('domainName').hasError('pattern')">
65   - {{ 'admin.error-verification-url' | translate }}
66   - </mat-error>
67   - <mat-error *ngIf="domain.get('domainName').hasError('unique')">
68   - {{ 'admin.domain-name-unique' | translate }}
69   - </mat-error>
70   - </mat-form-field>
71   -
72   - <ng-container formArrayName="clientRegistrations">
73   - <div class="container">
74   - <mat-expansion-panel *ngFor="let registration of clientDomainRegistrations(domain).controls; let j = index;"
75   - class="registration-card mat-elevation-z0">
76   - <mat-expansion-panel-header>
77   - <mat-panel-title fxLayoutAlign="start center">
78   - {{ getProviderName(registration) }}
79   - </mat-panel-title>
80   - <mat-panel-description fxLayoutAlign="end center">
81   - <button mat-icon-button
82   - type="button"
83   - (click)="deleteRegistration($event, domain, j)"
84   - matTooltip="{{ 'action.delete' | translate }}"
85   - matTooltipPosition="above">
86   - <mat-icon>delete</mat-icon>
87   - </button>
88   - </mat-panel-description>
89   - </mat-expansion-panel-header>
90   -
91   - <ng-template matExpansionPanelContent>
92   - <section [formGroupName]="j">
93   - <section formGroupName="additionalInfo" fxLayout="row">
94   - <mat-form-field fxFlex class="mat-block">
95   - <mat-label translate>admin.oauth2.login-provider</mat-label>
96   - <mat-select formControlName="providerName">
97   - <mat-option *ngFor="let provider of templateProvider" [value]="provider">
98   - {{ provider | uppercase }}
  54 + </mat-panel-description>
  55 + </mat-expansion-panel-header>
  56 +
  57 + <ng-template matExpansionPanelContent>
  58 + <ng-container formArrayName="domainInfos">
  59 + <section *ngFor="let domainInfo of clientDomainInfos(domain).controls; let n = index; trackBy: trackByParams"
  60 + class="domains-list">
  61 + <div [formGroupName]="n" fxLayout="row" fxLayoutGap="8px">
  62 + <div fxLayout="row" fxLayout.xs="column" fxFlex fxLayoutGap="8px">
  63 + <div fxLayout="row" fxLayout.xs="column" fxLayoutGap="8px" fxFlex.gt-xs="50">
  64 + <mat-form-field fxFlex="30" fxFlex.xs class="mat-block">
  65 + <mat-label translate>admin.oauth2.protocol</mat-label>
  66 + <mat-select formControlName="scheme">
  67 + <mat-option *ngFor="let protocol of protocols" [value]="protocol">
  68 + {{ domainSchemaTranslations.get(protocol) | translate | uppercase }}
99 69 </mat-option>
100 70 </mat-select>
101 71 </mat-form-field>
102   - <div [tb-help]="getHelpLink(registration)" *ngIf="getProviderName(registration) !== 'Custom'"></div>
103   - </section>
104 72
105   - <div fxLayout="row" fxLayout.xs="column" fxLayoutGap.gt-xs="8px">
106 73 <mat-form-field fxFlex class="mat-block">
107   - <mat-label translate>admin.oauth2.client-id</mat-label>
108   - <input matInput formControlName="clientId" required>
109   - <mat-error *ngIf="registration.get('clientId').hasError('required')">
110   - {{ 'admin.oauth2.client-id-required' | translate }}
  74 + <mat-label translate>admin.domain-name</mat-label>
  75 + <input matInput formControlName="name" required>
  76 + <mat-error *ngIf="domainInfo.get('name').hasError('pattern')">
  77 + {{ 'admin.error-verification-url' | translate }}
  78 + </mat-error>
  79 + <mat-error *ngIf="domainInfo.get('name').hasError('unique')">
  80 + {{ 'admin.domain-name-unique' | translate }}
111 81 </mat-error>
112 82 </mat-form-field>
  83 + </div>
113 84
  85 + <div fxFlex>
114 86 <mat-form-field fxFlex class="mat-block">
115   - <mat-label translate>admin.oauth2.client-secret</mat-label>
116   - <input matInput formControlName="clientSecret" required>
117   - <mat-error *ngIf="registration.get('clientSecret').hasError('required')">
118   - {{ 'admin.oauth2.client-secret-required' | translate }}
119   - </mat-error>
  87 + <mat-label translate>admin.oauth2.redirect-uri-template</mat-label>
  88 + <input matInput [value]="redirectURI(domainInfo)" readonly>
  89 + <button mat-icon-button color="primary" matSuffix type="button"
  90 + ngxClipboard cbContent="{{ redirectURI(domainInfo) }}"
  91 + matTooltip="{{ 'admin.oauth2.copy-redirect-uri' | translate }}"
  92 + matTooltipPosition="above">
  93 + <mat-icon class="material-icons" svgIcon="mdi:clipboard-arrow-left"></mat-icon>
  94 + </button>
  95 + </mat-form-field>
  96 +
  97 + <mat-form-field fxFlex *ngIf="domainInfo.get('scheme').value === 'MIXED'"
  98 + class="mat-block">
  99 + <mat-label></mat-label>
  100 + <input matInput [value]="redirectURIMixed(domainInfo)" readonly>
  101 + <button mat-icon-button color="primary" matSuffix type="button"
  102 + ngxClipboard cbContent="{{ redirectURIMixed(domainInfo) }}"
  103 + matTooltip="{{ 'admin.oauth2.copy-redirect-uri' | translate }}"
  104 + matTooltipPosition="above">
  105 + <mat-icon class="material-icons" svgIcon="mdi:clipboard-arrow-left"></mat-icon>
  106 + </button>
120 107 </mat-form-field>
121 108 </div>
122 109
123   - <mat-expansion-panel class="mat-elevation-z0 custom-settings"
124   - [disabled]="getProviderName(registration) === 'Custom'"
125   - [expanded]="getProviderName(registration) === 'Custom'">
126   - <mat-expansion-panel-header [fxHide]="getProviderName(registration) === 'Custom'">
127   - <mat-panel-description fxLayoutAlign="end center">
128   - {{ 'admin.oauth2.custom-setting' | translate }}
129   - </mat-panel-description>
130   - </mat-expansion-panel-header>
131   - <ng-template matExpansionPanelContent>
132   - <mat-tab-group dynamicHeight>
133   - <mat-tab label="{{ 'admin.oauth2.general' | translate }}">
134   - <div fxLayout="row" fxLayout.xs="column" fxLayoutGap.gt-xs="8px" style="margin-top: 16px;">
135   - <mat-form-field fxFlex class="mat-block">
136   - <mat-label translate>admin.oauth2.access-token-uri</mat-label>
137   - <input matInput formControlName="accessTokenUri" required>
138   - <button mat-icon-button matSuffix
139   - type="button"
140   - (click)="toggleEditMode(registration, 'accessTokenUri')"
141   - *ngIf="getProviderName(registration) !== 'Custom'">
142   - <mat-icon class="material-icons">create</mat-icon>
143   - </button>
144   - <mat-error *ngIf="registration.get('accessTokenUri').hasError('required')">
145   - {{ 'admin.oauth2.access-token-uri-required' | translate }}
146   - </mat-error>
147   - <mat-error *ngIf="registration.get('accessTokenUri').hasError('pattern')">
148   - {{ 'admin.oauth2.uri-pattern-error' | translate }}
149   - </mat-error>
150   - </mat-form-field>
  110 + </div>
151 111
152   - <mat-form-field fxFlex class="mat-block">
153   - <mat-label translate>admin.oauth2.authorization-uri</mat-label>
154   - <input matInput formControlName="authorizationUri" required>
155   - <button mat-icon-button matSuffix
156   - type="button"
157   - (click)="toggleEditMode(registration, 'authorizationUri')"
158   - *ngIf="getProviderName(registration) !== 'Custom'">
159   - <mat-icon class="material-icons">create</mat-icon>
160   - </button>
161   - <mat-error *ngIf="registration.get('authorizationUri').hasError('required')">
162   - {{ 'admin.oauth2.authorization-uri-required' | translate }}
163   - </mat-error>
164   - <mat-error *ngIf="registration.get('authorizationUri').hasError('pattern')">
165   - {{ 'admin.oauth2.uri-pattern-error' | translate }}
166   - </mat-error>
167   - </mat-form-field>
168   - </div>
169   -
170   - <div fxLayout="row" fxLayout.xs="column" fxLayoutGap.gt-xs="8px">
171   - <mat-form-field fxFlex class="mat-block" appearance="legacy">
172   - <mat-label translate>admin.oauth2.jwk-set-uri</mat-label>
173   - <input matInput formControlName="jwkSetUri">
174   - <button mat-icon-button matSuffix
175   - type="button" aria-label="Clear"
176   - (click)="toggleEditMode(registration, 'jwkSetUri')"
177   - *ngIf="getProviderName(registration) !== 'Custom'">
178   - <mat-icon class="material-icons">create</mat-icon>
179   - </button>
180   - <mat-error *ngIf="registration.get('jwkSetUri').hasError('pattern')">
181   - {{ 'admin.oauth2.uri-pattern-error' | translate }}
182   - </mat-error>
183   - </mat-form-field>
  112 + <div fxLayout="column" fxLayoutAlign="center start">
  113 + <button type="button" mat-icon-button color="primary"
  114 + (click)="removeDomain($event, domain, n)"
  115 + [disabled]="clientDomainInfos(domain).controls.length < 2"
  116 + matTooltip="{{ 'admin.oauth2.delete-domain' | translate }}"
  117 + matTooltipPosition="above">
  118 + <mat-icon>close</mat-icon>
  119 + </button>
  120 + </div>
  121 + </div>
  122 + </section>
  123 + <div fxLayout="row" fxLayoutAlign="end center" style="margin-bottom: 1.25em">
  124 + <button mat-button mat-raised-button color="primary"
  125 + [disabled]="(isLoading$ | async)"
  126 + (click)="addDomainInfo(domain)"
  127 + type="button">
  128 + {{'admin.oauth2.add-domain' | translate}}
  129 + </button>
  130 + </div>
  131 + </ng-container>
  132 +
  133 + <ng-container formArrayName="clientRegistrations">
  134 + <div class="container">
  135 + <mat-expansion-panel *ngFor="let registration of clientDomainProviders(domain).controls; let j = index; trackBy: trackByParams"
  136 + class="registration-card mat-elevation-z0">
  137 + <mat-expansion-panel-header>
  138 + <mat-panel-title fxLayoutAlign="start center">
  139 + {{ getProviderName(registration) }}
  140 + </mat-panel-title>
  141 + <mat-panel-description fxLayoutAlign="end center">
  142 + <button mat-icon-button
  143 + type="button"
  144 + [disabled]="clientDomainProviders(domain).controls.length < 2"
  145 + (click)="deleteProvider($event, domain, j)"
  146 + matTooltip="{{ 'admin.oauth2.delete-provider' | translate }}"
  147 + matTooltipPosition="above">
  148 + <mat-icon>delete</mat-icon>
  149 + </button>
  150 + </mat-panel-description>
  151 + </mat-expansion-panel-header>
  152 +
  153 + <ng-template matExpansionPanelContent>
  154 + <section [formGroupName]="j">
  155 + <section formGroupName="additionalInfo" fxLayout="row">
  156 + <mat-form-field fxFlex class="mat-block">
  157 + <mat-label translate>admin.oauth2.login-provider</mat-label>
  158 + <mat-select formControlName="providerName">
  159 + <mat-option *ngFor="let provider of templateProvider" [value]="provider">
  160 + {{ provider | uppercase }}
  161 + </mat-option>
  162 + </mat-select>
  163 + </mat-form-field>
  164 + <div [tb-help]="getHelpLink(registration)" *ngIf="getProviderName(registration) !== 'Custom'"></div>
  165 + </section>
  166 +
  167 + <div fxLayout="row" fxLayout.xs="column" fxLayoutGap.gt-xs="8px">
  168 + <mat-form-field fxFlex class="mat-block">
  169 + <mat-label translate>admin.oauth2.client-id</mat-label>
  170 + <input matInput formControlName="clientId" required>
  171 + <mat-error *ngIf="registration.get('clientId').hasError('required')">
  172 + {{ 'admin.oauth2.client-id-required' | translate }}
  173 + </mat-error>
  174 + </mat-form-field>
  175 +
  176 + <mat-form-field fxFlex class="mat-block">
  177 + <mat-label translate>admin.oauth2.client-secret</mat-label>
  178 + <input matInput formControlName="clientSecret" required>
  179 + <mat-error *ngIf="registration.get('clientSecret').hasError('required')">
  180 + {{ 'admin.oauth2.client-secret-required' | translate }}
  181 + </mat-error>
  182 + </mat-form-field>
  183 + </div>
  184 +
  185 + <mat-expansion-panel class="mat-elevation-z0 custom-settings"
  186 + [disabled]="getProviderName(registration) === 'Custom'"
  187 + [expanded]="getProviderName(registration) === 'Custom'">
  188 + <mat-expansion-panel-header [fxHide]="getProviderName(registration) === 'Custom'">
  189 + <mat-panel-description fxLayoutAlign="end center">
  190 + {{ 'admin.oauth2.custom-setting' | translate }}
  191 + </mat-panel-description>
  192 + </mat-expansion-panel-header>
  193 + <ng-template matExpansionPanelContent>
  194 + <mat-tab-group dynamicHeight>
  195 + <mat-tab label="{{ 'admin.oauth2.general' | translate }}">
  196 + <div fxLayout="row" fxLayout.xs="column" fxLayoutGap.gt-xs="8px" style="margin-top: 16px;">
  197 + <mat-form-field fxFlex class="mat-block">
  198 + <mat-label translate>admin.oauth2.access-token-uri</mat-label>
  199 + <input matInput formControlName="accessTokenUri" required>
  200 + <button mat-icon-button matSuffix
  201 + type="button"
  202 + (click)="toggleEditMode(registration, 'accessTokenUri')"
  203 + *ngIf="getProviderName(registration) !== 'Custom'">
  204 + <mat-icon class="material-icons">create</mat-icon>
  205 + </button>
  206 + <mat-error *ngIf="registration.get('accessTokenUri').hasError('required')">
  207 + {{ 'admin.oauth2.access-token-uri-required' | translate }}
  208 + </mat-error>
  209 + <mat-error *ngIf="registration.get('accessTokenUri').hasError('pattern')">
  210 + {{ 'admin.oauth2.uri-pattern-error' | translate }}
  211 + </mat-error>
  212 + </mat-form-field>
184 213
185   - <mat-form-field fxFlex class="mat-block">
186   - <mat-label translate>admin.oauth2.user-info-uri</mat-label>
187   - <input matInput formControlName="userInfoUri" required>
188   - <button mat-icon-button matSuffix
189   - type="button"
190   - (click)="toggleEditMode(registration, 'userInfoUri')"
191   - *ngIf="getProviderName(registration) !== 'Custom'">
192   - <mat-icon class="material-icons">create</mat-icon>
193   - </button>
194   - <mat-error *ngIf="registration.get('userInfoUri').hasError('required')">
195   - {{ 'admin.oauth2.user-info-uri-required' | translate }}
196   - </mat-error>
197   - <mat-error *ngIf="registration.get('userInfoUri').hasError('pattern')">
198   - {{ 'admin.oauth2.uri-pattern-error' | translate }}
199   - </mat-error>
200   - </mat-form-field>
201   - </div>
202   -
203   - <mat-form-field fxFlex class="mat-block">
204   - <mat-label translate>admin.oauth2.client-authentication-method</mat-label>
205   - <mat-select formControlName="clientAuthenticationMethod">
206   - <mat-option *ngFor="let clientAuthenticationMethod of clientAuthenticationMethods"
207   - [value]="clientAuthenticationMethod">
208   - {{ clientAuthenticationMethod | uppercase }}
209   - </mat-option>
210   - </mat-select>
211   - </mat-form-field>
212   -
213   - <div fxLayout="row" fxLayout.xs="column" fxLayoutGap.gt-xs="8px" *ngIf="getProviderName(registration) === 'Custom'">
214   - <mat-form-field fxFlex class="mat-block" floatLabel="always">
215   - <mat-label translate>admin.oauth2.login-button-label</mat-label>
216   - <input matInput formControlName="loginButtonLabel" placeholder="{{ 'admin.oauth2.login-button-label-1' | translate }}" required>
217   - <mat-error
218   - *ngIf="registration.get('loginButtonLabel').hasError('required')">
219   - {{ 'admin.oauth2.login-button-label-required' | translate }}
220   - </mat-error>
221   - </mat-form-field>
  214 + <mat-form-field fxFlex class="mat-block">
  215 + <mat-label translate>admin.oauth2.authorization-uri</mat-label>
  216 + <input matInput formControlName="authorizationUri" required>
  217 + <button mat-icon-button matSuffix
  218 + type="button"
  219 + (click)="toggleEditMode(registration, 'authorizationUri')"
  220 + *ngIf="getProviderName(registration) !== 'Custom'">
  221 + <mat-icon class="material-icons">create</mat-icon>
  222 + </button>
  223 + <mat-error *ngIf="registration.get('authorizationUri').hasError('required')">
  224 + {{ 'admin.oauth2.authorization-uri-required' | translate }}
  225 + </mat-error>
  226 + <mat-error *ngIf="registration.get('authorizationUri').hasError('pattern')">
  227 + {{ 'admin.oauth2.uri-pattern-error' | translate }}
  228 + </mat-error>
  229 + </mat-form-field>
  230 + </div>
222 231
223   - <mat-form-field fxFlex class="mat-block">
224   - <mat-label translate>admin.oauth2.login-button-icon</mat-label>
225   - <input matInput formControlName="loginButtonIcon">
226   - </mat-form-field>
227   - </div>
228   -
229   - <section formGroupName="mapperConfig">
230   - <div fxLayout="column" fxLayoutGap="8px" style="margin-bottom: 8px;">
231   - <mat-checkbox formControlName="allowUserCreation">
232   - {{ 'admin.oauth2.allow-user-creation' | translate }}
233   - </mat-checkbox>
234   - <mat-checkbox formControlName="activateUser">
235   - {{ 'admin.oauth2.activate-user' | translate }}
236   - </mat-checkbox>
  232 + <div fxLayout="row" fxLayout.xs="column" fxLayoutGap.gt-xs="8px">
  233 + <mat-form-field fxFlex class="mat-block" appearance="legacy">
  234 + <mat-label translate>admin.oauth2.jwk-set-uri</mat-label>
  235 + <input matInput formControlName="jwkSetUri">
  236 + <button mat-icon-button matSuffix
  237 + type="button" aria-label="Clear"
  238 + (click)="toggleEditMode(registration, 'jwkSetUri')"
  239 + *ngIf="getProviderName(registration) !== 'Custom'">
  240 + <mat-icon class="material-icons">create</mat-icon>
  241 + </button>
  242 + <mat-error *ngIf="registration.get('jwkSetUri').hasError('pattern')">
  243 + {{ 'admin.oauth2.uri-pattern-error' | translate }}
  244 + </mat-error>
  245 + </mat-form-field>
  246 +
  247 + <mat-form-field fxFlex class="mat-block">
  248 + <mat-label translate>admin.oauth2.user-info-uri</mat-label>
  249 + <input matInput formControlName="userInfoUri" required>
  250 + <button mat-icon-button matSuffix
  251 + type="button"
  252 + (click)="toggleEditMode(registration, 'userInfoUri')"
  253 + *ngIf="getProviderName(registration) !== 'Custom'">
  254 + <mat-icon class="material-icons">create</mat-icon>
  255 + </button>
  256 + <mat-error *ngIf="registration.get('userInfoUri').hasError('required')">
  257 + {{ 'admin.oauth2.user-info-uri-required' | translate }}
  258 + </mat-error>
  259 + <mat-error *ngIf="registration.get('userInfoUri').hasError('pattern')">
  260 + {{ 'admin.oauth2.uri-pattern-error' | translate }}
  261 + </mat-error>
  262 + </mat-form-field>
237 263 </div>
238   - </section>
239   -
240   - <mat-form-field fxFlex class="mat-block">
241   - <mat-label translate>admin.oauth2.scope</mat-label>
242   - <mat-chip-list #scopeList>
243   - <mat-chip *ngFor="let scope of registration.get('scope').value; let k = index;"
244   - removable (removed)="removeScope(k, registration)">
245   - {{scope}}
246   - <mat-icon matChipRemove>cancel</mat-icon>
247   - </mat-chip>
248   - <input [matChipInputFor]="scopeList"
249   - [matChipInputSeparatorKeyCodes]="separatorKeysCodes"
250   - matChipInputAddOnBlur
251   - (matChipInputTokenEnd)="addScope($event, registration)">
252   - </mat-chip-list>
253   - </mat-form-field>
254   -
255   - </mat-tab>
256   - <mat-tab label="{{ 'admin.oauth2.mapper' | translate }}">
257   - <mat-form-field class="mat-block" style="margin-top: 16px;">
258   - <mat-label translate>admin.oauth2.user-name-attribute-name</mat-label>
259   - <input matInput formControlName="userNameAttributeName" required>
260   - <mat-error *ngIf="registration.get('userNameAttributeName').hasError('required')">
261   - {{ 'admin.oauth2.user-name-attribute-name-required' | translate }}
262   - </mat-error>
263   - </mat-form-field>
264   -
265   - <section formGroupName="mapperConfig">
  264 +
266 265 <mat-form-field fxFlex class="mat-block">
267   - <mat-label translate>admin.oauth2.type</mat-label>
268   - <mat-select formControlName="type">
269   - <mat-option *ngFor="let converterTypeExternalUser of converterTypesExternalUser"
270   - [value]="converterTypeExternalUser">
271   - {{ converterTypeExternalUser }}
  266 + <mat-label translate>admin.oauth2.client-authentication-method</mat-label>
  267 + <mat-select formControlName="clientAuthenticationMethod">
  268 + <mat-option *ngFor="let clientAuthenticationMethod of clientAuthenticationMethods"
  269 + [value]="clientAuthenticationMethod">
  270 + {{ clientAuthenticationMethod | uppercase }}
272 271 </mat-option>
273 272 </mat-select>
274 273 </mat-form-field>
275 274
276   - <section formGroupName="basic"
277   - *ngIf="registration.get('mapperConfig.type').value === 'BASIC'">
278   - <mat-form-field class="mat-block">
279   - <mat-label translate>admin.oauth2.email-attribute-key</mat-label>
280   - <input matInput formControlName="emailAttributeKey" required>
281   - <mat-error
282   - *ngIf="registration.get('mapperConfig.basic.emailAttributeKey').hasError('required')">
283   - {{ 'admin.oauth2.email-attribute-key-required' | translate }}
  275 + <div fxLayout="row" fxLayout.xs="column" fxLayoutGap.gt-xs="8px" *ngIf="getProviderName(registration) === 'Custom'">
  276 + <mat-form-field fxFlex class="mat-block" floatLabel="always">
  277 + <mat-label translate>admin.oauth2.login-button-label</mat-label>
  278 + <input matInput formControlName="loginButtonLabel"
  279 + placeholder="{{ 'admin.oauth2.login-button-label-1' | translate }}"
  280 + required>
  281 + <mat-error *ngIf="registration.get('loginButtonLabel').hasError('required')">
  282 + {{ 'admin.oauth2.login-button-label-required' | translate }}
284 283 </mat-error>
285 284 </mat-form-field>
286 285
287   - <div fxLayout="row" fxLayout.xs="column" fxLayoutGap.gt-xs="8px">
288   - <mat-form-field fxFlex class="mat-block">
289   - <mat-label translate>admin.oauth2.first-name-attribute-key</mat-label>
290   - <input matInput formControlName="firstNameAttributeKey">
291   - </mat-form-field>
  286 + <mat-form-field fxFlex class="mat-block">
  287 + <mat-label translate>admin.oauth2.login-button-icon</mat-label>
  288 + <input matInput formControlName="loginButtonIcon">
  289 + </mat-form-field>
  290 + </div>
292 291
293   - <mat-form-field fxFlex class="mat-block">
294   - <mat-label translate>admin.oauth2.last-name-attribute-key</mat-label>
295   - <input matInput formControlName="lastNameAttributeKey">
296   - </mat-form-field>
  292 + <section formGroupName="mapperConfig">
  293 + <div fxLayout="column" fxLayoutGap="8px" style="margin-bottom: 8px;">
  294 + <mat-checkbox formControlName="allowUserCreation">
  295 + {{ 'admin.oauth2.allow-user-creation' | translate }}
  296 + </mat-checkbox>
  297 + <mat-checkbox formControlName="activateUser">
  298 + {{ 'admin.oauth2.activate-user' | translate }}
  299 + </mat-checkbox>
297 300 </div>
  301 + </section>
298 302
299   - <div fxLayout="row" fxLayout.xs="column" fxLayoutGap.gt-xs="8px">
300   - <mat-form-field fxFlex class="mat-block">
301   - <mat-label translate>admin.oauth2.tenant-name-strategy</mat-label>
302   - <mat-select formControlName="tenantNameStrategy">
303   - <mat-option *ngFor="let tenantNameStrategy of tenantNameStrategies"
304   - [value]="tenantNameStrategy">
305   - {{ tenantNameStrategy }}
306   - </mat-option>
307   - </mat-select>
308   - </mat-form-field>
  303 + <mat-form-field fxFlex class="mat-block">
  304 + <mat-label translate>admin.oauth2.scope</mat-label>
  305 + <mat-chip-list #scopeList>
  306 + <mat-chip *ngFor="let scope of registration.get('scope').value; let k = index; trackBy: trackByParams"
  307 + removable (removed)="removeScope(k, registration)">
  308 + {{scope}}
  309 + <mat-icon matChipRemove>cancel</mat-icon>
  310 + </mat-chip>
  311 + <input [matChipInputFor]="scopeList"
  312 + [matChipInputSeparatorKeyCodes]="separatorKeysCodes"
  313 + matChipInputAddOnBlur
  314 + (matChipInputTokenEnd)="addScope($event, registration)">
  315 + </mat-chip-list>
  316 + <mat-error *ngIf="registration.get('scope').hasError('required')">
  317 + {{ 'admin.oauth2.scope-required' | translate }}
  318 + </mat-error>
  319 + </mat-form-field>
309 320
310   - <mat-form-field fxFlex class="mat-block" [fxShow]="registration.get('mapperConfig.basic.tenantNameStrategy').value === 'CUSTOM'">
311   - <mat-label translate>admin.oauth2.tenant-name-pattern</mat-label>
312   - <input matInput
313   - formControlName="tenantNamePattern"
314   - [required]="registration.get('mapperConfig.basic.tenantNameStrategy').value === 'CUSTOM'">
315   - <mat-error
316   - *ngIf="registration.get('mapperConfig.basic.tenantNamePattern').hasError('required')">
317   - {{ 'admin.oauth2.tenant-name-pattern-required' | translate }}
318   - </mat-error>
319   - </mat-form-field>
320   - </div>
  321 + </mat-tab>
  322 + <mat-tab label="{{ 'admin.oauth2.mapper' | translate }}">
  323 + <mat-form-field class="mat-block" style="margin-top: 16px;">
  324 + <mat-label translate>admin.oauth2.user-name-attribute-name</mat-label>
  325 + <input matInput formControlName="userNameAttributeName" required>
  326 + <mat-error
  327 + *ngIf="registration.get('userNameAttributeName').hasError('required')">
  328 + {{ 'admin.oauth2.user-name-attribute-name-required' | translate }}
  329 + </mat-error>
  330 + </mat-form-field>
321 331
  332 + <section formGroupName="mapperConfig">
322 333 <mat-form-field fxFlex class="mat-block">
323   - <mat-label translate>admin.oauth2.customer-name-pattern</mat-label>
324   - <input matInput formControlName="customerNamePattern">
  334 + <mat-label translate>admin.oauth2.type</mat-label>
  335 + <mat-select formControlName="type">
  336 + <mat-option *ngFor="let converterTypeExternalUser of converterTypesExternalUser"
  337 + [value]="converterTypeExternalUser">
  338 + {{ converterTypeExternalUser }}
  339 + </mat-option>
  340 + </mat-select>
325 341 </mat-form-field>
326 342
327   - <div fxLayout="row" fxLayout.xs="column" fxLayoutGap.gt-xs="8px">
328   - <mat-form-field fxFlex class="mat-block">
329   - <mat-label translate>admin.oauth2.default-dashboard-name</mat-label>
330   - <input matInput formControlName="defaultDashboardName">
  343 + <section formGroupName="basic"
  344 + *ngIf="registration.get('mapperConfig.type').value === 'BASIC'">
  345 + <mat-form-field class="mat-block">
  346 + <mat-label translate>admin.oauth2.email-attribute-key</mat-label>
  347 + <input matInput formControlName="emailAttributeKey" required>
  348 + <mat-error
  349 + *ngIf="registration.get('mapperConfig.basic.emailAttributeKey').hasError('required')">
  350 + {{ 'admin.oauth2.email-attribute-key-required' | translate }}
  351 + </mat-error>
331 352 </mat-form-field>
332 353
333   - <mat-checkbox fxFlex formControlName="alwaysFullScreen" class="checkbox-row">
334   - {{ 'admin.oauth2.always-fullscreen' | translate}}
335   - </mat-checkbox>
336   - </div>
337   - </section>
  354 + <div fxLayout="row" fxLayout.xs="column" fxLayoutGap.gt-xs="8px">
  355 + <mat-form-field fxFlex class="mat-block">
  356 + <mat-label translate>admin.oauth2.first-name-attribute-key</mat-label>
  357 + <input matInput formControlName="firstNameAttributeKey">
  358 + </mat-form-field>
  359 +
  360 + <mat-form-field fxFlex class="mat-block">
  361 + <mat-label translate>admin.oauth2.last-name-attribute-key</mat-label>
  362 + <input matInput formControlName="lastNameAttributeKey">
  363 + </mat-form-field>
  364 + </div>
  365 +
  366 + <div fxLayout="row" fxLayout.xs="column" fxLayoutGap.gt-xs="8px">
  367 + <mat-form-field fxFlex class="mat-block">
  368 + <mat-label translate>admin.oauth2.tenant-name-strategy</mat-label>
  369 + <mat-select formControlName="tenantNameStrategy">
  370 + <mat-option *ngFor="let tenantNameStrategy of tenantNameStrategies"
  371 + [value]="tenantNameStrategy">
  372 + {{ tenantNameStrategy }}
  373 + </mat-option>
  374 + </mat-select>
  375 + </mat-form-field>
  376 +
  377 + <mat-form-field fxFlex class="mat-block" [fxShow]="registration.get('mapperConfig.basic.tenantNameStrategy').value === 'CUSTOM'">
  378 + <mat-label translate>admin.oauth2.tenant-name-pattern</mat-label>
  379 + <input matInput
  380 + formControlName="tenantNamePattern"
  381 + [required]="registration.get('mapperConfig.basic.tenantNameStrategy').value === 'CUSTOM'">
  382 + <mat-error
  383 + *ngIf="registration.get('mapperConfig.basic.tenantNamePattern').hasError('required')">
  384 + {{ 'admin.oauth2.tenant-name-pattern-required' | translate }}
  385 + </mat-error>
  386 + </mat-form-field>
  387 + </div>
338 388
339   - <section formGroupName="custom"
340   - *ngIf="registration.get('mapperConfig.type').value === 'CUSTOM'">
341   - <mat-form-field class="mat-block">
342   - <mat-label translate>admin.oauth2.url</mat-label>
343   - <input matInput formControlName="url" required>
344   - <mat-error
345   - *ngIf="registration.get('mapperConfig.custom.url').hasError('required')">
346   - {{ 'admin.oauth2.url-required' | translate }}
347   - </mat-error>
348   - <mat-error
349   - *ngIf="registration.get('mapperConfig.custom.url').hasError('pattern')">
350   - {{ 'admin.oauth2.url-pattern' | translate }}
351   - </mat-error>
352   - </mat-form-field>
353   -
354   - <div fxLayout="row" fxLayout.xs="column" fxLayoutGap.gt-xs="8px">
355 389 <mat-form-field fxFlex class="mat-block">
356   - <mat-label translate>common.username</mat-label>
357   - <input matInput formControlName="username" autocomplete="new-username">
  390 + <mat-label translate>admin.oauth2.customer-name-pattern</mat-label>
  391 + <input matInput formControlName="customerNamePattern">
358 392 </mat-form-field>
359 393
360   - <mat-form-field fxFlex class="mat-block">
361   - <mat-label translate>common.password</mat-label>
362   - <input matInput type="password" formControlName="password" autocomplete="new-password">
  394 + <div fxLayout="row" fxLayout.xs="column" fxLayoutGap.gt-xs="8px">
  395 + <mat-form-field fxFlex class="mat-block">
  396 + <mat-label translate>admin.oauth2.default-dashboard-name</mat-label>
  397 + <input matInput formControlName="defaultDashboardName">
  398 + </mat-form-field>
  399 +
  400 + <mat-checkbox fxFlex formControlName="alwaysFullScreen" class="checkbox-row">
  401 + {{ 'admin.oauth2.always-fullscreen' | translate}}
  402 + </mat-checkbox>
  403 + </div>
  404 + </section>
  405 +
  406 + <section formGroupName="custom"
  407 + *ngIf="registration.get('mapperConfig.type').value === 'CUSTOM'">
  408 + <mat-form-field class="mat-block">
  409 + <mat-label translate>admin.oauth2.url</mat-label>
  410 + <input matInput formControlName="url" required>
  411 + <mat-error
  412 + *ngIf="registration.get('mapperConfig.custom.url').hasError('required')">
  413 + {{ 'admin.oauth2.url-required' | translate }}
  414 + </mat-error>
  415 + <mat-error
  416 + *ngIf="registration.get('mapperConfig.custom.url').hasError('pattern')">
  417 + {{ 'admin.oauth2.url-pattern' | translate }}
  418 + </mat-error>
363 419 </mat-form-field>
364   - </div>
  420 +
  421 + <div fxLayout="row" fxLayout.xs="column" fxLayoutGap.gt-xs="8px">
  422 + <mat-form-field fxFlex class="mat-block">
  423 + <mat-label translate>common.username</mat-label>
  424 + <input matInput formControlName="username" autocomplete="new-username">
  425 + </mat-form-field>
  426 +
  427 + <mat-form-field fxFlex class="mat-block">
  428 + <mat-label translate>common.password</mat-label>
  429 + <input matInput type="password" formControlName="password" autocomplete="new-password">
  430 + </mat-form-field>
  431 + </div>
  432 + </section>
365 433 </section>
366   - </section>
367   - </mat-tab>
368   - </mat-tab-group>
369   - </ng-template>
370   - </mat-expansion-panel>
371   -
372   - </section>
373   - </ng-template>
374   - </mat-expansion-panel>
  434 + </mat-tab>
  435 + </mat-tab-group>
  436 + </ng-template>
  437 + </mat-expansion-panel>
  438 +
  439 + </section>
  440 + </ng-template>
  441 + </mat-expansion-panel>
  442 + </div>
  443 + </ng-container>
  444 +
  445 + <div fxLayout="row" fxLayoutAlign="end center" fxLayoutGap="8px">
  446 + <button mat-button mat-raised-button color="primary"
  447 + [disabled]="(isLoading$ | async)"
  448 + (click)="addProvider(domain)"
  449 + type="button">
  450 + {{'admin.oauth2.add-provider' | translate}}
  451 + </button>
375 452 </div>
376   - </ng-container>
377   -
378   - <div fxLayout="row" fxLayoutAlign="end center" fxLayoutGap="8px">
379   - <button mat-button mat-raised-button color="primary"
380   - [disabled]="(isLoading$ | async)"
381   - (click)="addRegistration(domain)"
382   - type="button">
383   - {{'admin.oauth2.add-provider' | translate}}
384   - </button>
385   - </div>
386   - </ng-template>
387   -
388   - </mat-expansion-panel>
389   - </ng-container>
390   - </mat-accordion>
391   - </div>
392   - </ng-container>
393   -
394   - <div fxLayout="row" fxLayoutAlign="end center" fxLayoutGap="8px">
395   - <button mat-button mat-raised-button color="primary"
396   - [disabled]="(isLoading$ | async)"
397   - (click)="addDomain()"
398   - type="button">
399   - {{'admin.oauth2.add-domain' | translate}}
400   - </button>
401   - <button mat-button mat-raised-button color="primary"
402   - [disabled]="(isLoading$ | async) || oauth2SettingsForm.invalid || !oauth2SettingsForm.dirty"
403   - type="submit">
404   - {{'action.save' | translate}}
405   - </button>
406   - </div>
  453 + </ng-template>
  454 +
  455 + </mat-expansion-panel>
  456 + </ng-container>
  457 + </mat-accordion>
  458 + </div>
  459 + </ng-container>
  460 + </section>
407 461 </fieldset>
  462 + <div fxLayout="row" fxLayoutAlign="end center" fxLayoutGap="8px">
  463 + <button type="button" mat-raised-button color="primary"
  464 + [disabled]="isLoading$ | async"
  465 + *ngIf="oauth2SettingsForm.get('enabled').value"
  466 + (click)="addDomain()">
  467 + <mat-icon>add</mat-icon>
  468 + <span translate>action.add</span>
  469 + </button>
  470 + <button mat-button mat-raised-button color="primary"
  471 + [disabled]="(isLoading$ | async) || oauth2SettingsForm.invalid || !oauth2SettingsForm.dirty"
  472 + type="submit">
  473 + {{'action.save' | translate}}
  474 + </button>
  475 + </div>
408 476 </form>
409 477 </mat-card-content>
410 478 </mat-card>
... ...
... ... @@ -15,11 +15,11 @@
15 15 */
16 16 :host{
17 17 .checkbox-row {
18   - margin-top: 16px;
  18 + margin-top: 1em;
19 19 }
20 20
21 21 .registration-card{
22   - margin-bottom: 8px;
  22 + margin-bottom: 0.5em;
23 23 border: 1px solid rgba(0, 0, 0, 0.2);
24 24
25 25 .custom-settings{
... ... @@ -31,7 +31,15 @@
31 31 }
32 32
33 33 .container{
34   - margin-bottom: 16px;
  34 + margin-bottom: 1em;
  35 +
  36 + .tb-highlight{
  37 + margin: 0;
  38 + }
  39 +
  40 + .tb-hint{
  41 + padding-bottom: 0;
  42 + }
35 43 }
36 44 }
37 45
... ... @@ -39,7 +47,7 @@
39 47 .registration-card{
40 48 .custom-settings{
41 49 .mat-expansion-panel-body{
42   - padding: 0 2px 16px;
  50 + padding: 0 2px 1em;
43 51 }
44 52 .mat-tab-label{
45 53 text-transform: none;
... ... @@ -49,4 +57,10 @@
49 57 }
50 58 }
51 59 }
  60 + .domains-list{
  61 + margin-bottom: 1.5em;
  62 + .mat-form-field-suffix .mat-icon-button .mat-icon{
  63 + font-size: 24px;
  64 + }
  65 + }
52 66 }
... ...
... ... @@ -14,12 +14,16 @@
14 14 /// limitations under the License.
15 15 ///
16 16
17   -import { ChangeDetectorRef, Component, Inject, OnDestroy, OnInit, ViewContainerRef } from '@angular/core';
  17 +import { Component, Inject, OnDestroy, OnInit } from '@angular/core';
18 18 import { AbstractControl, FormArray, FormBuilder, FormGroup, Validators } from '@angular/forms';
19 19 import {
20 20 ClientAuthenticationMethod,
21 21 ClientProviderTemplated,
22 22 ClientRegistration,
  23 + DomainInfo,
  24 + DomainSchema,
  25 + domainSchemaTranslations,
  26 + DomainsParam,
23 27 MapperConfig,
24 28 MapperConfigBasic,
25 29 MapperConfigCustom,
... ... @@ -38,13 +42,7 @@ import { WINDOW } from '@core/services/window.service';
38 42 import { forkJoin, Subscription } from 'rxjs';
39 43 import { DialogService } from '@core/services/dialog.service';
40 44 import { TranslateService } from '@ngx-translate/core';
41   -import { ConnectedPosition, Overlay, OverlayConfig, OverlayRef } from '@angular/cdk/overlay';
42   -import { ComponentPortal, PortalInjector } from '@angular/cdk/portal';
43   -import {
44   - EDIT_REDIRECT_URI_PANEL_DATA,
45   - EditRedirectUriPanelComponent,
46   - EditRedirectUriPanelData
47   -} from './edit-redirect-uri-panel.component';
  45 +import { isDefined } from '@core/utils';
48 46
49 47 @Component({
50 48 selector: 'tb-oauth2-settings',
... ... @@ -60,15 +58,15 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha
60 58 additionalInfo: {
61 59 providerName: 'Custom'
62 60 },
63   - clientAuthenticationMethod: 'POST',
  61 + clientAuthenticationMethod: ClientAuthenticationMethod.POST,
64 62 userNameAttributeName: 'email',
65 63 mapperConfig: {
66 64 allowUserCreation: true,
67 65 activateUser: false,
68   - type: 'BASIC',
  66 + type: MapperConfigType.BASIC,
69 67 basic: {
70 68 emailAttributeKey: 'email',
71   - tenantNameStrategy: 'DOMAIN',
  69 + tenantNameStrategy: TenantNameStrategy.DOMAIN,
72 70 alwaysFullScreen: false
73 71 }
74 72 }
... ... @@ -77,19 +75,18 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha
77 75 readonly separatorKeysCodes: number[] = [ENTER, COMMA];
78 76
79 77 oauth2SettingsForm: FormGroup;
80   - oauth2Settings: OAuth2Settings[];
  78 + oauth2Settings: OAuth2Settings;
81 79
82   - clientAuthenticationMethods: ClientAuthenticationMethod[] = ['BASIC', 'POST'];
83   - converterTypesExternalUser: MapperConfigType[] = ['BASIC', 'CUSTOM'];
84   - tenantNameStrategies: TenantNameStrategy[] = ['DOMAIN', 'EMAIL', 'CUSTOM'];
  80 + clientAuthenticationMethods = Object.keys(ClientAuthenticationMethod);
  81 + converterTypesExternalUser = Object.keys(MapperConfigType);
  82 + tenantNameStrategies = Object.keys(TenantNameStrategy);
  83 + protocols = Object.keys(DomainSchema);
  84 + domainSchemaTranslations = domainSchemaTranslations;
85 85
86 86 templateProvider = ['Custom'];
87 87
88 88 constructor(protected store: Store<AppState>,
89 89 private adminService: AdminService,
90   - private overlay: Overlay,
91   - private viewContainerRef: ViewContainerRef,
92   - private cd: ChangeDetectorRef,
93 90 private fb: FormBuilder,
94 91 private dialogService: DialogService,
95 92 private translate: TranslateService,
... ... @@ -124,8 +121,8 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha
124 121 this.templateProvider.sort();
125 122 }
126 123
127   - get clientDomains(): FormArray {
128   - return this.oauth2SettingsForm.get('clientDomains') as FormArray;
  124 + get domainsParams(): FormArray {
  125 + return this.oauth2SettingsForm.get('domainsParams') as FormArray;
129 126 }
130 127
131 128 private formBasicGroup(mapperConfigBasic?: MapperConfigBasic): FormGroup {
... ... @@ -139,7 +136,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha
139 136 emailAttributeKey: [mapperConfigBasic?.emailAttributeKey ? mapperConfigBasic.emailAttributeKey : 'email', Validators.required],
140 137 firstNameAttributeKey: [mapperConfigBasic?.firstNameAttributeKey ? mapperConfigBasic.firstNameAttributeKey : ''],
141 138 lastNameAttributeKey: [mapperConfigBasic?.lastNameAttributeKey ? mapperConfigBasic.lastNameAttributeKey : ''],
142   - tenantNameStrategy: [mapperConfigBasic?.tenantNameStrategy ? mapperConfigBasic.tenantNameStrategy : 'DOMAIN'],
  139 + tenantNameStrategy: [mapperConfigBasic?.tenantNameStrategy ? mapperConfigBasic.tenantNameStrategy : TenantNameStrategy.DOMAIN],
143 140 tenantNamePattern: [tenantNamePattern, Validators.required],
144 141 customerNamePattern: [mapperConfigBasic?.customerNamePattern ? mapperConfigBasic.customerNamePattern : null],
145 142 defaultDashboardName: [mapperConfigBasic?.defaultDashboardName ? mapperConfigBasic.defaultDashboardName : null],
... ... @@ -167,69 +164,78 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha
167 164
168 165 private buildOAuth2SettingsForm(): void {
169 166 this.oauth2SettingsForm = this.fb.group({
170   - clientDomains: this.fb.array([])
  167 + domainsParams: this.fb.array([]),
  168 + enabled: [false]
171 169 });
172 170 }
173 171
174   - private initOAuth2Settings(oauth2Settings: OAuth2Settings[]): void {
  172 + private initOAuth2Settings(oauth2Settings: OAuth2Settings): void {
175 173 if (oauth2Settings) {
176   - oauth2Settings.forEach((domain) => {
177   - this.clientDomains.push(this.buildSettingsDomain(domain));
  174 + this.oauth2SettingsForm.patchValue({enabled: oauth2Settings.enabled}, {emitEvent: false});
  175 + oauth2Settings.domainsParams.forEach((domain) => {
  176 + this.domainsParams.push(this.buildDomainsForm(domain));
178 177 });
179 178 }
180 179 }
181 180
182 181 private uniqueDomainValidator(control: AbstractControl): { [key: string]: boolean } | null {
183   - if (control.root.value.clientDomains?.length > 1) {
184   - const listDomainName = [];
185   - control.root.value.clientDomains.forEach((domain) => {
186   - listDomainName.push(domain.domainName);
187   - });
188   - if (listDomainName.indexOf(control.value) > -1) {
  182 + if (control.parent?.parent?.value) {
  183 + const domain = control.value;
  184 + const listProtocols = control.parent.parent.value
  185 + .filter((domainInfo) => domainInfo.name === domain)
  186 + .map((domainInfo) => domainInfo.scheme);
  187 + if (listProtocols.length > 1 && listProtocols.indexOf(DomainSchema.MIXED) > -1) {
189 188 return {unique: true};
190 189 }
191 190 }
192 191 return null;
193 192 }
194 193
195   - private buildSettingsDomain(domainParams?: OAuth2Settings): FormGroup {
196   - let url = this.window.location.protocol + '//' + this.window.location.hostname;
197   - const port = this.window.location.port;
198   - if (port !== '80' && port !== '443') {
199   - url += ':' + port;
  194 + public domainListTittle(control: AbstractControl): string {
  195 + const domainInfos = control.get('domainInfos').value as DomainInfo[];
  196 + if (domainInfos.length) {
  197 + const domainList = new Set<string>();
  198 + domainInfos.forEach((domain) => {
  199 + domainList.add(domain.name);
  200 + });
  201 + return Array.from(domainList).join(', ');
200 202 }
201   - url += '/login/oauth2/code/';
  203 + return this.translate.instant('admin.oauth2.new-domain');
  204 + }
  205 +
  206 + private buildDomainsForm(domainParams?: DomainsParam): FormGroup {
202 207 const formDomain = this.fb.group({
203   - domainName: [domainParams?.domainName ? domainParams.domainName : this.window.location.hostname, [
204   - Validators.required,
205   - Validators.pattern('((?![:/]).)*$'),
206   - this.uniqueDomainValidator]],
207   - redirectUriTemplate: [domainParams?.redirectUriTemplate ? domainParams.redirectUriTemplate : url, [
208   - Validators.required,
209   - Validators.pattern(this.URL_REGEXP)]],
  208 + domainInfos: this.fb.array([], Validators.required),
210 209 clientRegistrations: this.fb.array([], Validators.required)
211 210 });
212 211
213   - this.subscriptions.push(formDomain.get('domainName').valueChanges.subscribe((domain) => {
214   - if (!domain) {
215   - domain = this.window.location.hostname;
216   - }
217   - const uri = this.window.location.protocol + `//${domain}/login/oauth2/code/`;
218   - formDomain.get('redirectUriTemplate').patchValue(uri, {emitEvent: false});
219   - }));
220   -
221 212 if (domainParams) {
  213 + domainParams.domainInfos.forEach((domain) => {
  214 + this.clientDomainInfos(formDomain).push(this.buildDomainForm(domain));
  215 + });
222 216 domainParams.clientRegistrations.forEach((registration) => {
223   - this.clientDomainRegistrations(formDomain).push(this.buildSettingsRegistration(registration));
  217 + this.clientDomainProviders(formDomain).push(this.buildProviderForm(registration));
224 218 });
225 219 } else {
226   - this.clientDomainRegistrations(formDomain).push(this.buildSettingsRegistration());
  220 + this.clientDomainProviders(formDomain).push(this.buildProviderForm());
  221 + this.clientDomainInfos(formDomain).push(this.buildDomainForm());
227 222 }
228 223
229 224 return formDomain;
230 225 }
231 226
232   - private buildSettingsRegistration(registrationData?: ClientRegistration): FormGroup {
  227 + private buildDomainForm(domainInfo?: DomainInfo): FormGroup {
  228 + const domain = this.fb.group({
  229 + name: [domainInfo ? domainInfo.name : this.window.location.hostname, [
  230 + Validators.required,
  231 + Validators.pattern('((?![:/]).)*$'),
  232 + this.uniqueDomainValidator]],
  233 + scheme: [domainInfo?.scheme ? domainInfo.scheme : DomainSchema.HTTPS, Validators.required]
  234 + });
  235 + return domain;
  236 + }
  237 +
  238 + private buildProviderForm(registrationData?: ClientRegistration): FormGroup {
233 239 let additionalInfo = null;
234 240 if (registrationData?.additionalInfo) {
235 241 additionalInfo = JSON.parse(registrationData.additionalInfo);
... ... @@ -237,13 +243,18 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha
237 243 additionalInfo.providerName = 'Custom';
238 244 }
239 245 }
  246 + let defaultProviderName = 'Custom';
  247 + if (this.templateProvider.indexOf('Google')) {
  248 + defaultProviderName = 'Google';
  249 + }
  250 +
240 251 const clientRegistration = this.fb.group({
241 252 id: this.fb.group({
242 253 id: [registrationData?.id?.id ? registrationData.id.id : null],
243 254 entityType: [registrationData?.id?.entityType ? registrationData.id.entityType : null]
244 255 }),
245 256 additionalInfo: this.fb.group({
246   - providerName: [additionalInfo?.providerName ? additionalInfo?.providerName : 'Custom', Validators.required]
  257 + providerName: [additionalInfo?.providerName ? additionalInfo?.providerName : defaultProviderName, Validators.required]
247 258 }),
248 259 loginButtonLabel: [registrationData?.loginButtonLabel ? registrationData.loginButtonLabel : null, Validators.required],
249 260 loginButtonIcon: [registrationData?.loginButtonIcon ? registrationData.loginButtonIcon : null],
... ... @@ -255,19 +266,20 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha
255 266 authorizationUri: [registrationData?.authorizationUri ? registrationData.authorizationUri : '',
256 267 [Validators.required,
257 268 Validators.pattern(this.URL_REGEXP)]],
258   - scope: this.fb.array(registrationData?.scope ? registrationData.scope : []),
  269 + scope: this.fb.array(registrationData?.scope ? registrationData.scope : [], Validators.required),
259 270 jwkSetUri: [registrationData?.jwkSetUri ? registrationData.jwkSetUri : '', Validators.pattern(this.URL_REGEXP)],
260 271 userInfoUri: [registrationData?.userInfoUri ? registrationData.userInfoUri : '',
261 272 [Validators.required,
262 273 Validators.pattern(this.URL_REGEXP)]],
263 274 clientAuthenticationMethod: [
264   - registrationData?.clientAuthenticationMethod ? registrationData.clientAuthenticationMethod : 'POST', Validators.required],
  275 + registrationData?.clientAuthenticationMethod ? registrationData.clientAuthenticationMethod : ClientAuthenticationMethod.POST,
  276 + Validators.required],
265 277 userNameAttributeName: [
266 278 registrationData?.userNameAttributeName ? registrationData.userNameAttributeName : 'email', Validators.required],
267 279 mapperConfig: this.fb.group({
268 280 allowUserCreation: [registrationData?.mapperConfig?.allowUserCreation ? registrationData.mapperConfig.allowUserCreation : true],
269 281 activateUser: [registrationData?.mapperConfig?.activateUser ? registrationData.mapperConfig.activateUser : false],
270   - type: [registrationData?.mapperConfig?.type ? registrationData.mapperConfig.type : 'BASIC', Validators.required]
  282 + type: [registrationData?.mapperConfig?.type ? registrationData.mapperConfig.type : MapperConfigType.BASIC, Validators.required]
271 283 }
272 284 )
273 285 });
... ... @@ -275,7 +287,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha
275 287 if (registrationData) {
276 288 this.changeMapperConfigType(clientRegistration, registrationData.mapperConfig.type, registrationData.mapperConfig);
277 289 } else {
278   - this.changeMapperConfigType(clientRegistration, 'BASIC');
  290 + this.changeMapperConfigType(clientRegistration, MapperConfigType.BASIC);
279 291 }
280 292
281 293 this.subscriptions.push(clientRegistration.get('mapperConfig.type').valueChanges.subscribe((value) => {
... ... @@ -313,7 +325,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha
313 325
314 326 private changeMapperConfigType(control: AbstractControl, type: MapperConfigType, predefinedValue?: MapperConfig) {
315 327 const mapperConfig = control.get('mapperConfig') as FormGroup;
316   - if (type === 'BASIC') {
  328 + if (type === MapperConfigType.BASIC) {
317 329 mapperConfig.removeControl('custom');
318 330 mapperConfig.addControl('basic', this.formBasicGroup(predefinedValue?.basic));
319 331 } else {
... ... @@ -323,7 +335,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha
323 335 }
324 336
325 337 save(): void {
326   - const setting = this.prepareFormValue(this.oauth2SettingsForm.getRawValue().clientDomains);
  338 + const setting = this.prepareFormValue(this.oauth2SettingsForm.getRawValue());
327 339 this.adminService.saveOAuth2Settings(setting).subscribe(
328 340 (oauth2Settings) => {
329 341 this.oauth2Settings = oauth2Settings;
... ... @@ -333,20 +345,16 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha
333 345 );
334 346 }
335 347
336   - private prepareFormValue(formValue: OAuth2Settings[]): OAuth2Settings[]{
337   - const prepereValue = [];
338   - formValue.forEach((setting, index) => {
339   - if (this.clientDomains.at(index).dirty) {
340   - setting.clientRegistrations.forEach((registration) => {
341   - registration.additionalInfo = JSON.stringify(registration.additionalInfo);
342   - if (registration.id.id === null) {
343   - delete registration.id;
344   - }
345   - });
346   - prepereValue.push(setting);
347   - }
  348 + private prepareFormValue(formValue: OAuth2Settings): OAuth2Settings{
  349 + formValue.domainsParams.forEach((setting, index) => {
  350 + setting.clientRegistrations.forEach((registration) => {
  351 + registration.additionalInfo = JSON.stringify(registration.additionalInfo);
  352 + if (registration.id.id === null) {
  353 + delete registration.id;
  354 + }
  355 + });
348 356 });
349   - return prepereValue;
  357 + return formValue;
350 358 }
351 359
352 360 confirmForm(): FormGroup {
... ... @@ -359,6 +367,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha
359 367 const controller = control.get('scope') as FormArray;
360 368 if ((value.trim() !== '')) {
361 369 controller.push(this.fb.control(value.trim()));
  370 + controller.markAsDirty();
362 371 }
363 372
364 373 if (input) {
... ... @@ -369,10 +378,12 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha
369 378 removeScope(i: number, control: AbstractControl): void {
370 379 const controller = control.get('scope') as FormArray;
371 380 controller.removeAt(i);
  381 + controller.markAsTouched();
  382 + controller.markAsDirty();
372 383 }
373 384
374 385 addDomain(): void {
375   - this.clientDomains.push(this.buildSettingsDomain());
  386 + this.domainsParams.push(this.buildDomainsForm());
376 387 }
377 388
378 389 deleteDomain($event: Event, index: number): void {
... ... @@ -381,97 +392,48 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha
381 392 $event.preventDefault();
382 393 }
383 394
384   - const domainName = this.clientDomains.at(index).get('domainName').value;
  395 + const domainName = this.domainListTittle(this.domainsParams.at(index));
385 396 this.dialogService.confirm(
386 397 this.translate.instant('admin.oauth2.delete-domain-title', {domainName: domainName || ''}),
387 398 this.translate.instant('admin.oauth2.delete-domain-text'), null,
388 399 this.translate.instant('action.delete')
389 400 ).subscribe((data) => {
390 401 if (data) {
391   - if (index < this.oauth2Settings.length) {
392   - this.adminService.deleteOAuth2Domain(this.oauth2Settings[index].domainName).subscribe(() => {
393   - this.oauth2Settings.splice(index, 1);
394   - this.clientDomains.removeAt(index);
395   - });
396   - } else {
397   - this.clientDomains.removeAt(index);
398   - }
  402 + this.domainsParams.removeAt(index);
  403 + this.domainsParams.markAsTouched();
  404 + this.domainsParams.markAsDirty();
399 405 }
400 406 });
401 407 }
402 408
403   - clientDomainRegistrations(control: AbstractControl): FormArray {
  409 + clientDomainProviders(control: AbstractControl): FormArray {
404 410 return control.get('clientRegistrations') as FormArray;
405 411 }
406 412
407   - addRegistration(control: AbstractControl): void {
408   - this.clientDomainRegistrations(control).push(this.buildSettingsRegistration());
  413 + clientDomainInfos(control: AbstractControl): FormArray {
  414 + return control.get('domainInfos') as FormArray;
409 415 }
410 416
411   - deleteRegistration($event: Event, control: AbstractControl, index: number): void {
  417 + addProvider(control: AbstractControl): void {
  418 + this.clientDomainProviders(control).push(this.buildProviderForm());
  419 + }
  420 +
  421 + deleteProvider($event: Event, control: AbstractControl, index: number): void {
412 422 if ($event) {
413 423 $event.stopPropagation();
414 424 $event.preventDefault();
415 425 }
416 426
417   - const providerName = this.clientDomainRegistrations(control).at(index).get('additionalInfo.providerName').value;
  427 + const providerName = this.clientDomainProviders(control).at(index).get('additionalInfo.providerName').value;
418 428 this.dialogService.confirm(
419 429 this.translate.instant('admin.oauth2.delete-registration-title', {name: providerName || ''}),
420 430 this.translate.instant('admin.oauth2.delete-registration-text'), null,
421 431 this.translate.instant('action.delete')
422 432 ).subscribe((data) => {
423 433 if (data) {
424   - const registrationId = this.clientDomainRegistrations(control).at(index).get('id.id').value;
425   - if (registrationId) {
426   - this.adminService.deleteOAuthCclientRegistrationId(registrationId).subscribe(() => {
427   - this.clientDomainRegistrations(control).removeAt(index);
428   - });
429   - } else {
430   - this.clientDomainRegistrations(control).removeAt(index);
431   - }
432   - }
433   - });
434   - }
435   -
436   - editRedirectURI($event: MouseEvent, index: number) {
437   - if ($event) {
438   - $event.stopPropagation();
439   - $event.preventDefault();
440   - }
441   - const target = $event.target || $event.currentTarget;
442   - const config = new OverlayConfig();
443   - config.backdropClass = 'cdk-overlay-transparent-backdrop';
444   - config.hasBackdrop = true;
445   - const connectedPosition: ConnectedPosition = {
446   - originX: 'end',
447   - originY: 'bottom',
448   - overlayX: 'end',
449   - overlayY: 'top'
450   - };
451   - config.positionStrategy = this.overlay.position().flexibleConnectedTo(target as HTMLElement)
452   - .withPositions([connectedPosition]);
453   -
454   - const overlayRef = this.overlay.create(config);
455   - overlayRef.backdropClick().subscribe(() => {
456   - overlayRef.dispose();
457   - });
458   -
459   - const redirectURI = this.clientDomains.at(index).get('redirectUriTemplate').value;
460   -
461   - const injectionTokens = new WeakMap<any, any>([
462   - [EDIT_REDIRECT_URI_PANEL_DATA, {
463   - redirectURI
464   - } as EditRedirectUriPanelData],
465   - [OverlayRef, overlayRef]
466   - ]);
467   - const injector = new PortalInjector(this.viewContainerRef.injector, injectionTokens);
468   - const componentRef = overlayRef.attach(new ComponentPortal(EditRedirectUriPanelComponent,
469   - this.viewContainerRef, injector));
470   - componentRef.onDestroy(() => {
471   - if (componentRef.instance.result !== null) {
472   - const attributeValue = componentRef.instance.result;
473   - this.clientDomains.at(index).get('redirectUriTemplate').patchValue(attributeValue);
474   - this.clientDomains.at(index).get('redirectUriTemplate').markAsDirty();
  434 + this.clientDomainProviders(control).removeAt(index);
  435 + this.clientDomainProviders(control).markAsTouched();
  436 + this.clientDomainProviders(control).markAsDirty();
475 437 }
476 438 });
477 439 }
... ... @@ -491,4 +453,43 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha
491 453 }
492 454 return this.templates.get(provider).helpLink;
493 455 }
  456 +
  457 + addDomainInfo(control: AbstractControl): void {
  458 + this.clientDomainInfos(control).push(this.buildDomainForm({
  459 + name: '',
  460 + scheme: DomainSchema.HTTPS
  461 + }));
  462 + }
  463 +
  464 + removeDomain($event: Event, control: AbstractControl, index: number): void {
  465 + if ($event) {
  466 + $event.stopPropagation();
  467 + $event.preventDefault();
  468 + }
  469 + this.clientDomainInfos(control).removeAt(index);
  470 + this.clientDomainInfos(control).markAsTouched();
  471 + this.clientDomainInfos(control).markAsDirty();
  472 + }
  473 +
  474 + redirectURI(control: AbstractControl, schema?: DomainSchema): string {
  475 + const domainInfo = control.value as DomainInfo;
  476 + if (domainInfo.name !== '') {
  477 + let protocol;
  478 + if (isDefined(schema)) {
  479 + protocol = schema.toLowerCase();
  480 + } else {
  481 + protocol = domainInfo.scheme === DomainSchema.MIXED ? DomainSchema.HTTPS.toLowerCase() : domainInfo.scheme.toLowerCase();
  482 + }
  483 + return `${protocol}://${domainInfo.name}/login/oauth2/code/`;
  484 + }
  485 + return '';
  486 + }
  487 +
  488 + redirectURIMixed(control: AbstractControl): string {
  489 + return this.redirectURI(control, DomainSchema.HTTP);
  490 + }
  491 +
  492 + trackByParams(index: number): number {
  493 + return index;
  494 + }
494 495 }
... ...
... ... @@ -26,10 +26,6 @@ export interface AdminSettings<T> {
26 26
27 27 export declare type SmtpProtocol = 'smtp' | 'smtps';
28 28
29   -export declare type ClientAuthenticationMethod = 'BASIC' | 'POST';
30   -export declare type MapperConfigType = 'BASIC' | 'CUSTOM';
31   -export declare type TenantNameStrategy = 'DOMAIN' | 'EMAIL' | 'CUSTOM';
32   -
33 29 export interface MailServerSettings {
34 30 mailFrom: string;
35 31 smtpProtocol: SmtpProtocol;
... ... @@ -69,9 +65,43 @@ export interface UpdateMessage {
69 65 }
70 66
71 67 export interface OAuth2Settings {
72   - domainName: string;
73   - redirectUriTemplate: string;
  68 + enabled: boolean;
  69 + domainsParams: DomainsParam[];
  70 +}
  71 +
  72 +export interface DomainsParam {
74 73 clientRegistrations: ClientRegistration[];
  74 + domainInfos: DomainInfo[];
  75 +}
  76 +
  77 +export interface DomainInfo {
  78 + name: string;
  79 + scheme: DomainSchema;
  80 +}
  81 +
  82 +export enum DomainSchema{
  83 + HTTP = 'HTTP',
  84 + HTTPS = 'HTTPS',
  85 + MIXED = 'MIXED'
  86 +}
  87 +
  88 +export const domainSchemaTranslations = new Map<DomainSchema, string>(
  89 + [
  90 + [DomainSchema.HTTP, 'admin.oauth2.domain-schema-http'],
  91 + [DomainSchema.HTTPS, 'admin.oauth2.domain-schema-https'],
  92 + [DomainSchema.MIXED, 'admin.oauth2.domain-schema-mixed']
  93 + ]
  94 +);
  95 +
  96 +export enum MapperConfigType{
  97 + BASIC = 'BASIC',
  98 + CUSTOM = 'CUSTOM'
  99 +}
  100 +
  101 +export enum TenantNameStrategy{
  102 + DOMAIN = 'DOMAIN',
  103 + EMAIL = 'EMAIL',
  104 + CUSTOM = 'CUSTOM'
75 105 }
76 106
77 107 export interface ClientProviderTemplated extends ClientRegistration{
... ... @@ -100,6 +130,11 @@ export interface ClientRegistration {
100 130 additionalInfo: string;
101 131 }
102 132
  133 +export enum ClientAuthenticationMethod {
  134 + BASIC = 'BASIC',
  135 + POST = 'POST'
  136 +}
  137 +
103 138 export interface MapperConfig {
104 139 allowUserCreation: boolean;
105 140 activateUser: boolean;
... ...
... ... @@ -128,7 +128,9 @@
128 128 "access-token-uri-required": "Access token URI is required.",
129 129 "activate-user": "Activate user",
130 130 "add-domain": "Add domain",
  131 + "delete-domain": "Delete domain",
131 132 "add-provider": "Add provider",
  133 + "delete-provider": "Delete provider",
132 134 "allow-user-creation": "Allow user creation",
133 135 "always-fullscreen": "Always fullscreen",
134 136 "authorization-uri": "Authorization URI",
... ... @@ -141,10 +143,10 @@
141 143 "custom-setting": "Custom settings",
142 144 "customer-name-pattern": "Customer name pattern",
143 145 "default-dashboard-name": "Default dashboard name",
144   - "delete-domain-text": "Be careful, after the confirmation a domain and all registration data will be unavailable.",
145   - "delete-domain-title": "Are you sure you want to delete the domain '{{domainName}}'?",
146   - "delete-registration-text": "Be careful, after the confirmation a registration data will be unavailable.",
147   - "delete-registration-title": "Are you sure you want to delete the registration '{{name}}'?",
  146 + "delete-domain-text": "Be careful, after the confirmation a domain and all provider data will be unavailable.",
  147 + "delete-domain-title": "Are you sure you want to delete settings the domain '{{domainName}}'?",
  148 + "delete-registration-text": "Be careful, after the confirmation a provider data will be unavailable.",
  149 + "delete-registration-title": "Are you sure you want to delete the provider '{{name}}'?",
148 150 "email-attribute-key": "Email attribute key",
149 151 "email-attribute-key-required": "Email attribute key is required.",
150 152 "first-name-attribute-key": "First name attribute key",
... ... @@ -160,11 +162,12 @@
160 162 "new-domain": "New domain",
161 163 "oauth2": "OAuth2",
162 164 "redirect-uri-template": "Redirect URI template",
163   - "redirect-uri-template-required": "Redirect URI template is required.",
  165 + "copy-redirect-uri": "Copy redirect URI",
164 166 "registration-id": "Registration ID",
165 167 "registration-id-required": "Registration ID is required.",
166 168 "registration-id-unique": "Registration ID need to unique for the system.",
167 169 "scope": "Scope",
  170 + "scope-required": "Scope is required.",
168 171 "tenant-name-pattern": "Tenant name pattern",
169 172 "tenant-name-pattern-required": "Tenant name pattern is required.",
170 173 "tenant-name-strategy": "Tenant name strategy",
... ... @@ -176,7 +179,12 @@
176 179 "user-info-uri": "User info URI",
177 180 "user-info-uri-required": "User info URI is required.",
178 181 "user-name-attribute-name": "User name attribute key",
179   - "user-name-attribute-name-required": "User name attribute key is required"
  182 + "user-name-attribute-name-required": "User name attribute key is required",
  183 + "protocol": "Protocol",
  184 + "domain-schema-http": "HTTP",
  185 + "domain-schema-https": "HTTPS",
  186 + "domain-schema-mixed": "HTTP+HTTPS",
  187 + "enable": "Enable OAuth2 settings"
180 188 }
181 189 },
182 190 "alarm": {
... ...