mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-07-24 17:32:15 +00:00
[ACS-5279] enhanced oath2 configuration handling (#8575)
* [ACS-5279] enhanced oath2 configuration handling * fix tests * fix schema
This commit is contained in:
@@ -18,7 +18,6 @@
|
||||
import { AlfrescoApiConfig } from '@alfresco/js-api';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { AppConfigService, AppConfigValues } from '../app-config/app-config.service';
|
||||
import { OauthConfigModel } from '../auth/models/oauth-config.model';
|
||||
import { AlfrescoApiService } from '../services/alfresco-api.service';
|
||||
|
||||
export function createAlfrescoApiInstance(angularAlfrescoApiService: AlfrescoApiLoaderService) {
|
||||
@@ -37,7 +36,7 @@ export class AlfrescoApiLoaderService {
|
||||
}
|
||||
|
||||
private async initAngularAlfrescoApi() {
|
||||
const oauth: OauthConfigModel = Object.assign({}, this.appConfig.get<OauthConfigModel>(AppConfigValues.OAUTHCONFIG, null));
|
||||
const oauth = this.appConfig.oauth2;
|
||||
|
||||
if (oauth) {
|
||||
oauth.redirectUri = window.location.origin + window.location.pathname;
|
||||
|
@@ -22,6 +22,7 @@ import { Observable, Subject } from 'rxjs';
|
||||
import { map, distinctUntilChanged, take } from 'rxjs/operators';
|
||||
import { ExtensionConfig, ExtensionService, mergeObjects } from '@alfresco/adf-extensions';
|
||||
import { OpenidConfiguration } from '../auth/interfaces/openid-configuration.interface';
|
||||
import { OauthConfigModel } from '../auth/models/oauth-config.model';
|
||||
|
||||
/* spellchecker: disable */
|
||||
// eslint-disable-next-line no-shadow
|
||||
@@ -229,6 +230,23 @@ export class AppConfigService {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* OAuth2 configuration
|
||||
*/
|
||||
get oauth2(): OauthConfigModel {
|
||||
const config = this.get(AppConfigValues.OAUTHCONFIG, {});
|
||||
const implicitFlow = config['implicitFlow'] === true || config['implicitFlow'] === 'true';
|
||||
const silentLogin = config['silentLogin'] === true || config['silentLogin'] === 'true';
|
||||
const codeFlow = config['codeFlow'] === true || config['codeFlow'] === 'true';
|
||||
|
||||
return {
|
||||
...(config as OauthConfigModel),
|
||||
implicitFlow,
|
||||
silentLogin,
|
||||
codeFlow
|
||||
};
|
||||
}
|
||||
|
||||
private formatString(str: string, keywords: Map<string, string>): string {
|
||||
let result = str;
|
||||
|
||||
|
@@ -1517,7 +1517,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"silentLogin": {
|
||||
"type": "boolean"
|
||||
"type": ["boolean", "string"]
|
||||
},
|
||||
"authPath": {
|
||||
"type": "string"
|
||||
|
@@ -118,13 +118,7 @@ export abstract class AuthGuardBase implements CanActivate, CanActivateChild {
|
||||
}
|
||||
|
||||
protected getOauthConfig(): OauthConfigModel {
|
||||
return (
|
||||
this.appConfigService &&
|
||||
this.appConfigService.get<OauthConfigModel>(
|
||||
AppConfigValues.OAUTHCONFIG,
|
||||
null
|
||||
)
|
||||
);
|
||||
return this.appConfigService.oauth2;
|
||||
}
|
||||
|
||||
protected getLoginRoute(): string {
|
||||
@@ -148,21 +142,12 @@ export abstract class AuthGuardBase implements CanActivate, CanActivateChild {
|
||||
}
|
||||
|
||||
protected isOAuthWithoutSilentLogin(): boolean {
|
||||
const oauth = this.appConfigService.get<OauthConfigModel>(
|
||||
AppConfigValues.OAUTHCONFIG,
|
||||
null
|
||||
);
|
||||
return (
|
||||
this.authenticationService.isOauth() && !!oauth && !oauth.silentLogin
|
||||
);
|
||||
const oauth = this.appConfigService.oauth2;
|
||||
return this.authenticationService.isOauth() && !!oauth && !oauth.silentLogin;
|
||||
}
|
||||
|
||||
protected isSilentLogin(): boolean {
|
||||
const oauth = this.appConfigService.get<OauthConfigModel>(
|
||||
AppConfigValues.OAUTHCONFIG,
|
||||
null
|
||||
);
|
||||
|
||||
const oauth = this.appConfigService.oauth2;;
|
||||
return this.authenticationService.isOauth() && oauth && oauth.silentLogin;
|
||||
}
|
||||
|
||||
|
@@ -1,50 +0,0 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* 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 const mockAuthConfigImplicitFlow = {
|
||||
host: 'http://localhost:3000/auth/realms/alfresco',
|
||||
clientId: 'alfresco',
|
||||
scope: 'openid profile email',
|
||||
secret: '',
|
||||
implicitFlow: true,
|
||||
silentLogin: true,
|
||||
redirectSilentIframeUri: 'http://localhost:3000/assets/silent-refresh.html',
|
||||
redirectUri: '/',
|
||||
redirectUriLogout: '#/logout',
|
||||
publicUrls: [
|
||||
'**/preview/s/*',
|
||||
'**/settings',
|
||||
'**/logout'
|
||||
]
|
||||
};
|
||||
|
||||
export const mockAuthConfigCodeFlow = {
|
||||
host: 'http://localhost:3000/auth/realms/alfresco',
|
||||
clientId: 'alfresco',
|
||||
scope: 'openid profile email',
|
||||
secret: '',
|
||||
codeFlow: true,
|
||||
silentLogin: true,
|
||||
redirectSilentIframeUri: 'http://localhost:3000/assets/silent-refresh.html',
|
||||
redirectUri: '/',
|
||||
redirectUriLogout: '#/logout',
|
||||
publicUrls: [
|
||||
'**/preview/s/*',
|
||||
'**/settings',
|
||||
'**/logout'
|
||||
]
|
||||
};
|
@@ -25,6 +25,7 @@ export interface OauthConfigModel {
|
||||
silentLogin?: boolean;
|
||||
secret?: string;
|
||||
redirectUriLogout?: string;
|
||||
redirectSilentIframeUri?: string;
|
||||
refreshTokenTimeout?: number;
|
||||
publicUrls: string[];
|
||||
}
|
||||
|
@@ -20,36 +20,67 @@ import { TestBed } from '@angular/core/testing';
|
||||
import { EMPTY } from 'rxjs';
|
||||
import { AppConfigService } from '../../app-config/app-config.service';
|
||||
import { AUTH_MODULE_CONFIG } from './auth-config';
|
||||
import { mockAuthConfigCodeFlow, mockAuthConfigImplicitFlow } from '../mock/auth-config.service.mock';
|
||||
|
||||
import { AuthConfigService } from './auth-config.service';
|
||||
import { AuthConfig } from 'angular-oauth2-oidc';
|
||||
import { OauthConfigModel } from '../models/oauth-config.model';
|
||||
|
||||
describe('AuthConfigService', () => {
|
||||
let service: AuthConfigService;
|
||||
let appConfigServiceMock;
|
||||
let appConfigService: AppConfigService;
|
||||
|
||||
const mockAuthConfigImplicitFlow: OauthConfigModel = {
|
||||
host: 'http://localhost:3000/auth/realms/alfresco',
|
||||
clientId: 'alfresco',
|
||||
scope: 'openid profile email',
|
||||
secret: '',
|
||||
implicitFlow: true,
|
||||
silentLogin: true,
|
||||
redirectSilentIframeUri: 'http://localhost:3000/assets/silent-refresh.html',
|
||||
redirectUri: '/',
|
||||
redirectUriLogout: '#/logout',
|
||||
publicUrls: [
|
||||
'**/preview/s/*',
|
||||
'**/settings',
|
||||
'**/logout'
|
||||
]
|
||||
};
|
||||
|
||||
const mockAuthConfigCodeFlow: OauthConfigModel = {
|
||||
host: 'http://localhost:3000/auth/realms/alfresco',
|
||||
clientId: 'alfresco',
|
||||
scope: 'openid profile email',
|
||||
secret: '',
|
||||
implicitFlow: false,
|
||||
codeFlow: true,
|
||||
silentLogin: true,
|
||||
redirectSilentIframeUri: 'http://localhost:3000/assets/silent-refresh.html',
|
||||
redirectUri: '/',
|
||||
redirectUriLogout: '#/logout',
|
||||
publicUrls: [
|
||||
'**/preview/s/*',
|
||||
'**/settings',
|
||||
'**/logout'
|
||||
]
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
appConfigServiceMock = jasmine.createSpyObj(['get'], { onLoad: EMPTY });
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
imports: [HttpClientTestingModule],
|
||||
providers: [
|
||||
{ provide: AUTH_MODULE_CONFIG, useValue: { useHash: true } },
|
||||
{ provide: AppConfigService, useValue: appConfigServiceMock }
|
||||
{ provide: AUTH_MODULE_CONFIG, useValue: { useHash: true } }
|
||||
]
|
||||
});
|
||||
service = TestBed.inject(AuthConfigService);
|
||||
spyOn<any>(service, 'getLocationOrigin').and.returnValue('http://localhost:3000');
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
appConfigService = TestBed.inject(AppConfigService);
|
||||
appConfigService.onLoad = EMPTY;
|
||||
});
|
||||
|
||||
describe('load auth config using hash', () => {
|
||||
it('should load configuration if implicit flow is true ', async () => {
|
||||
appConfigServiceMock.get.and.returnValue(mockAuthConfigImplicitFlow);
|
||||
const expectedConfig = {
|
||||
spyOnProperty(appConfigService, 'oauth2').and.returnValue(mockAuthConfigImplicitFlow);
|
||||
const expectedConfig: AuthConfig = {
|
||||
oidc: true,
|
||||
issuer: 'http://localhost:3000/auth/realms/alfresco',
|
||||
redirectUri: 'http://localhost:3000/#/view/authentication-confirmation/?',
|
||||
@@ -64,7 +95,7 @@ describe('AuthConfigService', () => {
|
||||
});
|
||||
|
||||
it('should load configuration if code flow is true ', async () => {
|
||||
appConfigServiceMock.get.and.returnValue(mockAuthConfigCodeFlow);
|
||||
spyOnProperty(appConfigService, 'oauth2').and.returnValue(mockAuthConfigCodeFlow);
|
||||
const expectedConfig = {
|
||||
oidc: true,
|
||||
issuer: 'http://localhost:3000/auth/realms/alfresco',
|
||||
|
@@ -18,8 +18,7 @@
|
||||
import { Inject, Injectable } from '@angular/core';
|
||||
import { AuthConfig } from 'angular-oauth2-oidc';
|
||||
import { take } from 'rxjs/operators';
|
||||
import { AppConfigService, AppConfigValues } from '../../app-config/app-config.service';
|
||||
import { OauthConfigModel } from '../models/oauth-config.model';
|
||||
import { AppConfigService } from '../../app-config/app-config.service';
|
||||
import { AuthModuleConfig, AUTH_MODULE_CONFIG } from './auth-config';
|
||||
|
||||
export function authConfigFactory(authConfigService: AuthConfigService): Promise<AuthConfig> {
|
||||
@@ -45,7 +44,7 @@ export class AuthConfigService {
|
||||
}
|
||||
|
||||
loadAppConfig(): AuthConfig {
|
||||
const oauth2: OauthConfigModel = Object.assign({}, this.appConfigService.get<OauthConfigModel>(AppConfigValues.OAUTHCONFIG, null));
|
||||
const oauth2 = this.appConfigService.oauth2;
|
||||
const origin = this.getLocationOrigin();
|
||||
const redirectUri = this.getRedirectUri();
|
||||
|
||||
@@ -73,7 +72,7 @@ export class AuthConfigService {
|
||||
? `${this.getLocationOrigin()}/#/${viewUrl}`
|
||||
: `${this.getLocationOrigin()}/${viewUrl}`;
|
||||
|
||||
const oauth2: OauthConfigModel = Object.assign({}, this.appConfigService.get<OauthConfigModel>(AppConfigValues.OAUTHCONFIG, null));
|
||||
const oauth2 = this.appConfigService.oauth2;
|
||||
|
||||
// handle issue from the OIDC library with hashStrategy and implicitFlow, with would append &state to the url with would lead to error
|
||||
// `cannot match any routes`, and displaying the wildcard ** error page
|
||||
|
@@ -20,7 +20,6 @@ import { OAuthService, OAuthStorage } from 'angular-oauth2-oidc';
|
||||
import { EMPTY, Observable } from 'rxjs';
|
||||
import { catchError, map } from 'rxjs/operators';
|
||||
import { AppConfigService, AppConfigValues } from '../../app-config/app-config.service';
|
||||
import { OauthConfigModel } from '../models/oauth-config.model';
|
||||
import { AlfrescoApiService } from '../../services/alfresco-api.service';
|
||||
import { BaseAuthenticationService } from '../../services/base-authentication.service';
|
||||
import { CookieService } from '../../common/services/cookie.service';
|
||||
@@ -73,14 +72,12 @@ export class OIDCAuthenticationService extends BaseAuthenticationService {
|
||||
return this.appConfig.get(AppConfigValues.AUTHTYPE) === 'OAUTH';
|
||||
}
|
||||
|
||||
isImplicitFlow() {
|
||||
const oauth2: OauthConfigModel = Object.assign({}, this.appConfig.get<OauthConfigModel>(AppConfigValues.OAUTHCONFIG, null));
|
||||
return !!oauth2?.implicitFlow;
|
||||
isImplicitFlow(): boolean {
|
||||
return !!this.appConfig.oauth2?.implicitFlow;
|
||||
}
|
||||
|
||||
isAuthCodeFlow() {
|
||||
const oauth2: OauthConfigModel = Object.assign({}, this.appConfig.get<OauthConfigModel>(AppConfigValues.OAUTHCONFIG, null));
|
||||
return !!oauth2?.codeFlow;
|
||||
isAuthCodeFlow(): boolean {
|
||||
return !!this.appConfig.oauth2?.codeFlow;
|
||||
}
|
||||
|
||||
login(username: string, password: string, rememberMe: boolean = false): Observable<{ type: string; ticket: any }> {
|
||||
@@ -121,7 +118,7 @@ export class OIDCAuthenticationService extends BaseAuthenticationService {
|
||||
reset(): void {
|
||||
const config = this.authConfig.loadAppConfig();
|
||||
this.auth.updateIDPConfiguration(config);
|
||||
const oauth2: OauthConfigModel = Object.assign({}, this.appConfig.get<OauthConfigModel>(AppConfigValues.OAUTHCONFIG, null));
|
||||
const oauth2 = this.appConfig.oauth2;
|
||||
|
||||
if (config.oidc && oauth2.silentLogin) {
|
||||
this.auth.login();
|
||||
|
@@ -24,7 +24,6 @@ import { AppConfigService, AppConfigValues } from '../../app-config/app-config.s
|
||||
import { map, catchError, tap } from 'rxjs/operators';
|
||||
import { JwtHelperService } from './jwt-helper.service';
|
||||
import { StorageService } from '../../common/services/storage.service';
|
||||
import { OauthConfigModel } from '../models/oauth-config.model';
|
||||
import { BaseAuthenticationService } from '../../services/base-authentication.service';
|
||||
|
||||
@Injectable({
|
||||
@@ -162,8 +161,7 @@ export class AuthenticationService extends BaseAuthenticationService {
|
||||
}
|
||||
|
||||
isImplicitFlow(): boolean {
|
||||
const oauth2: OauthConfigModel = Object.assign({}, this.appConfig.get<OauthConfigModel>(AppConfigValues.OAUTHCONFIG, null));
|
||||
return !!oauth2?.implicitFlow;
|
||||
return !!this.appConfig.oauth2?.implicitFlow;
|
||||
}
|
||||
|
||||
isAuthCodeFlow(): boolean {
|
||||
|
@@ -22,7 +22,6 @@ import {
|
||||
import { AbstractControl, UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
|
||||
import { Router, ActivatedRoute, Params } from '@angular/router';
|
||||
import { AuthenticationService } from '../../auth/services/authentication.service';
|
||||
import { OauthConfigModel } from '../../auth/models/oauth-config.model';
|
||||
import { TranslationService } from '../../translation/translation.service';
|
||||
import { UserPreferencesService } from '../../common/services/user-preferences.service';
|
||||
import { AlfrescoApiService } from '../../services/alfresco-api.service';
|
||||
@@ -154,7 +153,7 @@ export class LoginComponent implements OnInit, OnDestroy {
|
||||
} else {
|
||||
|
||||
if (this.authService.isOauth()) {
|
||||
const oauth: OauthConfigModel = this.appConfig.get<OauthConfigModel>(AppConfigValues.OAUTHCONFIG, null);
|
||||
const oauth = this.appConfig.oauth2;
|
||||
if (oauth && oauth.silentLogin) {
|
||||
this.redirectToImplicitLogin();
|
||||
} else if (oauth && oauth.implicitFlow) {
|
||||
|
@@ -65,7 +65,8 @@ export class AlfrescoApiService {
|
||||
}
|
||||
|
||||
private getAuthWithFixedOriginLocation(): OauthConfigModel {
|
||||
const oauth: OauthConfigModel = Object.assign({}, this.appConfig.get<OauthConfigModel>(AppConfigValues.OAUTHCONFIG, null));
|
||||
const oauth = this.appConfig.oauth2;
|
||||
|
||||
if (oauth) {
|
||||
oauth.redirectUri = window.location.origin + window.location.pathname;
|
||||
oauth.redirectUriLogout = window.location.origin + window.location.pathname;
|
||||
|
Reference in New Issue
Block a user