Commit 9c5b353a17a21d3d392afc2e3b43d2849100cf8f

Authored by Vladyslav Prykhodko
1 parent fe00a9bd

UI: Added validation unique domain

@@ -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",