Commit 556cb5aa5df8ddd7eafe39e5a9336d63d5d88226

Authored by Vladyslav_Prykhodko
1 parent cea56664

Refactoring OAuth2

@@ -22,7 +22,6 @@ export interface AuthPayload { @@ -22,7 +22,6 @@ export interface AuthPayload {
22 userTokenAccessEnabled: boolean; 22 userTokenAccessEnabled: boolean;
23 allowedDashboardIds: string[]; 23 allowedDashboardIds: string[];
24 forceFullscreen: boolean; 24 forceFullscreen: boolean;
25 - allowOAuth2Configuration: boolean;  
26 } 25 }
27 26
28 export interface AuthState { 27 export interface AuthState {
@@ -34,5 +33,4 @@ export interface AuthState { @@ -34,5 +33,4 @@ export interface AuthState {
34 allowedDashboardIds: string[]; 33 allowedDashboardIds: string[];
35 forceFullscreen: boolean; 34 forceFullscreen: boolean;
36 lastPublicDashboardId: string; 35 lastPublicDashboardId: string;
37 - allowOAuth2Configuration: boolean;  
38 } 36 }
@@ -22,7 +22,6 @@ const emptyUserAuthState: AuthPayload = { @@ -22,7 +22,6 @@ const emptyUserAuthState: AuthPayload = {
22 userDetails: null, 22 userDetails: null,
23 userTokenAccessEnabled: false, 23 userTokenAccessEnabled: false,
24 forceFullscreen: false, 24 forceFullscreen: false,
25 - allowOAuth2Configuration: false,  
26 allowedDashboardIds: [] 25 allowedDashboardIds: []
27 }; 26 };
28 27
@@ -18,7 +18,7 @@ import { Injectable, NgZone } from '@angular/core'; @@ -18,7 +18,7 @@ import { Injectable, NgZone } from '@angular/core';
18 import { JwtHelperService } from '@auth0/angular-jwt'; 18 import { JwtHelperService } from '@auth0/angular-jwt';
19 import { HttpClient } from '@angular/common/http'; 19 import { HttpClient } from '@angular/common/http';
20 20
21 -import { forkJoin, Observable, of, throwError, ReplaySubject } from 'rxjs'; 21 +import { forkJoin, Observable, of, ReplaySubject, throwError } from 'rxjs';
22 import { catchError, map, mergeMap, tap } from 'rxjs/operators'; 22 import { catchError, map, mergeMap, tap } from 'rxjs/operators';
23 23
24 import { LoginRequest, LoginResponse, OAuth2Client, PublicLoginRequest } from '@shared/models/login.models'; 24 import { LoginRequest, LoginResponse, OAuth2Client, PublicLoginRequest } from '@shared/models/login.models';
@@ -425,25 +425,15 @@ export class AuthService { @@ -425,25 +425,15 @@ export class AuthService {
425 } 425 }
426 } 426 }
427 427
428 - private loadIsOAuth2ConfigurationAllow(authUser: AuthUser): Observable<boolean> {  
429 - if (authUser.authority === Authority.TENANT_ADMIN) {  
430 - return this.http.get<boolean>('/api/oauth2/config/isAllowed', defaultHttpOptions());  
431 - } else {  
432 - return of(false);  
433 - }  
434 - }  
435 -  
436 private loadSystemParams(authPayload: AuthPayload): Observable<any> { 428 private loadSystemParams(authPayload: AuthPayload): Observable<any> {
437 const sources: Array<Observable<any>> = [this.loadIsUserTokenAccessEnabled(authPayload.authUser), 429 const sources: Array<Observable<any>> = [this.loadIsUserTokenAccessEnabled(authPayload.authUser),
438 this.fetchAllowedDashboardIds(authPayload), 430 this.fetchAllowedDashboardIds(authPayload),
439 - this.loadIsOAuth2ConfigurationAllow(authPayload.authUser),  
440 this.timeService.loadMaxDatapointsLimit()]; 431 this.timeService.loadMaxDatapointsLimit()];
441 return forkJoin(sources) 432 return forkJoin(sources)
442 .pipe(map((data) => { 433 .pipe(map((data) => {
443 const userTokenAccessEnabled: boolean = data[0]; 434 const userTokenAccessEnabled: boolean = data[0];
444 const allowedDashboardIds: string[] = data[1]; 435 const allowedDashboardIds: string[] = data[1];
445 - const allowOAuth2Configuration: boolean = data[2];  
446 - return {userTokenAccessEnabled, allowedDashboardIds, allowOAuth2Configuration}; 436 + return {userTokenAccessEnabled, allowedDashboardIds};
447 })); 437 }));
448 } 438 }
449 439
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 { Injectable } from '@angular/core';  
18 -import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router';  
19 -import { AuthState } from '@core/auth/auth.models';  
20 -import { select, Store } from '@ngrx/store';  
21 -import { selectAuth } from '@core/auth/auth.selectors';  
22 -import { take } from 'rxjs/operators';  
23 -import { AppState } from '@core/core.state';  
24 -import { Authority } from '@shared/models/authority.enum';  
25 -  
26 -@Injectable({  
27 - providedIn: 'root'  
28 -})  
29 -export class RedirectGuardSettings implements CanActivate {  
30 - constructor(private store: Store<AppState>,  
31 - private router: Router) { }  
32 -  
33 - canActivate(  
34 - next: ActivatedRouteSnapshot,  
35 - state: RouterStateSnapshot) {  
36 - let auth: AuthState = null;  
37 - this.store.pipe(select(selectAuth), take(1)).subscribe(  
38 - (authState: AuthState) => {  
39 - auth = authState;  
40 - }  
41 - );  
42 -  
43 - if (auth?.userDetails?.authority === Authority.TENANT_ADMIN) {  
44 - this.router.navigateByUrl('/settings/oauth2');  
45 - return false;  
46 - }  
47 - this.router.navigateByUrl('/settings/general');  
48 - return false;  
49 - }  
50 -  
51 -}  
@@ -18,13 +18,12 @@ import { Injectable } from '@angular/core'; @@ -18,13 +18,12 @@ import { Injectable } from '@angular/core';
18 import { AuthService } from '../auth/auth.service'; 18 import { AuthService } from '../auth/auth.service';
19 import { select, Store } from '@ngrx/store'; 19 import { select, Store } from '@ngrx/store';
20 import { AppState } from '../core.state'; 20 import { AppState } from '../core.state';
21 -import { getCurrentAuthState, selectAuthUser, selectIsAuthenticated } from '../auth/auth.selectors'; 21 +import { selectAuthUser, selectIsAuthenticated } from '../auth/auth.selectors';
22 import { take } from 'rxjs/operators'; 22 import { take } from 'rxjs/operators';
23 import { HomeSection, MenuSection } from '@core/services/menu.models'; 23 import { HomeSection, MenuSection } from '@core/services/menu.models';
24 import { BehaviorSubject, Observable, Subject } from 'rxjs'; 24 import { BehaviorSubject, Observable, Subject } from 'rxjs';
25 import { Authority } from '@shared/models/authority.enum'; 25 import { Authority } from '@shared/models/authority.enum';
26 import { AuthUser } from '@shared/models/user.model'; 26 import { AuthUser } from '@shared/models/user.model';
27 -import { AuthState } from '@core/auth/auth.models';  
28 27
29 @Injectable({ 28 @Injectable({
30 providedIn: 'root' 29 providedIn: 'root'
@@ -44,8 +43,6 @@ export class MenuService { @@ -44,8 +43,6 @@ export class MenuService {
44 ); 43 );
45 } 44 }
46 45
47 - authState: AuthState = getCurrentAuthState(this.store);  
48 -  
49 private buildMenu() { 46 private buildMenu() {
50 this.store.pipe(select(selectAuthUser), take(1)).subscribe( 47 this.store.pipe(select(selectAuthUser), take(1)).subscribe(
51 (authUser: AuthUser) => { 48 (authUser: AuthUser) => {
@@ -236,25 +233,6 @@ export class MenuService { @@ -236,25 +233,6 @@ export class MenuService {
236 icon: 'track_changes' 233 icon: 'track_changes'
237 } 234 }
238 ); 235 );
239 -  
240 - if (this.authState.allowOAuth2Configuration) {  
241 - sections.push({  
242 - name: 'admin.settings',  
243 - type: 'toggle',  
244 - path: '/settings',  
245 - height: '40px',  
246 - icon: 'settings',  
247 - pages: [  
248 - {  
249 - name: 'admin.oauth2.oauth2',  
250 - type: 'link',  
251 - path: '/settings/oauth2',  
252 - icon: 'security'  
253 - }  
254 - ]  
255 - });  
256 - }  
257 -  
258 return sections; 236 return sections;
259 } 237 }
260 238
@@ -23,7 +23,6 @@ import { Authority } from '@shared/models/authority.enum'; @@ -23,7 +23,6 @@ import { Authority } from '@shared/models/authority.enum';
23 import { GeneralSettingsComponent } from '@modules/home/pages/admin/general-settings.component'; 23 import { GeneralSettingsComponent } from '@modules/home/pages/admin/general-settings.component';
24 import { SecuritySettingsComponent } from '@modules/home/pages/admin/security-settings.component'; 24 import { SecuritySettingsComponent } from '@modules/home/pages/admin/security-settings.component';
25 import { OAuth2SettingsComponent } from '@home/pages/admin/oauth2-settings.component'; 25 import { OAuth2SettingsComponent } from '@home/pages/admin/oauth2-settings.component';
26 -import { RedirectGuardSettings } from '../../../../core/guards/redirect-guard-settings.service';  
27 26
28 const routes: Routes = [ 27 const routes: Routes = [
29 { 28 {
@@ -38,7 +37,7 @@ const routes: Routes = [ @@ -38,7 +37,7 @@ const routes: Routes = [
38 children: [ 37 children: [
39 { 38 {
40 path: '', 39 path: '',
41 - canActivate: [RedirectGuardSettings], 40 + redirectTo: 'general',
42 pathMatch: 'full' 41 pathMatch: 'full'
43 }, 42 },
44 { 43 {
@@ -85,7 +84,7 @@ const routes: Routes = [ @@ -85,7 +84,7 @@ const routes: Routes = [
85 component: OAuth2SettingsComponent, 84 component: OAuth2SettingsComponent,
86 canDeactivate: [ConfirmOnExitGuard], 85 canDeactivate: [ConfirmOnExitGuard],
87 data: { 86 data: {
88 - auth: [Authority.SYS_ADMIN, Authority.TENANT_ADMIN], 87 + auth: [Authority.SYS_ADMIN],
89 title: 'admin.oauth2.oauth2', 88 title: 'admin.oauth2.oauth2',
90 breadcrumb: { 89 breadcrumb: {
91 label: 'admin.oauth2.oauth2', 90 label: 'admin.oauth2.oauth2',
@@ -60,7 +60,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha @@ -60,7 +60,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha
60 additionalInfo: { 60 additionalInfo: {
61 providerName: 'Custom' 61 providerName: 'Custom'
62 }, 62 },
63 - clientAuthenticationMethod: 'Post', 63 + clientAuthenticationMethod: 'POST',
64 userNameAttributeName: 'email', 64 userNameAttributeName: 'email',
65 mapperConfig: { 65 mapperConfig: {
66 allowUserCreation: true, 66 allowUserCreation: true,
@@ -249,17 +249,17 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha @@ -249,17 +249,17 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha
249 loginButtonIcon: [registrationData?.loginButtonIcon ? registrationData.loginButtonIcon : null], 249 loginButtonIcon: [registrationData?.loginButtonIcon ? registrationData.loginButtonIcon : null],
250 clientId: [registrationData?.clientId ? registrationData.clientId : '', Validators.required], 250 clientId: [registrationData?.clientId ? registrationData.clientId : '', Validators.required],
251 clientSecret: [registrationData?.clientSecret ? registrationData.clientSecret : '', Validators.required], 251 clientSecret: [registrationData?.clientSecret ? registrationData.clientSecret : '', Validators.required],
252 - accessTokenUri: [registrationData?.accessTokenUri ? registrationData.accessTokenUri : '', [  
253 - Validators.required,  
254 - Validators.pattern(this.URL_REGEXP)]],  
255 - authorizationUri: [registrationData?.authorizationUri ? registrationData.authorizationUri : '', [  
256 - Validators.required,  
257 - Validators.pattern(this.URL_REGEXP)]], 252 + accessTokenUri: [registrationData?.accessTokenUri ? registrationData.accessTokenUri : '',
  253 + [Validators.required,
  254 + Validators.pattern(this.URL_REGEXP)]],
  255 + authorizationUri: [registrationData?.authorizationUri ? registrationData.authorizationUri : '',
  256 + [Validators.required,
  257 + Validators.pattern(this.URL_REGEXP)]],
258 scope: this.fb.array(registrationData?.scope ? registrationData.scope : []), 258 scope: this.fb.array(registrationData?.scope ? registrationData.scope : []),
259 jwkSetUri: [registrationData?.jwkSetUri ? registrationData.jwkSetUri : '', Validators.pattern(this.URL_REGEXP)], 259 jwkSetUri: [registrationData?.jwkSetUri ? registrationData.jwkSetUri : '', Validators.pattern(this.URL_REGEXP)],
260 - userInfoUri: [registrationData?.userInfoUri ? registrationData.userInfoUri : '', [  
261 - Validators.required,  
262 - Validators.pattern(this.URL_REGEXP)]], 260 + userInfoUri: [registrationData?.userInfoUri ? registrationData.userInfoUri : '',
  261 + [Validators.required,
  262 + Validators.pattern(this.URL_REGEXP)]],
263 clientAuthenticationMethod: [ 263 clientAuthenticationMethod: [
264 registrationData?.clientAuthenticationMethod ? registrationData.clientAuthenticationMethod : 'POST', Validators.required], 264 registrationData?.clientAuthenticationMethod ? registrationData.clientAuthenticationMethod : 'POST', Validators.required],
265 userNameAttributeName: [ 265 userNameAttributeName: [
@@ -285,7 +285,8 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha @@ -285,7 +285,8 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha
285 this.subscriptions.push(clientRegistration.get('additionalInfo.providerName').valueChanges.subscribe((provider) => { 285 this.subscriptions.push(clientRegistration.get('additionalInfo.providerName').valueChanges.subscribe((provider) => {
286 (clientRegistration.get('scope') as FormArray).clear(); 286 (clientRegistration.get('scope') as FormArray).clear();
287 if (provider === 'Custom') { 287 if (provider === 'Custom') {
288 - clientRegistration.reset(this.defaultProvider, {emitEvent: false}); 288 + const defaultSettings = {...this.defaultProvider, ...{id: clientRegistration.get('id').value}};
  289 + clientRegistration.reset(defaultSettings, {emitEvent: false});
289 clientRegistration.get('accessTokenUri').enable(); 290 clientRegistration.get('accessTokenUri').enable();
290 clientRegistration.get('authorizationUri').enable(); 291 clientRegistration.get('authorizationUri').enable();
291 clientRegistration.get('jwkSetUri').enable(); 292 clientRegistration.get('jwkSetUri').enable();
@@ -294,6 +295,8 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha @@ -294,6 +295,8 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha
294 const template = this.templates.get(provider); 295 const template = this.templates.get(provider);
295 delete template.id; 296 delete template.id;
296 delete template.additionalInfo; 297 delete template.additionalInfo;
  298 + template.clientId = '';
  299 + template.clientSecret = '';
297 template.scope.forEach(() => { 300 template.scope.forEach(() => {
298 (clientRegistration.get('scope') as FormArray).push(this.fb.control('')); 301 (clientRegistration.get('scope') as FormArray).push(this.fb.control(''));
299 }); 302 });
@@ -301,13 +304,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha @@ -301,13 +304,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha
301 clientRegistration.get('authorizationUri').disable(); 304 clientRegistration.get('authorizationUri').disable();
302 clientRegistration.get('jwkSetUri').disable(); 305 clientRegistration.get('jwkSetUri').disable();
303 clientRegistration.get('userInfoUri').disable(); 306 clientRegistration.get('userInfoUri').disable();
304 - clientRegistration.patchValue({  
305 - ...template, ...{  
306 - clientId: '',  
307 - clientSecret: '',  
308 - id: {id: null, entityType: null}  
309 - }  
310 - }, {emitEvent: false}); 307 + clientRegistration.patchValue(template, {emitEvent: false});
311 } 308 }
312 })); 309 }));
313 310
@@ -469,7 +466,8 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha @@ -469,7 +466,8 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha
469 componentRef.onDestroy(() => { 466 componentRef.onDestroy(() => {
470 if (componentRef.instance.result !== null) { 467 if (componentRef.instance.result !== null) {
471 const attributeValue = componentRef.instance.result; 468 const attributeValue = componentRef.instance.result;
472 - this.clientDomains.at(index).get('redirectUriTemplate').patchValue(attributeValue, {emitEvent: true}); 469 + this.clientDomains.at(index).get('redirectUriTemplate').patchValue(attributeValue);
  470 + this.clientDomains.at(index).get('redirectUriTemplate').markAsDirty();
473 } 471 }
474 }); 472 });
475 } 473 }
@@ -54,9 +54,6 @@ @@ -54,9 +54,6 @@
54 <mat-label translate>tenant.description</mat-label> 54 <mat-label translate>tenant.description</mat-label>
55 <textarea matInput formControlName="description" rows="2"></textarea> 55 <textarea matInput formControlName="description" rows="2"></textarea>
56 </mat-form-field> 56 </mat-form-field>
57 - <mat-checkbox fxFlex formControlName="allowOAuth2Configuration" style="padding-bottom: 16px;">  
58 - {{ 'tenant.allow-oauth2-configuration' | translate }}  
59 - </mat-checkbox>  
60 </div> 57 </div>
61 <tb-contact [parentForm]="entityForm" [isEdit]="isEdit"></tb-contact> 58 <tb-contact [parentForm]="entityForm" [isEdit]="isEdit"></tb-contact>
62 <div fxLayout="column"> 59 <div fxLayout="column">
@@ -23,7 +23,6 @@ import { ActionNotificationShow } from '@app/core/notification/notification.acti @@ -23,7 +23,6 @@ import { ActionNotificationShow } from '@app/core/notification/notification.acti
23 import { TranslateService } from '@ngx-translate/core'; 23 import { TranslateService } from '@ngx-translate/core';
24 import { ContactBasedComponent } from '../../components/entity/contact-based.component'; 24 import { ContactBasedComponent } from '../../components/entity/contact-based.component';
25 import { EntityTableConfig } from '@home/models/entity/entities-table-config.models'; 25 import { EntityTableConfig } from '@home/models/entity/entities-table-config.models';
26 -import { isDefined } from '@core/utils';  
27 26
28 @Component({ 27 @Component({
29 selector: 'tb-tenant', 28 selector: 'tb-tenant',
@@ -54,11 +53,8 @@ export class TenantComponent extends ContactBasedComponent<Tenant> { @@ -54,11 +53,8 @@ export class TenantComponent extends ContactBasedComponent<Tenant> {
54 title: [entity ? entity.title : '', [Validators.required]], 53 title: [entity ? entity.title : '', [Validators.required]],
55 isolatedTbCore: [entity ? entity.isolatedTbCore : false, []], 54 isolatedTbCore: [entity ? entity.isolatedTbCore : false, []],
56 isolatedTbRuleEngine: [entity ? entity.isolatedTbRuleEngine : false, []], 55 isolatedTbRuleEngine: [entity ? entity.isolatedTbRuleEngine : false, []],
57 - additionalInfo: this.fb.group(  
58 - {  
59 - description: [entity && entity.additionalInfo ? entity.additionalInfo.description : ''],  
60 - allowOAuth2Configuration: [isDefined(entity?.additionalInfo?.allowOAuth2Configuration) ?  
61 - entity.additionalInfo.allowOAuth2Configuration : false] 56 + additionalInfo: this.fb.group({
  57 + description: [entity && entity.additionalInfo ? entity.additionalInfo.description : '']
62 } 58 }
63 ) 59 )
64 } 60 }
@@ -69,11 +65,7 @@ export class TenantComponent extends ContactBasedComponent<Tenant> { @@ -69,11 +65,7 @@ export class TenantComponent extends ContactBasedComponent<Tenant> {
69 this.entityForm.patchValue({title: entity.title}); 65 this.entityForm.patchValue({title: entity.title});
70 this.entityForm.patchValue({isolatedTbCore: entity.isolatedTbCore}); 66 this.entityForm.patchValue({isolatedTbCore: entity.isolatedTbCore});
71 this.entityForm.patchValue({isolatedTbRuleEngine: entity.isolatedTbRuleEngine}); 67 this.entityForm.patchValue({isolatedTbRuleEngine: entity.isolatedTbRuleEngine});
72 - this.entityForm.patchValue({additionalInfo: {  
73 - description: entity.additionalInfo ? entity.additionalInfo.description : '',  
74 - allowOAuth2Configuration: isDefined(entity?.additionalInfo?.allowOAuth2Configuration) ?  
75 - entity.additionalInfo.allowOAuth2Configuration : false  
76 - }}); 68 + this.entityForm.patchValue({additionalInfo: {description: entity.additionalInfo ? entity.additionalInfo.description : ''}});
77 } 69 }
78 70
79 updateFormState() { 71 updateFormState() {
@@ -126,7 +126,6 @@ @@ -126,7 +126,6 @@
126 "add-domain": "Add domain", 126 "add-domain": "Add domain",
127 "new-domain": "New domain", 127 "new-domain": "New domain",
128 "add-provider": "Add provider", 128 "add-provider": "Add provider",
129 - "settings": "Settings",  
130 "oauth2": { 129 "oauth2": {
131 "oauth2": "OAuth2", 130 "oauth2": "OAuth2",
132 "registration-id": "Registration ID", 131 "registration-id": "Registration ID",
@@ -1715,8 +1714,7 @@ @@ -1715,8 +1714,7 @@
1715 "isolated-tb-core": "Processing in isolated ThingsBoard Core container", 1714 "isolated-tb-core": "Processing in isolated ThingsBoard Core container",
1716 "isolated-tb-rule-engine": "Processing in isolated ThingsBoard Rule Engine container", 1715 "isolated-tb-rule-engine": "Processing in isolated ThingsBoard Rule Engine container",
1717 "isolated-tb-core-details": "Requires separate microservice(s) per isolated Tenant", 1716 "isolated-tb-core-details": "Requires separate microservice(s) per isolated Tenant",
1718 - "isolated-tb-rule-engine-details": "Requires separate microservice(s) per isolated Tenant",  
1719 - "allow-oauth2-configuration": "Allow OAuth2 configuration" 1717 + "isolated-tb-rule-engine-details": "Requires separate microservice(s) per isolated Tenant"
1720 }, 1718 },
1721 "timeinterval": { 1719 "timeinterval": {
1722 "seconds-interval": "{ seconds, plural, 1 {1 second} other {# seconds} }", 1720 "seconds-interval": "{ seconds, plural, 1 {1 second} other {# seconds} }",