From cec9297e14237a36d4cc52133f06e10da986903f Mon Sep 17 00:00:00 2001 From: Maurizio Vitale Date: Fri, 13 May 2022 19:03:06 +0100 Subject: [PATCH] [AAE-8639] Discovery OpenId - Load discovery and pass info to jsapi (#7632) * Load discovery and pass info to jsapi * fix the roles empty scenario tests * Make lint happier * Rename the initApi method * Add secret field Co-authored-by: arditdomi --- demo-shell/proxy.conf.js | 2 +- lib/core/app-config/app-config.service.ts | 21 ++++++ lib/core/i18n/en.json | 3 +- lib/core/services/alfresco-api.interface.ts | 20 ++++++ lib/core/services/alfresco-api.service.ts | 64 +++++++++++++++---- .../auth-guard-sso-role.service.spec.ts | 7 ++ .../services/auth-guard-sso-role.service.ts | 10 ++- .../openid-configuration.interface.ts | 26 ++++++++ .../settings/host-settings.component.html | 9 +++ lib/core/settings/host-settings.component.ts | 4 +- 10 files changed, 145 insertions(+), 21 deletions(-) create mode 100644 lib/core/services/alfresco-api.interface.ts create mode 100644 lib/core/services/openid-configuration.interface.ts diff --git a/demo-shell/proxy.conf.js b/demo-shell/proxy.conf.js index 9b904a33f2..216930bd06 100644 --- a/demo-shell/proxy.conf.js +++ b/demo-shell/proxy.conf.js @@ -3,7 +3,7 @@ require('dotenv').config(); const { getDeployedAppsProxy, getShareProxy, getApsProxy } = require('./proxy-helpers'); const legacyHost = process.env.PROXY_HOST_ADF; -const cloudHost = process.env.CLOUD_PROXY_HOST_ADF; +const cloudHost = process.env.CLOUD_PROXY_HOST_ADF || process.env.PROXY_HOST_ADF; const cloudApps = process.env.APP_CONFIG_APPS_DEPLOYED; const apsHost = process.env.PROXY_HOST_ADF; diff --git a/lib/core/app-config/app-config.service.ts b/lib/core/app-config/app-config.service.ts index 11bd711c17..ecd66373cc 100644 --- a/lib/core/app-config/app-config.service.ts +++ b/lib/core/app-config/app-config.service.ts @@ -21,6 +21,7 @@ import { ObjectUtils } from '../utils/object-utils'; import { Observable, Subject } from 'rxjs'; import { map, distinctUntilChanged, take } from 'rxjs/operators'; import { ExtensionConfig, ExtensionService, mergeObjects } from '@alfresco/adf-extensions'; +import { OpenidConfiguration } from '../services/openid-configuration.interface'; /* spellchecker: disable */ // eslint-disable-next-line no-shadow @@ -208,6 +209,26 @@ export class AppConfigService { }); } + /** + * Call the discovery API to fetch configuration + * + * @returns Discovery configuration + */ + loadWellKnown(hostIdp: string): Promise { + return new Promise(async (resolve, reject) => { + this.http + .get(`${hostIdp}/.well-known/openid-configuration`) + .subscribe({ + next: (res: OpenidConfiguration) => { + resolve(res); + }, + error: (err: any) => { + reject(err); + } + }); + }); + } + private formatString(str: string, keywords: Map): string { let result = str; diff --git a/lib/core/i18n/en.json b/lib/core/i18n/en.json index de999306a7..432423680c 100644 --- a/lib/core/i18n/en.json +++ b/lib/core/i18n/en.json @@ -186,7 +186,8 @@ "SILENT": "Silent Login", "SCOPE": "Scope", "CLIENT": "Client ID", - "PUBLIC_URLS": "Public urls silent Login" + "PUBLIC_URLS": "Public urls silent Login", + "SECRET": "Secret" }, "CARDVIEW": { "KEYVALUEPAIRS": { diff --git a/lib/core/services/alfresco-api.interface.ts b/lib/core/services/alfresco-api.interface.ts new file mode 100644 index 0000000000..0bbd42023c --- /dev/null +++ b/lib/core/services/alfresco-api.interface.ts @@ -0,0 +1,20 @@ +/*! + * @license + * Copyright 2019 Alfresco Software, Ltd. + * + * 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. + */ + +export interface AlfrescoApiInterface { + load(): Promise ; +} diff --git a/lib/core/services/alfresco-api.service.ts b/lib/core/services/alfresco-api.service.ts index 27c986cb81..00f39a33f1 100644 --- a/lib/core/services/alfresco-api.service.ts +++ b/lib/core/services/alfresco-api.service.ts @@ -21,6 +21,7 @@ import { AppConfigService, AppConfigValues } from '../app-config/app-config.serv import { Subject, ReplaySubject } from 'rxjs'; import { OauthConfigModel } from '../models/oauth-config.model'; import { StorageService } from './storage.service'; +import { OpenidConfiguration } from './openid-configuration.interface'; @Injectable({ providedIn: 'root' @@ -36,6 +37,9 @@ export class AlfrescoApiService { protected alfrescoApi: AlfrescoApi; lastConfig: AlfrescoApiConfig; + currentAppConfig: AlfrescoApiConfig; + + idpConfig: OpenidConfiguration; private excludedErrorUrl: string[] = ['api/enterprise/system/properties']; @@ -49,25 +53,52 @@ export class AlfrescoApiService { } async load() { - await this.appConfig.load().then(() => { + try { + await this.appConfig.load(); this.storageService.prefix = this.appConfig.get(AppConfigValues.STORAGE_PREFIX, ''); - this.initAlfrescoApi(); - this.alfrescoApiInitialized.next(true); - }); + this.getCurrentAppConfig(); + + if (this.currentAppConfig.authType === 'OAUTH') { + this.idpConfig = await this.appConfig.loadWellKnown(this.currentAppConfig.oauth2.host); + this.mapAlfrescoApiOpenIdConfig(); + } + } catch { + throw new Error('Something wrong happened when calling the app.config.json'); + } + + this.initAlfrescoApiWithConfig(); + this.alfrescoApiInitialized.next(true); } - reset() { - this.initAlfrescoApi(); + async reset() { + this.getCurrentAppConfig(); + if (this.currentAppConfig.authType === 'OAUTH') { + this.idpConfig = await this.appConfig.loadWellKnown(this.currentAppConfig.oauth2.host); + this.mapAlfrescoApiOpenIdConfig(); + } + this.initAlfrescoApiWithConfig(); } - protected initAlfrescoApi() { + private getAuthWithFixedOriginLocation(): OauthConfigModel { const oauth: OauthConfigModel = Object.assign({}, this.appConfig.get(AppConfigValues.OAUTHCONFIG, null)); if (oauth) { oauth.redirectUri = window.location.origin + window.location.pathname; oauth.redirectUriLogout = window.location.origin + window.location.pathname; } + return oauth; + } - const config = new AlfrescoApiConfig({ + private mapAlfrescoApiOpenIdConfig() { + this.currentAppConfig.oauth2.tokenUrl = this.idpConfig.token_endpoint; + this.currentAppConfig.oauth2.authorizationUrl = this.idpConfig.authorization_endpoint; + this.currentAppConfig.oauth2.logoutUrl = this.idpConfig.end_session_endpoint; + this.currentAppConfig.oauth2.userinfoEndpoint = this.idpConfig.userinfo_endpoint; + } + + private getCurrentAppConfig() { + const oauth = this.getAuthWithFixedOriginLocation(); + + this.currentAppConfig = new AlfrescoApiConfig({ provider: this.appConfig.get(AppConfigValues.PROVIDERS), hostEcm: this.appConfig.get(AppConfigValues.ECMHOST), hostBpm: this.appConfig.get(AppConfigValues.BPMHOST), @@ -79,15 +110,20 @@ export class AlfrescoApiService { domainPrefix : this.appConfig.get(AppConfigValues.STORAGE_PREFIX), oauth2: oauth }); + } - if (this.alfrescoApi && this.isDifferentConfig(this.lastConfig, config)) { - this.lastConfig = config; - this.alfrescoApi.setConfig(config); + protected initAlfrescoApi() { + this.getCurrentAppConfig(); + this.initAlfrescoApiWithConfig(); + } + + private initAlfrescoApiWithConfig() { + if (this.alfrescoApi && this.isDifferentConfig(this.lastConfig, this.currentAppConfig)) { + this.alfrescoApi.setConfig(this.currentAppConfig); } else { - this.lastConfig = config; - this.alfrescoApi = new AlfrescoApi(config); + this.alfrescoApi = new AlfrescoApi(this.currentAppConfig); } - + this.lastConfig = this.currentAppConfig; } isDifferentConfig(lastConfig: AlfrescoApiConfig, newConfig: AlfrescoApiConfig) { diff --git a/lib/core/services/auth-guard-sso-role.service.spec.ts b/lib/core/services/auth-guard-sso-role.service.spec.ts index 30771f7bf9..1640ce620f 100644 --- a/lib/core/services/auth-guard-sso-role.service.spec.ts +++ b/lib/core/services/auth-guard-sso-role.service.spec.ts @@ -59,6 +59,13 @@ describe('Auth Guard SSO role service', () => { expect(await authGuard.canActivate(router)).toBeTruthy(); }); + it('Should canActivate be true if case of empty roles to check', async () => { + const router: ActivatedRouteSnapshot = new ActivatedRouteSnapshot(); + router.data = { roles: [] }; + + expect(await authGuard.canActivate(router)).toBeTruthy(); + }); + it('Should canActivate be false if the Role is not present int the JWT token', async () => { spyOn(jwtHelperService, 'getAccessToken').and.returnValue('my-access_token'); spyOn(jwtHelperService, 'decodeToken').and.returnValue({ realm_access: { roles: ['role3'] } }); diff --git a/lib/core/services/auth-guard-sso-role.service.ts b/lib/core/services/auth-guard-sso-role.service.ts index 6c40838dc0..e07b5f1780 100644 --- a/lib/core/services/auth-guard-sso-role.service.ts +++ b/lib/core/services/auth-guard-sso-role.service.ts @@ -38,9 +38,13 @@ export class AuthGuardSsoRoleService implements CanActivate { if (route.data) { if (route.data['roles']) { const rolesToCheck: string[] = route.data['roles']; - const excludedRoles = route.data['excludedRoles'] || []; - const isContentAdmin = rolesToCheck.includes(ContentGroups.ALFRESCO_ADMINISTRATORS) || excludedRoles.includes(ContentGroups.ALFRESCO_ADMINISTRATORS) ? await this.peopleContentService.isContentAdmin() : false; - hasRealmRole = excludedRoles.length ? this.checkAccessWithExcludedRoles(rolesToCheck, excludedRoles, isContentAdmin) : this.hasRoles(rolesToCheck, isContentAdmin); + if (rolesToCheck.length === 0) { + hasRealmRole = true; + } else { + const excludedRoles = route.data['excludedRoles'] || []; + const isContentAdmin = rolesToCheck.includes(ContentGroups.ALFRESCO_ADMINISTRATORS) || excludedRoles.includes(ContentGroups.ALFRESCO_ADMINISTRATORS) ? await this.peopleContentService.isContentAdmin() : false; + hasRealmRole = excludedRoles.length ? this.checkAccessWithExcludedRoles(rolesToCheck, excludedRoles, isContentAdmin) : this.hasRoles(rolesToCheck, isContentAdmin); + } } if (route.data['clientRoles']) { diff --git a/lib/core/services/openid-configuration.interface.ts b/lib/core/services/openid-configuration.interface.ts new file mode 100644 index 0000000000..2d8b6c4089 --- /dev/null +++ b/lib/core/services/openid-configuration.interface.ts @@ -0,0 +1,26 @@ +/*! + * @license + * Copyright 2019 Alfresco Software, Ltd. + * + * 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. + */ + +export interface OpenidConfiguration { + authorization_endpoint: string; + token_endpoint: string; + userinfo_endpoint: string; + end_session_endpoint: string; + check_session_iframe: string; + revocation_endpoint: string; + introspection_endpoint: string; +} diff --git a/lib/core/settings/host-settings.component.html b/lib/core/settings/host-settings.component.html index 169b5522a7..17275eda5b 100644 --- a/lib/core/settings/host-settings.component.html +++ b/lib/core/settings/host-settings.component.html @@ -103,6 +103,15 @@ + + {{ 'CORE.HOST_SETTINGS.SECRET'| translate }} + + + {{ 'CORE.HOST_SETTINGS.REQUIRED'| translate }} + + + diff --git a/lib/core/settings/host-settings.component.ts b/lib/core/settings/host-settings.component.ts index ec0c7139f1..0365d2274f 100644 --- a/lib/core/settings/host-settings.component.ts +++ b/lib/core/settings/host-settings.component.ts @@ -259,8 +259,8 @@ export class HostSettingsComponent implements OnInit { return this.oauthConfig.get('scope') as FormControl; } - get secretId(): FormControl { - return this.oauthConfig.get('secretId') as FormControl; + get secret(): FormControl { + return this.oauthConfig.get('secret') as FormControl; } get implicitFlow(): FormControl {