From 556cb5aa5df8ddd7eafe39e5a9336d63d5d88226 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Mon, 21 Sep 2020 16:10:00 +0300 Subject: [PATCH] Refactoring OAuth2 --- ui-ngx/src/app/core/auth/auth.models.ts | 2 - ui-ngx/src/app/core/auth/auth.reducer.ts | 1 - ui-ngx/src/app/core/auth/auth.service.ts | 14 +---- .../guards/redirect-guard-settings.service.ts | 51 ------------------- ui-ngx/src/app/core/services/menu.service.ts | 24 +-------- .../home/pages/admin/admin-routing.module.ts | 5 +- .../pages/admin/oauth2-settings.component.ts | 36 +++++++------ .../home/pages/tenant/tenant.component.html | 3 -- .../home/pages/tenant/tenant.component.ts | 14 ++--- .../assets/locale/locale.constant-en_US.json | 4 +- 10 files changed, 26 insertions(+), 128 deletions(-) delete mode 100644 ui-ngx/src/app/core/guards/redirect-guard-settings.service.ts diff --git a/ui-ngx/src/app/core/auth/auth.models.ts b/ui-ngx/src/app/core/auth/auth.models.ts index 573b692a29..c7210da6ed 100644 --- a/ui-ngx/src/app/core/auth/auth.models.ts +++ b/ui-ngx/src/app/core/auth/auth.models.ts @@ -22,7 +22,6 @@ export interface AuthPayload { userTokenAccessEnabled: boolean; allowedDashboardIds: string[]; forceFullscreen: boolean; - allowOAuth2Configuration: boolean; } export interface AuthState { @@ -34,5 +33,4 @@ export interface AuthState { allowedDashboardIds: string[]; forceFullscreen: boolean; lastPublicDashboardId: string; - allowOAuth2Configuration: boolean; } diff --git a/ui-ngx/src/app/core/auth/auth.reducer.ts b/ui-ngx/src/app/core/auth/auth.reducer.ts index 0e7ca095af..6e02d03191 100644 --- a/ui-ngx/src/app/core/auth/auth.reducer.ts +++ b/ui-ngx/src/app/core/auth/auth.reducer.ts @@ -22,7 +22,6 @@ const emptyUserAuthState: AuthPayload = { userDetails: null, userTokenAccessEnabled: false, forceFullscreen: false, - allowOAuth2Configuration: false, allowedDashboardIds: [] }; diff --git a/ui-ngx/src/app/core/auth/auth.service.ts b/ui-ngx/src/app/core/auth/auth.service.ts index f0b9c09a1e..32733ac139 100644 --- a/ui-ngx/src/app/core/auth/auth.service.ts +++ b/ui-ngx/src/app/core/auth/auth.service.ts @@ -18,7 +18,7 @@ import { Injectable, NgZone } from '@angular/core'; import { JwtHelperService } from '@auth0/angular-jwt'; import { HttpClient } from '@angular/common/http'; -import { forkJoin, Observable, of, throwError, ReplaySubject } from 'rxjs'; +import { forkJoin, Observable, of, ReplaySubject, throwError } from 'rxjs'; import { catchError, map, mergeMap, tap } from 'rxjs/operators'; import { LoginRequest, LoginResponse, OAuth2Client, PublicLoginRequest } from '@shared/models/login.models'; @@ -425,25 +425,15 @@ export class AuthService { } } - private loadIsOAuth2ConfigurationAllow(authUser: AuthUser): Observable { - if (authUser.authority === Authority.TENANT_ADMIN) { - return this.http.get('/api/oauth2/config/isAllowed', defaultHttpOptions()); - } else { - return of(false); - } - } - private loadSystemParams(authPayload: AuthPayload): Observable { const sources: Array> = [this.loadIsUserTokenAccessEnabled(authPayload.authUser), this.fetchAllowedDashboardIds(authPayload), - this.loadIsOAuth2ConfigurationAllow(authPayload.authUser), this.timeService.loadMaxDatapointsLimit()]; return forkJoin(sources) .pipe(map((data) => { const userTokenAccessEnabled: boolean = data[0]; const allowedDashboardIds: string[] = data[1]; - const allowOAuth2Configuration: boolean = data[2]; - return {userTokenAccessEnabled, allowedDashboardIds, allowOAuth2Configuration}; + return {userTokenAccessEnabled, allowedDashboardIds}; })); } diff --git a/ui-ngx/src/app/core/guards/redirect-guard-settings.service.ts b/ui-ngx/src/app/core/guards/redirect-guard-settings.service.ts deleted file mode 100644 index 4c69f21aae..0000000000 --- a/ui-ngx/src/app/core/guards/redirect-guard-settings.service.ts +++ /dev/null @@ -1,51 +0,0 @@ -/// -/// Copyright © 2016-2020 The Thingsboard Authors -/// -/// Licensed under the Apache License, Version 2.0 (the "License"); -/// you may not use this file except in compliance with the License. -/// You may obtain a copy of the License at -/// -/// http://www.apache.org/licenses/LICENSE-2.0 -/// -/// Unless required by applicable law or agreed to in writing, software -/// distributed under the License is distributed on an "AS IS" BASIS, -/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -/// See the License for the specific language governing permissions and -/// limitations under the License. -/// - -import { Injectable } from '@angular/core'; -import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router'; -import { AuthState } from '@core/auth/auth.models'; -import { select, Store } from '@ngrx/store'; -import { selectAuth } from '@core/auth/auth.selectors'; -import { take } from 'rxjs/operators'; -import { AppState } from '@core/core.state'; -import { Authority } from '@shared/models/authority.enum'; - -@Injectable({ - providedIn: 'root' -}) -export class RedirectGuardSettings implements CanActivate { - constructor(private store: Store, - private router: Router) { } - - canActivate( - next: ActivatedRouteSnapshot, - state: RouterStateSnapshot) { - let auth: AuthState = null; - this.store.pipe(select(selectAuth), take(1)).subscribe( - (authState: AuthState) => { - auth = authState; - } - ); - - if (auth?.userDetails?.authority === Authority.TENANT_ADMIN) { - this.router.navigateByUrl('/settings/oauth2'); - return false; - } - this.router.navigateByUrl('/settings/general'); - return false; - } - -} diff --git a/ui-ngx/src/app/core/services/menu.service.ts b/ui-ngx/src/app/core/services/menu.service.ts index 7d3503a613..14f3a632db 100644 --- a/ui-ngx/src/app/core/services/menu.service.ts +++ b/ui-ngx/src/app/core/services/menu.service.ts @@ -18,13 +18,12 @@ import { Injectable } from '@angular/core'; import { AuthService } from '../auth/auth.service'; import { select, Store } from '@ngrx/store'; import { AppState } from '../core.state'; -import { getCurrentAuthState, selectAuthUser, selectIsAuthenticated } from '../auth/auth.selectors'; +import { selectAuthUser, selectIsAuthenticated } from '../auth/auth.selectors'; import { take } from 'rxjs/operators'; import { HomeSection, MenuSection } from '@core/services/menu.models'; import { BehaviorSubject, Observable, Subject } from 'rxjs'; import { Authority } from '@shared/models/authority.enum'; import { AuthUser } from '@shared/models/user.model'; -import { AuthState } from '@core/auth/auth.models'; @Injectable({ providedIn: 'root' @@ -44,8 +43,6 @@ export class MenuService { ); } - authState: AuthState = getCurrentAuthState(this.store); - private buildMenu() { this.store.pipe(select(selectAuthUser), take(1)).subscribe( (authUser: AuthUser) => { @@ -236,25 +233,6 @@ export class MenuService { icon: 'track_changes' } ); - - if (this.authState.allowOAuth2Configuration) { - sections.push({ - name: 'admin.settings', - type: 'toggle', - path: '/settings', - height: '40px', - icon: 'settings', - pages: [ - { - name: 'admin.oauth2.oauth2', - type: 'link', - path: '/settings/oauth2', - icon: 'security' - } - ] - }); - } - return sections; } diff --git a/ui-ngx/src/app/modules/home/pages/admin/admin-routing.module.ts b/ui-ngx/src/app/modules/home/pages/admin/admin-routing.module.ts index c4a38fdf30..40a8d3addd 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/admin-routing.module.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/admin-routing.module.ts @@ -23,7 +23,6 @@ import { Authority } from '@shared/models/authority.enum'; import { GeneralSettingsComponent } from '@modules/home/pages/admin/general-settings.component'; import { SecuritySettingsComponent } from '@modules/home/pages/admin/security-settings.component'; import { OAuth2SettingsComponent } from '@home/pages/admin/oauth2-settings.component'; -import { RedirectGuardSettings } from '../../../../core/guards/redirect-guard-settings.service'; const routes: Routes = [ { @@ -38,7 +37,7 @@ const routes: Routes = [ children: [ { path: '', - canActivate: [RedirectGuardSettings], + redirectTo: 'general', pathMatch: 'full' }, { @@ -85,7 +84,7 @@ const routes: Routes = [ component: OAuth2SettingsComponent, canDeactivate: [ConfirmOnExitGuard], data: { - auth: [Authority.SYS_ADMIN, Authority.TENANT_ADMIN], + auth: [Authority.SYS_ADMIN], title: 'admin.oauth2.oauth2', breadcrumb: { label: 'admin.oauth2.oauth2', diff --git a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts index 1f627fd2de..7a0e130758 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts @@ -60,7 +60,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha additionalInfo: { providerName: 'Custom' }, - clientAuthenticationMethod: 'Post', + clientAuthenticationMethod: 'POST', userNameAttributeName: 'email', mapperConfig: { allowUserCreation: true, @@ -249,17 +249,17 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha loginButtonIcon: [registrationData?.loginButtonIcon ? registrationData.loginButtonIcon : null], clientId: [registrationData?.clientId ? registrationData.clientId : '', Validators.required], clientSecret: [registrationData?.clientSecret ? registrationData.clientSecret : '', Validators.required], - accessTokenUri: [registrationData?.accessTokenUri ? registrationData.accessTokenUri : '', [ - Validators.required, - Validators.pattern(this.URL_REGEXP)]], - authorizationUri: [registrationData?.authorizationUri ? registrationData.authorizationUri : '', [ - Validators.required, - Validators.pattern(this.URL_REGEXP)]], + accessTokenUri: [registrationData?.accessTokenUri ? registrationData.accessTokenUri : '', + [Validators.required, + Validators.pattern(this.URL_REGEXP)]], + authorizationUri: [registrationData?.authorizationUri ? registrationData.authorizationUri : '', + [Validators.required, + Validators.pattern(this.URL_REGEXP)]], scope: this.fb.array(registrationData?.scope ? registrationData.scope : []), jwkSetUri: [registrationData?.jwkSetUri ? registrationData.jwkSetUri : '', Validators.pattern(this.URL_REGEXP)], - userInfoUri: [registrationData?.userInfoUri ? registrationData.userInfoUri : '', [ - Validators.required, - Validators.pattern(this.URL_REGEXP)]], + userInfoUri: [registrationData?.userInfoUri ? registrationData.userInfoUri : '', + [Validators.required, + Validators.pattern(this.URL_REGEXP)]], clientAuthenticationMethod: [ registrationData?.clientAuthenticationMethod ? registrationData.clientAuthenticationMethod : 'POST', Validators.required], userNameAttributeName: [ @@ -285,7 +285,8 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha this.subscriptions.push(clientRegistration.get('additionalInfo.providerName').valueChanges.subscribe((provider) => { (clientRegistration.get('scope') as FormArray).clear(); if (provider === 'Custom') { - clientRegistration.reset(this.defaultProvider, {emitEvent: false}); + const defaultSettings = {...this.defaultProvider, ...{id: clientRegistration.get('id').value}}; + clientRegistration.reset(defaultSettings, {emitEvent: false}); clientRegistration.get('accessTokenUri').enable(); clientRegistration.get('authorizationUri').enable(); clientRegistration.get('jwkSetUri').enable(); @@ -294,6 +295,8 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha const template = this.templates.get(provider); delete template.id; delete template.additionalInfo; + template.clientId = ''; + template.clientSecret = ''; template.scope.forEach(() => { (clientRegistration.get('scope') as FormArray).push(this.fb.control('')); }); @@ -301,13 +304,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha clientRegistration.get('authorizationUri').disable(); clientRegistration.get('jwkSetUri').disable(); clientRegistration.get('userInfoUri').disable(); - clientRegistration.patchValue({ - ...template, ...{ - clientId: '', - clientSecret: '', - id: {id: null, entityType: null} - } - }, {emitEvent: false}); + clientRegistration.patchValue(template, {emitEvent: false}); } })); @@ -469,7 +466,8 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha componentRef.onDestroy(() => { if (componentRef.instance.result !== null) { const attributeValue = componentRef.instance.result; - this.clientDomains.at(index).get('redirectUriTemplate').patchValue(attributeValue, {emitEvent: true}); + this.clientDomains.at(index).get('redirectUriTemplate').patchValue(attributeValue); + this.clientDomains.at(index).get('redirectUriTemplate').markAsDirty(); } }); } diff --git a/ui-ngx/src/app/modules/home/pages/tenant/tenant.component.html b/ui-ngx/src/app/modules/home/pages/tenant/tenant.component.html index f13809ffa6..335a7d3c9a 100644 --- a/ui-ngx/src/app/modules/home/pages/tenant/tenant.component.html +++ b/ui-ngx/src/app/modules/home/pages/tenant/tenant.component.html @@ -54,9 +54,6 @@ tenant.description - - {{ 'tenant.allow-oauth2-configuration' | translate }} -
diff --git a/ui-ngx/src/app/modules/home/pages/tenant/tenant.component.ts b/ui-ngx/src/app/modules/home/pages/tenant/tenant.component.ts index aa40d792b0..cbf55857da 100644 --- a/ui-ngx/src/app/modules/home/pages/tenant/tenant.component.ts +++ b/ui-ngx/src/app/modules/home/pages/tenant/tenant.component.ts @@ -23,7 +23,6 @@ import { ActionNotificationShow } from '@app/core/notification/notification.acti import { TranslateService } from '@ngx-translate/core'; import { ContactBasedComponent } from '../../components/entity/contact-based.component'; import { EntityTableConfig } from '@home/models/entity/entities-table-config.models'; -import { isDefined } from '@core/utils'; @Component({ selector: 'tb-tenant', @@ -54,11 +53,8 @@ export class TenantComponent extends ContactBasedComponent { title: [entity ? entity.title : '', [Validators.required]], isolatedTbCore: [entity ? entity.isolatedTbCore : false, []], isolatedTbRuleEngine: [entity ? entity.isolatedTbRuleEngine : false, []], - additionalInfo: this.fb.group( - { - description: [entity && entity.additionalInfo ? entity.additionalInfo.description : ''], - allowOAuth2Configuration: [isDefined(entity?.additionalInfo?.allowOAuth2Configuration) ? - entity.additionalInfo.allowOAuth2Configuration : false] + additionalInfo: this.fb.group({ + description: [entity && entity.additionalInfo ? entity.additionalInfo.description : ''] } ) } @@ -69,11 +65,7 @@ export class TenantComponent extends ContactBasedComponent { this.entityForm.patchValue({title: entity.title}); this.entityForm.patchValue({isolatedTbCore: entity.isolatedTbCore}); this.entityForm.patchValue({isolatedTbRuleEngine: entity.isolatedTbRuleEngine}); - this.entityForm.patchValue({additionalInfo: { - description: entity.additionalInfo ? entity.additionalInfo.description : '', - allowOAuth2Configuration: isDefined(entity?.additionalInfo?.allowOAuth2Configuration) ? - entity.additionalInfo.allowOAuth2Configuration : false - }}); + this.entityForm.patchValue({additionalInfo: {description: entity.additionalInfo ? entity.additionalInfo.description : ''}}); } updateFormState() { diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 50b74e393a..78086a7972 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -126,7 +126,6 @@ "add-domain": "Add domain", "new-domain": "New domain", "add-provider": "Add provider", - "settings": "Settings", "oauth2": { "oauth2": "OAuth2", "registration-id": "Registration ID", @@ -1715,8 +1714,7 @@ "isolated-tb-core": "Processing in isolated ThingsBoard Core container", "isolated-tb-rule-engine": "Processing in isolated ThingsBoard Rule Engine container", "isolated-tb-core-details": "Requires separate microservice(s) per isolated Tenant", - "isolated-tb-rule-engine-details": "Requires separate microservice(s) per isolated Tenant", - "allow-oauth2-configuration": "Allow OAuth2 configuration" + "isolated-tb-rule-engine-details": "Requires separate microservice(s) per isolated Tenant" }, "timeinterval": { "seconds-interval": "{ seconds, plural, 1 {1 second} other {# seconds} }",