Commit 9c5b353a17a21d3d392afc2e3b43d2849100cf8f
1 parent
fe00a9bd
UI: Added validation unique domain
Showing
3 changed files
with
60 additions
and
56 deletions
@@ -59,30 +59,31 @@ | @@ -59,30 +59,31 @@ | ||
59 | <section *ngFor="let domainInfo of clientDomainInfos(domain).controls; let n = index; trackBy: trackByParams" | 59 | <section *ngFor="let domainInfo of clientDomainInfos(domain).controls; let n = index; trackBy: trackByParams" |
60 | class="domains-list"> | 60 | class="domains-list"> |
61 | <div [formGroupName]="n" fxLayout="row" fxLayoutGap="8px"> | 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 }} | ||
69 | - </mat-option> | ||
70 | - </mat-select> | ||
71 | - </mat-form-field> | ||
72 | - | ||
73 | - <mat-form-field fxFlex class="mat-block"> | ||
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 }} | ||
81 | - </mat-error> | ||
82 | - </mat-form-field> | 62 | + <div fxLayout="row" fxLayout.xs="column" fxLayoutGap="8px"> |
63 | + <div fxLayout="column" fxFlex.sm="60" fxFlex.gt-sm="50"> | ||
64 | + <div fxLayout="row" fxLayout.xs="column" fxLayoutGap="8px"> | ||
65 | + <mat-form-field fxFlex="30" fxFlex.xs class="mat-block"> | ||
66 | + <mat-label translate>admin.oauth2.protocol</mat-label> | ||
67 | + <mat-select formControlName="scheme"> | ||
68 | + <mat-option *ngFor="let protocol of protocols" [value]="protocol"> | ||
69 | + {{ domainSchemaTranslations.get(protocol) | translate | uppercase }} | ||
70 | + </mat-option> | ||
71 | + </mat-select> | ||
72 | + </mat-form-field> | ||
73 | + <mat-form-field fxFlex class="mat-block"> | ||
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-form-field> | ||
80 | + </div> | ||
81 | + <mat-error *ngIf="domainInfo.hasError('unique')"> | ||
82 | + {{ 'admin.domain-name-unique' | translate }} | ||
83 | + </mat-error> | ||
83 | </div> | 84 | </div> |
84 | 85 | ||
85 | - <div fxFlex> | 86 | + <div fxFlex fxLayout="column"> |
86 | <mat-form-field fxFlex class="mat-block"> | 87 | <mat-form-field fxFlex class="mat-block"> |
87 | <mat-label translate>admin.oauth2.redirect-uri-template</mat-label> | 88 | <mat-label translate>admin.oauth2.redirect-uri-template</mat-label> |
88 | <input matInput [value]="redirectURI(domainInfo)" readonly> | 89 | <input matInput [value]="redirectURI(domainInfo)" readonly> |
@@ -94,8 +95,7 @@ | @@ -94,8 +95,7 @@ | ||
94 | </button> | 95 | </button> |
95 | </mat-form-field> | 96 | </mat-form-field> |
96 | 97 | ||
97 | - <mat-form-field fxFlex *ngIf="domainInfo.get('scheme').value === 'MIXED'" | ||
98 | - class="mat-block"> | 98 | + <mat-form-field fxFlex *ngIf="domainInfo.get('scheme').value === 'MIXED'" class="mat-block"> |
99 | <mat-label></mat-label> | 99 | <mat-label></mat-label> |
100 | <input matInput [value]="redirectURIMixed(domainInfo)" readonly> | 100 | <input matInput [value]="redirectURIMixed(domainInfo)" readonly> |
101 | <button mat-icon-button color="primary" matSuffix type="button" | 101 | <button mat-icon-button color="primary" matSuffix type="button" |
@@ -106,7 +106,6 @@ | @@ -106,7 +106,6 @@ | ||
106 | </button> | 106 | </button> |
107 | </mat-form-field> | 107 | </mat-form-field> |
108 | </div> | 108 | </div> |
109 | - | ||
110 | </div> | 109 | </div> |
111 | 110 | ||
112 | <div fxLayout="column" fxLayoutAlign="center start"> | 111 | <div fxLayout="column" fxLayoutAlign="center start"> |
@@ -178,13 +178,14 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha | @@ -178,13 +178,14 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha | ||
178 | } | 178 | } |
179 | } | 179 | } |
180 | 180 | ||
181 | - private uniqueDomainValidator(control: AbstractControl): { [key: string]: boolean } | null { | ||
182 | - if (control.parent?.parent?.value) { | ||
183 | - const domain = control.value; | ||
184 | - const listProtocols = control.parent.parent.value | 181 | + private uniqueDomainValidator(control: FormGroup): { [key: string]: boolean } | null { |
182 | + if (control.parent?.value) { | ||
183 | + const domain = control.value.name; | ||
184 | + const listProtocols = control.parent.getRawValue() | ||
185 | .filter((domainInfo) => domainInfo.name === domain) | 185 | .filter((domainInfo) => domainInfo.name === domain) |
186 | .map((domainInfo) => domainInfo.scheme); | 186 | .map((domainInfo) => domainInfo.scheme); |
187 | - if (listProtocols.length > 1 && listProtocols.indexOf(DomainSchema.MIXED) > -1) { | 187 | + if (listProtocols.length > 1 && listProtocols.indexOf(DomainSchema.MIXED) > -1 || |
188 | + new Set(listProtocols).size !== listProtocols.length) { | ||
188 | return {unique: true}; | 189 | return {unique: true}; |
189 | } | 190 | } |
190 | } | 191 | } |
@@ -228,10 +229,9 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha | @@ -228,10 +229,9 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha | ||
228 | const domain = this.fb.group({ | 229 | const domain = this.fb.group({ |
229 | name: [domainInfo ? domainInfo.name : this.window.location.hostname, [ | 230 | name: [domainInfo ? domainInfo.name : this.window.location.hostname, [ |
230 | Validators.required, | 231 | Validators.required, |
231 | - Validators.pattern('((?![:/]).)*$'), | ||
232 | - this.uniqueDomainValidator]], | 232 | + Validators.pattern('((?![:/]).)*$')]], |
233 | scheme: [domainInfo?.scheme ? domainInfo.scheme : DomainSchema.HTTPS, Validators.required] | 233 | scheme: [domainInfo?.scheme ? domainInfo.scheme : DomainSchema.HTTPS, Validators.required] |
234 | - }); | 234 | + }, {validators: this.uniqueDomainValidator}); |
235 | return domain; | 235 | return domain; |
236 | } | 236 | } |
237 | 237 | ||
@@ -288,6 +288,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha | @@ -288,6 +288,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha | ||
288 | this.changeMapperConfigType(clientRegistration, registrationData.mapperConfig.type, registrationData.mapperConfig); | 288 | this.changeMapperConfigType(clientRegistration, registrationData.mapperConfig.type, registrationData.mapperConfig); |
289 | } else { | 289 | } else { |
290 | this.changeMapperConfigType(clientRegistration, MapperConfigType.BASIC); | 290 | this.changeMapperConfigType(clientRegistration, MapperConfigType.BASIC); |
291 | + this.setProviderDefaultValue(defaultProviderName, clientRegistration); | ||
291 | } | 292 | } |
292 | 293 | ||
293 | this.subscriptions.push(clientRegistration.get('mapperConfig.type').valueChanges.subscribe((value) => { | 294 | this.subscriptions.push(clientRegistration.get('mapperConfig.type').valueChanges.subscribe((value) => { |
@@ -296,33 +297,37 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha | @@ -296,33 +297,37 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha | ||
296 | 297 | ||
297 | this.subscriptions.push(clientRegistration.get('additionalInfo.providerName').valueChanges.subscribe((provider) => { | 298 | this.subscriptions.push(clientRegistration.get('additionalInfo.providerName').valueChanges.subscribe((provider) => { |
298 | (clientRegistration.get('scope') as FormArray).clear(); | 299 | (clientRegistration.get('scope') as FormArray).clear(); |
299 | - if (provider === 'Custom') { | ||
300 | - const defaultSettings = {...this.defaultProvider, ...{id: clientRegistration.get('id').value}}; | ||
301 | - clientRegistration.reset(defaultSettings, {emitEvent: false}); | ||
302 | - clientRegistration.get('accessTokenUri').enable(); | ||
303 | - clientRegistration.get('authorizationUri').enable(); | ||
304 | - clientRegistration.get('jwkSetUri').enable(); | ||
305 | - clientRegistration.get('userInfoUri').enable(); | ||
306 | - } else { | ||
307 | - const template = this.templates.get(provider); | ||
308 | - delete template.id; | ||
309 | - delete template.additionalInfo; | ||
310 | - template.clientId = ''; | ||
311 | - template.clientSecret = ''; | ||
312 | - template.scope.forEach(() => { | ||
313 | - (clientRegistration.get('scope') as FormArray).push(this.fb.control('')); | ||
314 | - }); | ||
315 | - clientRegistration.get('accessTokenUri').disable(); | ||
316 | - clientRegistration.get('authorizationUri').disable(); | ||
317 | - clientRegistration.get('jwkSetUri').disable(); | ||
318 | - clientRegistration.get('userInfoUri').disable(); | ||
319 | - clientRegistration.patchValue(template, {emitEvent: false}); | ||
320 | - } | 300 | + this.setProviderDefaultValue(provider, clientRegistration); |
321 | })); | 301 | })); |
322 | 302 | ||
323 | return clientRegistration; | 303 | return clientRegistration; |
324 | } | 304 | } |
325 | 305 | ||
306 | + private setProviderDefaultValue(provider: string, clientRegistration: FormGroup) { | ||
307 | + if (provider === 'Custom') { | ||
308 | + const defaultSettings = {...this.defaultProvider, ...{id: clientRegistration.get('id').value}}; | ||
309 | + clientRegistration.reset(defaultSettings, {emitEvent: false}); | ||
310 | + clientRegistration.get('accessTokenUri').enable(); | ||
311 | + clientRegistration.get('authorizationUri').enable(); | ||
312 | + clientRegistration.get('jwkSetUri').enable(); | ||
313 | + clientRegistration.get('userInfoUri').enable(); | ||
314 | + } else { | ||
315 | + const template = this.templates.get(provider); | ||
316 | + delete template.id; | ||
317 | + delete template.additionalInfo; | ||
318 | + template.clientId = ''; | ||
319 | + template.clientSecret = ''; | ||
320 | + template.scope.forEach(() => { | ||
321 | + (clientRegistration.get('scope') as FormArray).push(this.fb.control('')); | ||
322 | + }); | ||
323 | + clientRegistration.get('accessTokenUri').disable(); | ||
324 | + clientRegistration.get('authorizationUri').disable(); | ||
325 | + clientRegistration.get('jwkSetUri').disable(); | ||
326 | + clientRegistration.get('userInfoUri').disable(); | ||
327 | + clientRegistration.patchValue(template, {emitEvent: false}); | ||
328 | + } | ||
329 | + } | ||
330 | + | ||
326 | private changeMapperConfigType(control: AbstractControl, type: MapperConfigType, predefinedValue?: MapperConfig) { | 331 | private changeMapperConfigType(control: AbstractControl, type: MapperConfigType, predefinedValue?: MapperConfig) { |
327 | const mapperConfig = control.get('mapperConfig') as FormGroup; | 332 | const mapperConfig = control.get('mapperConfig') as FormGroup; |
328 | if (type === MapperConfigType.BASIC) { | 333 | if (type === MapperConfigType.BASIC) { |
@@ -121,7 +121,7 @@ | @@ -121,7 +121,7 @@ | ||
121 | "minimum-max-failed-login-attempts-range": "Maximum number of failed login attempts can't be negative", | 121 | "minimum-max-failed-login-attempts-range": "Maximum number of failed login attempts can't be negative", |
122 | "user-lockout-notification-email": "In case user account lockout, send notification to email", | 122 | "user-lockout-notification-email": "In case user account lockout, send notification to email", |
123 | "domain-name": "Domain name", | 123 | "domain-name": "Domain name", |
124 | - "domain-name-unique": "Domain name need to unique for the system.", | 124 | + "domain-name-unique": "Domain name and protocol need to unique.", |
125 | "error-verification-url": "A domain name shouldn't contain symbols '/' and ':'. Example: thingsboard.io", | 125 | "error-verification-url": "A domain name shouldn't contain symbols '/' and ':'. Example: thingsboard.io", |
126 | "oauth2": { | 126 | "oauth2": { |
127 | "access-token-uri": "Access token URI", | 127 | "access-token-uri": "Access token URI", |