Skip to content

Commit

Permalink
[ACS-5279] enhanced oath2 configuration handling (#8575)
Browse files Browse the repository at this point in the history
* [ACS-5279] enhanced oath2 configuration handling

* fix tests

* fix schema
  • Loading branch information
DenysVuika committed Jun 2, 2023
1 parent ea5c346 commit 654acd5
Show file tree
Hide file tree
Showing 13 changed files with 83 additions and 105 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

import { Component, EventEmitter, Output, ViewEncapsulation, OnInit, Input } from '@angular/core';
import { Validators, UntypedFormGroup, UntypedFormBuilder, UntypedFormControl } from '@angular/forms';
import { AppConfigService, AppConfigValues, StorageService, AlfrescoApiService, OauthConfigModel, AuthenticationService } from '@alfresco/adf-core';
import { AppConfigService, AppConfigValues, StorageService, AlfrescoApiService, AuthenticationService } from '@alfresco/adf-core';
import { ENTER } from '@angular/cdk/keycodes';

export const HOST_REGEX = '^(http|https):\/\/.*[^/]$';
Expand Down Expand Up @@ -137,7 +137,7 @@ export class HostSettingsComponent implements OnInit {
}

private createOAuthFormGroup(): UntypedFormGroup {
const oauth = this.appConfig.get<OauthConfigModel>(AppConfigValues.OAUTHCONFIG, {} as any);
const oauth = this.appConfig.oauth2;

return this.formBuilder.group({
host: [oauth.host, [Validators.required, Validators.pattern(HOST_REGEX)]],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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;
Expand Down
18 changes: 18 additions & 0 deletions lib/core/src/lib/app-config/app-config.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;

Expand Down
2 changes: 1 addition & 1 deletion lib/core/src/lib/app-config/app.config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -1517,7 +1517,7 @@
"type": "string"
},
"silentLogin": {
"type": "boolean"
"type": ["boolean", "string"]
},
"authPath": {
"type": "string"
Expand Down
23 changes: 4 additions & 19 deletions lib/core/src/lib/auth/guard/auth-guard-base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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;
}

Expand Down
50 changes: 0 additions & 50 deletions lib/core/src/lib/auth/mock/auth-config.service.mock.ts

This file was deleted.

1 change: 1 addition & 0 deletions lib/core/src/lib/auth/models/oauth-config.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export interface OauthConfigModel {
silentLogin?: boolean;
secret?: string;
redirectUriLogout?: string;
redirectSilentIframeUri?: string;
refreshTokenTimeout?: number;
publicUrls: string[];
}
57 changes: 44 additions & 13 deletions lib/core/src/lib/auth/oidc/auth-config.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;

beforeEach(() => {
appConfigServiceMock = jasmine.createSpyObj(['get'], { onLoad: EMPTY });
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(() => {
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/?',
Expand All @@ -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',
Expand Down
7 changes: 3 additions & 4 deletions lib/core/src/lib/auth/oidc/auth-config.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand All @@ -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();

Expand Down Expand Up @@ -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
Expand Down
13 changes: 5 additions & 8 deletions lib/core/src/lib/auth/oidc/oidc-authentication.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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 }> {
Expand Down Expand Up @@ -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();
Expand Down
4 changes: 1 addition & 3 deletions lib/core/src/lib/auth/services/authentication.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand Down Expand Up @@ -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 {
Expand Down
3 changes: 1 addition & 2 deletions lib/core/src/lib/login/components/login.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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) {
Expand Down
3 changes: 2 additions & 1 deletion lib/core/src/lib/services/alfresco-api.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit 654acd5

Please sign in to comment.