Commit fe00a9bd45842c425b2ffe05d1ff61360b1dd849
1 parent
cd7a7ce4
UI: Added support multiple domains is OAuth2 settings
Showing
10 changed files
with
614 additions
and
644 deletions
... | ... | @@ -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 | -} |
ui-ngx/src/app/modules/home/pages/admin/edit-redirect-uri-panel.component.ts
deleted
100644 → 0
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": { | ... | ... |