mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-07-24 17:32:15 +00:00
feat: add auth module
This commit is contained in:
committed by
Amedeo Lepore
parent
492f145665
commit
71e2864303
@@ -0,0 +1,38 @@
|
|||||||
|
/*!
|
||||||
|
* @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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { AlfrescoApiHttpClient } from '@alfresco/adf-core/api';
|
||||||
|
import { StorageService } from '../services/storage.service';
|
||||||
|
import { AlfrescoApi, AlfrescoApiConfig } from '@alfresco/js-api';
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { AppConfigService } from '../app-config';
|
||||||
|
import { AlfrescoApiService } from '../services/alfresco-api.service';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class AlfrescoApiServiceWithAngularBasedHttpClient extends AlfrescoApiService {
|
||||||
|
constructor(
|
||||||
|
storage: StorageService,
|
||||||
|
appConfig: AppConfigService,
|
||||||
|
private readonly alfrescoApiHttpClient: AlfrescoApiHttpClient
|
||||||
|
) {
|
||||||
|
super(appConfig, storage);
|
||||||
|
}
|
||||||
|
|
||||||
|
override createInstance(config: AlfrescoApiConfig) {
|
||||||
|
return new AlfrescoApi(config, this.alfrescoApiHttpClient);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,62 @@
|
|||||||
|
/*!
|
||||||
|
* @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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { AlfrescoApiConfig } from '@alfresco/js-api';
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { AppConfigService, AppConfigValues } from '../app-config/app-config.service';
|
||||||
|
import { OauthConfigModel } from '../models/oauth-config.model';
|
||||||
|
import { AlfrescoApiService } from '../services/alfresco-api.service';
|
||||||
|
|
||||||
|
export function createAlfrescoApiInstance(angularAlfrescoApiService: AlfrescoApiLoaderService) {
|
||||||
|
return () => angularAlfrescoApiService.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class AlfrescoApiLoaderService {
|
||||||
|
constructor(private readonly appConfig: AppConfigService, private readonly apiService: AlfrescoApiService) {}
|
||||||
|
|
||||||
|
async init(): Promise<any> {
|
||||||
|
await this.appConfig.load();
|
||||||
|
return this.initAngularAlfrescoApi();
|
||||||
|
}
|
||||||
|
|
||||||
|
private initAngularAlfrescoApi() {
|
||||||
|
const oauth: OauthConfigModel = Object.assign({}, this.appConfig.get<OauthConfigModel>(AppConfigValues.OAUTHCONFIG, null));
|
||||||
|
|
||||||
|
if (oauth) {
|
||||||
|
oauth.redirectUri = window.location.origin + window.location.pathname;
|
||||||
|
oauth.redirectUriLogout = window.location.origin + window.location.pathname;
|
||||||
|
}
|
||||||
|
|
||||||
|
const config = new AlfrescoApiConfig({
|
||||||
|
provider: this.appConfig.get<string>(AppConfigValues.PROVIDERS),
|
||||||
|
hostEcm: this.appConfig.get<string>(AppConfigValues.ECMHOST),
|
||||||
|
hostBpm: this.appConfig.get<string>(AppConfigValues.BPMHOST),
|
||||||
|
authType: this.appConfig.get<string>(AppConfigValues.AUTHTYPE, 'BASIC'),
|
||||||
|
contextRootBpm: this.appConfig.get<string>(AppConfigValues.CONTEXTROOTBPM),
|
||||||
|
contextRoot: this.appConfig.get<string>(AppConfigValues.CONTEXTROOTECM),
|
||||||
|
disableCsrf: this.appConfig.get<boolean>(AppConfigValues.DISABLECSRF),
|
||||||
|
withCredentials: this.appConfig.get<boolean>(AppConfigValues.AUTH_WITH_CREDENTIALS, false),
|
||||||
|
domainPrefix: this.appConfig.get<string>(AppConfigValues.STORAGE_PREFIX),
|
||||||
|
oauth2: oauth
|
||||||
|
});
|
||||||
|
|
||||||
|
this.apiService.load(config);
|
||||||
|
}
|
||||||
|
}
|
25
lib/core/src/lib/app-config/app-config.loader.ts
Normal file
25
lib/core/src/lib/app-config/app-config.loader.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
/*!
|
||||||
|
* @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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { AppConfigService, AppConfigValues } from './app-config.service';
|
||||||
|
import { StorageService } from '../services/storage.service';
|
||||||
|
|
||||||
|
export function loadAppConfig(appConfigService: AppConfigService, storageService: StorageService) {
|
||||||
|
return () => appConfigService.load().then(() => {
|
||||||
|
storageService.prefix = appConfigService.get<string>(AppConfigValues.STORAGE_PREFIX, '');
|
||||||
|
});
|
||||||
|
}
|
@@ -192,8 +192,9 @@ export class AppConfigService {
|
|||||||
this.http.get(configUrl).subscribe(
|
this.http.get(configUrl).subscribe(
|
||||||
(data: any) => {
|
(data: any) => {
|
||||||
this.status = Status.LOADED;
|
this.status = Status.LOADED;
|
||||||
this.onDataLoaded(data);
|
|
||||||
resolve(this.config);
|
resolve(this.config);
|
||||||
|
// WARNING: Risky change! Despite the fact that this would be the right order, this is a breaking change...
|
||||||
|
this.onDataLoaded(data);
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
resolve(this.config);
|
resolve(this.config);
|
||||||
|
40
lib/core/src/lib/auth/auth-config.service.spec.ts
Normal file
40
lib/core/src/lib/auth/auth-config.service.spec.ts
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
/*!
|
||||||
|
* @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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { HttpClientTestingModule } from '@angular/common/http/testing';
|
||||||
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
import { AUTH_MODULE_CONFIG } from './auth-config';
|
||||||
|
|
||||||
|
import { AuthConfigService } from './auth-config.service';
|
||||||
|
|
||||||
|
describe('AuthConfigService', () => {
|
||||||
|
let service: AuthConfigService;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [HttpClientTestingModule],
|
||||||
|
providers: [
|
||||||
|
{ provide: AUTH_MODULE_CONFIG, useValue: { useHash: true } }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
service = TestBed.inject(AuthConfigService);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be created', () => {
|
||||||
|
expect(service).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
82
lib/core/src/lib/auth/auth-config.service.ts
Normal file
82
lib/core/src/lib/auth/auth-config.service.ts
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
/*!
|
||||||
|
* @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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
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 { AuthModuleConfig, AUTH_MODULE_CONFIG } from './auth-config';
|
||||||
|
|
||||||
|
export function authConfigFactory(authConfigService: AuthConfigService): Promise<AuthConfig> {
|
||||||
|
return authConfigService.loadConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class AuthConfigService {
|
||||||
|
constructor(
|
||||||
|
private appConfigService: AppConfigService,
|
||||||
|
@Inject(AUTH_MODULE_CONFIG) private readonly authModuleConfig: AuthModuleConfig
|
||||||
|
) {}
|
||||||
|
|
||||||
|
private _authConfig!: AuthConfig;
|
||||||
|
get authConfig(): AuthConfig {
|
||||||
|
return this._authConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
loadConfig(): Promise<AuthConfig> {
|
||||||
|
return this.appConfigService.onLoad.pipe(take(1)).toPromise().then(this.loadAppConfig.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
loadAppConfig(): AuthConfig {
|
||||||
|
const oauth2: OauthConfigModel = Object.assign({}, this.appConfigService.get<OauthConfigModel>(AppConfigValues.OAUTHCONFIG, null));
|
||||||
|
const origin = window.location.origin;
|
||||||
|
const redirectUri = this.getRedirectUri();
|
||||||
|
|
||||||
|
const authConfig: AuthConfig = {
|
||||||
|
oidc: oauth2.implicitFlow || oauth2.codeFlow || false,
|
||||||
|
issuer: oauth2.host,
|
||||||
|
redirectUri,
|
||||||
|
silentRefreshRedirectUri: `${origin}/silent-refresh.html`,
|
||||||
|
postLogoutRedirectUri: `${origin}/${oauth2.redirectUriLogout}`,
|
||||||
|
clientId: oauth2.clientId,
|
||||||
|
scope: oauth2.scope,
|
||||||
|
dummyClientSecret: oauth2.secret || '',
|
||||||
|
...(oauth2.codeFlow && { responseType: 'code' })
|
||||||
|
};
|
||||||
|
|
||||||
|
return authConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
getRedirectUri(): string {
|
||||||
|
// required for this package as we handle the returned token on this view, with is provided by the AuthModule
|
||||||
|
const viewUrl = `view/authentication-confirmation`;
|
||||||
|
const useHash = this.authModuleConfig.useHash;
|
||||||
|
|
||||||
|
const redirectUri = useHash
|
||||||
|
? `${window.location.origin}/#/${viewUrl}`
|
||||||
|
: `${window.location.origin}/${viewUrl}`;
|
||||||
|
|
||||||
|
const oauth2: OauthConfigModel = Object.assign({}, this.appConfigService.get<OauthConfigModel>(AppConfigValues.OAUTHCONFIG, null));
|
||||||
|
|
||||||
|
// 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
|
||||||
|
return oauth2.implicitFlow && useHash ? `${redirectUri}/?` : redirectUri;
|
||||||
|
}
|
||||||
|
}
|
24
lib/core/src/lib/auth/auth-config.ts
Normal file
24
lib/core/src/lib/auth/auth-config.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
/*!
|
||||||
|
* @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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { InjectionToken } from '@angular/core';
|
||||||
|
|
||||||
|
export interface AuthModuleConfig {
|
||||||
|
readonly useHash: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const AUTH_MODULE_CONFIG = new InjectionToken<AuthModuleConfig>('AUTH_MODULE_CONFIG');
|
30
lib/core/src/lib/auth/auth-routing.module.ts
Normal file
30
lib/core/src/lib/auth/auth-routing.module.ts
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
/*!
|
||||||
|
* @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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
|
import { AuthenticationConfirmationComponent } from './view/authentication-confirmation/authentication-confirmation.component';
|
||||||
|
|
||||||
|
const routes: Routes = [
|
||||||
|
{ path: 'view/authentication-confirmation', component: AuthenticationConfirmationComponent }
|
||||||
|
];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [RouterModule.forChild(routes)],
|
||||||
|
exports: [RouterModule]
|
||||||
|
})
|
||||||
|
export class AuthRoutingModule {}
|
74
lib/core/src/lib/auth/auth.module.ts
Normal file
74
lib/core/src/lib/auth/auth.module.ts
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
/*!
|
||||||
|
* @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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { APP_INITIALIZER, ModuleWithProviders, NgModule } from '@angular/core';
|
||||||
|
import { AuthConfig, AUTH_CONFIG, OAuthModule, OAuthService, OAuthStorage } from 'angular-oauth2-oidc';
|
||||||
|
import { AlfrescoApiServiceWithAngularBasedHttpClient } from '../api-factories/alfresco-api-service-with-angular-based-http-client';
|
||||||
|
import { AlfrescoApiService } from '../services/alfresco-api.service';
|
||||||
|
import { AuthGuardBpm } from '../services/auth-guard-bpm.service';
|
||||||
|
import { AuthGuardEcm } from '../services/auth-guard-ecm.service';
|
||||||
|
import { AuthGuard } from '../services/auth-guard.service';
|
||||||
|
import { AuthenticationService } from '../services/authentication.service';
|
||||||
|
import { StorageService } from '../services/storage.service';
|
||||||
|
import { AuthModuleConfig, AUTH_MODULE_CONFIG } from './auth-config';
|
||||||
|
import { authConfigFactory, AuthConfigService } from './auth-config.service';
|
||||||
|
import { AuthRoutingModule } from './auth-routing.module';
|
||||||
|
import { AuthService } from './auth.service';
|
||||||
|
import { OidcAuthGuard } from './oidc-auth.guard';
|
||||||
|
import { OIDCAuthenticationService } from './oidc-authentication.service';
|
||||||
|
import { RedirectAuthService } from './redirect-auth.service';
|
||||||
|
import { AuthenticationConfirmationComponent } from './view/authentication-confirmation/authentication-confirmation.component';
|
||||||
|
|
||||||
|
|
||||||
|
export function loginFactory(oAuthService: OAuthService, storage: OAuthStorage, config: AuthConfig) {
|
||||||
|
const service = new RedirectAuthService(oAuthService, storage, config);
|
||||||
|
return () => service.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [AuthenticationConfirmationComponent],
|
||||||
|
imports: [AuthRoutingModule, OAuthModule.forRoot()],
|
||||||
|
providers: [
|
||||||
|
{ provide: OAuthStorage, useExisting: StorageService },
|
||||||
|
{ provide: AuthGuard, useClass: OidcAuthGuard },
|
||||||
|
{ provide: AuthGuardEcm, useClass: OidcAuthGuard },
|
||||||
|
{ provide: AuthGuardBpm, useClass: OidcAuthGuard },
|
||||||
|
{ provide: AuthenticationService, useClass: OIDCAuthenticationService },
|
||||||
|
{ provide: AlfrescoApiService, useClass: AlfrescoApiServiceWithAngularBasedHttpClient },
|
||||||
|
{
|
||||||
|
provide: AUTH_CONFIG,
|
||||||
|
useFactory: authConfigFactory,
|
||||||
|
deps: [AuthConfigService]
|
||||||
|
},
|
||||||
|
RedirectAuthService,
|
||||||
|
{ provide: AuthService, useExisting: RedirectAuthService },
|
||||||
|
{
|
||||||
|
provide: APP_INITIALIZER,
|
||||||
|
useFactory: loginFactory,
|
||||||
|
deps: [OAuthService, OAuthStorage, AUTH_CONFIG],
|
||||||
|
multi: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class AuthModule {
|
||||||
|
static forRoot(config: AuthModuleConfig = { useHash: false }): ModuleWithProviders<AuthModule> {
|
||||||
|
return {
|
||||||
|
ngModule: AuthModule,
|
||||||
|
providers: [{ provide: AUTH_MODULE_CONFIG, useValue: config }]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
60
lib/core/src/lib/auth/auth.service.ts
Normal file
60
lib/core/src/lib/auth/auth.service.ts
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
/*!
|
||||||
|
* @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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { TokenResponse } from 'angular-oauth2-oidc';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provide authentication/authorization through OAuth2/OIDC protocol.
|
||||||
|
*/
|
||||||
|
export abstract class AuthService {
|
||||||
|
/** Subscribe to whether the user has valid Id/Access tokens. */
|
||||||
|
abstract authenticated$: Observable<boolean>;
|
||||||
|
|
||||||
|
/** Get whether the user has valid Id/Access tokens. */
|
||||||
|
abstract authenticated: boolean;
|
||||||
|
|
||||||
|
/** Subscribe to errors reaching the IdP. */
|
||||||
|
abstract idpUnreachable$: Observable<Error>;
|
||||||
|
|
||||||
|
/** Get user profile, if authenticated. */
|
||||||
|
abstract getUserProfile<T>(): Promise<T>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initiate the IdP login flow.
|
||||||
|
*/
|
||||||
|
abstract login(currentUrl?: string): Promise<void> | void;
|
||||||
|
|
||||||
|
abstract baseAuthLogin(username: string, password: string): Observable<TokenResponse> ;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disconnect from IdP.
|
||||||
|
*
|
||||||
|
* @returns Promise may be returned depending on implementation
|
||||||
|
*/
|
||||||
|
abstract logout(): Promise<void> | void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Complete the login flow.
|
||||||
|
*
|
||||||
|
* In browsers, checks URL for auth and stored state. Call this once the application returns from IdP.
|
||||||
|
*
|
||||||
|
* @returns Promise, resolve with stored state, reject if unable to reach IdP
|
||||||
|
*/
|
||||||
|
abstract loginCallback(): Promise<string | undefined>;
|
||||||
|
abstract updateIDPConfiguration(...args: any[]): void;
|
||||||
|
}
|
23
lib/core/src/lib/auth/index.ts
Normal file
23
lib/core/src/lib/auth/index.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
/*!
|
||||||
|
* @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 * from './auth-routing.module';
|
||||||
|
export * from './auth.module';
|
||||||
|
export * from './auth.service';
|
||||||
|
export * from './oidc-auth.guard';
|
||||||
|
export * from './redirect-auth.service';
|
||||||
|
export * from './view/authentication-confirmation/authentication-confirmation.component';
|
36
lib/core/src/lib/auth/oidc-auth.guard.spec.ts
Normal file
36
lib/core/src/lib/auth/oidc-auth.guard.spec.ts
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
/*!
|
||||||
|
* @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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
import { MockProvider } from 'ng-mocks';
|
||||||
|
import { AuthService } from './auth.service';
|
||||||
|
import { OidcAuthGuard } from './oidc-auth.guard';
|
||||||
|
|
||||||
|
describe('OidcAuthGuard', () => {
|
||||||
|
let guard: OidcAuthGuard;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
providers: [OidcAuthGuard, MockProvider(AuthService)]
|
||||||
|
});
|
||||||
|
guard = TestBed.inject(OidcAuthGuard);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be created', () => {
|
||||||
|
expect(guard).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
52
lib/core/src/lib/auth/oidc-auth.guard.ts
Normal file
52
lib/core/src/lib/auth/oidc-auth.guard.ts
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
/*!
|
||||||
|
* @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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot, UrlTree } from '@angular/router';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { AuthService } from './auth.service';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class OidcAuthGuard implements CanActivate {
|
||||||
|
constructor(private auth: AuthService) {}
|
||||||
|
|
||||||
|
canActivate(
|
||||||
|
_route: ActivatedRouteSnapshot,
|
||||||
|
state: RouterStateSnapshot
|
||||||
|
): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
|
||||||
|
return this._isAuthenticated(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
canActivateChild(_route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
|
||||||
|
return this._isAuthenticated(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _isAuthenticated(state: RouterStateSnapshot) {
|
||||||
|
if (this.auth.authenticated) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const loginResult = this.auth.login(state.url);
|
||||||
|
|
||||||
|
if (loginResult instanceof Promise) {
|
||||||
|
return loginResult.then(() => true).catch(() => false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
125
lib/core/src/lib/auth/oidc-authentication.service.ts
Normal file
125
lib/core/src/lib/auth/oidc-authentication.service.ts
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
/*!
|
||||||
|
* @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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
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 '../services/cookie.service';
|
||||||
|
import { JwtHelperService } from '../services/jwt-helper.service';
|
||||||
|
import { LogService } from '../services/log.service';
|
||||||
|
import { AuthConfigService } from './auth-config.service';
|
||||||
|
import { AuthService } from './auth.service';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class OIDCAuthenticationService extends BaseAuthenticationService {
|
||||||
|
readonly supportCodeFlow = true;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
alfrescoApi: AlfrescoApiService,
|
||||||
|
appConfig: AppConfigService,
|
||||||
|
cookie: CookieService,
|
||||||
|
logService: LogService,
|
||||||
|
private authStorage: OAuthStorage,
|
||||||
|
private oauthService: OAuthService,
|
||||||
|
private readonly authConfig: AuthConfigService,
|
||||||
|
private readonly auth: AuthService
|
||||||
|
) {
|
||||||
|
super(alfrescoApi, appConfig, cookie, logService);
|
||||||
|
}
|
||||||
|
|
||||||
|
isEcmLoggedIn(): boolean {
|
||||||
|
return this.isLoggedIn();
|
||||||
|
}
|
||||||
|
|
||||||
|
isBpmLoggedIn(): boolean {
|
||||||
|
return this.isLoggedIn();
|
||||||
|
}
|
||||||
|
|
||||||
|
isLoggedIn(): boolean {
|
||||||
|
return this.oauthService.hasValidAccessToken() && this.oauthService.hasValidIdToken();
|
||||||
|
}
|
||||||
|
|
||||||
|
isLoggedInWith(_provider?: string): boolean {
|
||||||
|
return this.isLoggedIn();
|
||||||
|
}
|
||||||
|
|
||||||
|
isOauth(): boolean {
|
||||||
|
return this.appConfig.get(AppConfigValues.AUTHTYPE) === 'OAUTH';
|
||||||
|
}
|
||||||
|
|
||||||
|
isImplicitFlow() {
|
||||||
|
const oauth2: OauthConfigModel = Object.assign({}, this.appConfig.get<OauthConfigModel>(AppConfigValues.OAUTHCONFIG, null));
|
||||||
|
return !!oauth2?.implicitFlow;
|
||||||
|
}
|
||||||
|
|
||||||
|
isAuthCodeFlow() {
|
||||||
|
const oauth2: OauthConfigModel = Object.assign({}, this.appConfig.get<OauthConfigModel>(AppConfigValues.OAUTHCONFIG, null));
|
||||||
|
return !!oauth2?.codeFlow;
|
||||||
|
}
|
||||||
|
|
||||||
|
login(username: string, password: string, rememberMe: boolean = false): Observable<{ type: string; ticket: any }> {
|
||||||
|
return this.auth.baseAuthLogin(username, password).pipe(
|
||||||
|
map((response) => {
|
||||||
|
this.saveRememberMeCookie(rememberMe);
|
||||||
|
this.onLogin.next(response);
|
||||||
|
return {
|
||||||
|
type: this.appConfig.get(AppConfigValues.PROVIDERS),
|
||||||
|
ticket: response
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
catchError((err) => this.handleError(err))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ssoImplicitLogin() {
|
||||||
|
this.oauthService.initLoginFlow();
|
||||||
|
}
|
||||||
|
|
||||||
|
ssoCodeFlowLogin() {
|
||||||
|
this.oauthService.initCodeFlow();
|
||||||
|
}
|
||||||
|
|
||||||
|
isRememberMeSet(): boolean {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
logout() {
|
||||||
|
this.oauthService.logOut();
|
||||||
|
return EMPTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
getToken(): string {
|
||||||
|
return this.authStorage.getItem(JwtHelperService.USER_ACCESS_TOKEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
reset(): void {
|
||||||
|
const config = this.authConfig.loadAppConfig();
|
||||||
|
this.auth.updateIDPConfiguration(config);
|
||||||
|
const oauth2: OauthConfigModel = Object.assign({}, this.appConfig.get<OauthConfigModel>(AppConfigValues.OAUTHCONFIG, null));
|
||||||
|
|
||||||
|
if (config.oidc && oauth2.silentLogin) {
|
||||||
|
this.auth.login();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
167
lib/core/src/lib/auth/redirect-auth.service.ts
Normal file
167
lib/core/src/lib/auth/redirect-auth.service.ts
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
/*!
|
||||||
|
* @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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Inject, Injectable } from '@angular/core';
|
||||||
|
import { AuthConfig, AUTH_CONFIG, OAuthErrorEvent, OAuthService, OAuthStorage, TokenResponse } from 'angular-oauth2-oidc';
|
||||||
|
import { JwksValidationHandler } from 'angular-oauth2-oidc-jwks';
|
||||||
|
import { from, Observable } from 'rxjs';
|
||||||
|
import { distinctUntilChanged, filter, map, shareReplay, startWith } from 'rxjs/operators';
|
||||||
|
import { AuthService } from './auth.service';
|
||||||
|
|
||||||
|
const isPromise = <T>(value: T | Promise<T>): value is Promise<T> => value && typeof (value as Promise<T>).then === 'function';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class RedirectAuthService extends AuthService {
|
||||||
|
private _loadDiscoveryDocumentPromise = Promise.resolve(false);
|
||||||
|
|
||||||
|
/** Subscribe to whether the user has valid Id/Access tokens. */
|
||||||
|
authenticated$!: Observable<boolean>;
|
||||||
|
|
||||||
|
/** Subscribe to errors reaching the IdP. */
|
||||||
|
idpUnreachable$!: Observable<Error>;
|
||||||
|
|
||||||
|
/** Get whether the user has valid Id/Access tokens. */
|
||||||
|
get authenticated(): boolean {
|
||||||
|
return this.oauthService.hasValidIdToken() && this.oauthService.hasValidAccessToken();
|
||||||
|
}
|
||||||
|
|
||||||
|
private authConfig!: AuthConfig | Promise<AuthConfig>;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private oauthService: OAuthService,
|
||||||
|
private _oauthStorage: OAuthStorage,
|
||||||
|
@Inject(AUTH_CONFIG) authConfig: AuthConfig
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
this.authConfig = authConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
this.oauthService.clearHashAfterLogin = true;
|
||||||
|
|
||||||
|
this.authenticated$ = this.oauthService.events.pipe(
|
||||||
|
startWith(undefined),
|
||||||
|
map(() => this.authenticated),
|
||||||
|
distinctUntilChanged(),
|
||||||
|
shareReplay(1)
|
||||||
|
);
|
||||||
|
|
||||||
|
this.idpUnreachable$ = this.oauthService.events.pipe(
|
||||||
|
filter((event): event is OAuthErrorEvent => event.type === 'discovery_document_load_error'),
|
||||||
|
map((event) => event.reason as Error)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isPromise(this.authConfig)) {
|
||||||
|
return this.authConfig.then((config) => this.configureAuth(config));
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.configureAuth(this.authConfig);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
logout() {
|
||||||
|
this.oauthService.logOut();
|
||||||
|
}
|
||||||
|
|
||||||
|
async getUserProfile<T = unknown>(): Promise<T> {
|
||||||
|
await this.ensureDiscoveryDocument();
|
||||||
|
const userProfile = await this.oauthService.loadUserProfile();
|
||||||
|
return (userProfile as any).info;
|
||||||
|
}
|
||||||
|
|
||||||
|
ensureDiscoveryDocument(): Promise<boolean> {
|
||||||
|
this._loadDiscoveryDocumentPromise = this._loadDiscoveryDocumentPromise
|
||||||
|
.catch(() => false)
|
||||||
|
.then((loaded) => {
|
||||||
|
if (!loaded) {
|
||||||
|
return this.oauthService.loadDiscoveryDocument().then(() => true);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
return this._loadDiscoveryDocumentPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
login(currentUrl?: string): void {
|
||||||
|
let stateKey: string | undefined;
|
||||||
|
|
||||||
|
if (currentUrl) {
|
||||||
|
stateKey = `auth_state_${Math.random()}${Date.now()}`;
|
||||||
|
this._oauthStorage.setItem(stateKey, JSON.stringify(currentUrl || {}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// initLoginFlow will initialize the login flow in either code or implicit depending on the configuration
|
||||||
|
this.ensureDiscoveryDocument().then(() => void this.oauthService.initLoginFlow(stateKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
baseAuthLogin(username: string, password: string): Observable<TokenResponse> {
|
||||||
|
this.oauthService.useHttpBasicAuth = true;
|
||||||
|
|
||||||
|
return from(this.oauthService.fetchTokenUsingPasswordFlow(username, password)).pipe(
|
||||||
|
map((response) => {
|
||||||
|
const props = new Map<string, string>();
|
||||||
|
props.set('id_token', response.id_token);
|
||||||
|
// for backward compatibility we need to set the response in our storage
|
||||||
|
this.oauthService['storeAccessTokenResponse'](response.access_token, response.refresh_token, response.expires_in, response.scope, props);
|
||||||
|
return response;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async loginCallback(): Promise<string | undefined> {
|
||||||
|
return this.ensureDiscoveryDocument()
|
||||||
|
.then(() => this.oauthService.tryLogin({ preventClearHashAfterLogin: false }))
|
||||||
|
.then(() => this._getRedirectUrl());
|
||||||
|
}
|
||||||
|
|
||||||
|
private _getRedirectUrl() {
|
||||||
|
const DEFAULT_REDIRECT = '/';
|
||||||
|
const stateKey = this.oauthService.state;
|
||||||
|
|
||||||
|
if (stateKey) {
|
||||||
|
const stateStringified = this._oauthStorage.getItem(stateKey);
|
||||||
|
if (stateStringified) {
|
||||||
|
// cleanup state from storage
|
||||||
|
this._oauthStorage.removeItem(stateKey);
|
||||||
|
return JSON.parse(stateStringified);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return DEFAULT_REDIRECT;
|
||||||
|
}
|
||||||
|
|
||||||
|
private configureAuth(config: AuthConfig) {
|
||||||
|
this.oauthService.configure(config);
|
||||||
|
this.oauthService.tokenValidationHandler = new JwksValidationHandler();
|
||||||
|
|
||||||
|
if (config.sessionChecksEnabled) {
|
||||||
|
this.oauthService.events.pipe(filter((event) => event.type === 'session_terminated')).subscribe(() => {
|
||||||
|
this.oauthService.logOut();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.ensureDiscoveryDocument().then(() =>
|
||||||
|
void this.oauthService.setupAutomaticSilentRefresh()
|
||||||
|
).catch(() => {
|
||||||
|
// catch error to prevent the app from crashing when trying to access unprotected routes
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
updateIDPConfiguration(config: AuthConfig) {
|
||||||
|
this.oauthService.configure(config);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,51 @@
|
|||||||
|
/*!
|
||||||
|
* @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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { RouterTestingModule } from '@angular/router/testing';
|
||||||
|
import { MockProvider } from 'ng-mocks';
|
||||||
|
import { AuthService } from '../../auth.service';
|
||||||
|
import { AuthenticationConfirmationComponent } from './authentication-confirmation.component';
|
||||||
|
|
||||||
|
describe('AuthenticationConfirmationComponent', () => {
|
||||||
|
let component: AuthenticationConfirmationComponent;
|
||||||
|
let fixture: ComponentFixture<AuthenticationConfirmationComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [AuthenticationConfirmationComponent],
|
||||||
|
providers: [
|
||||||
|
MockProvider(AuthService, {
|
||||||
|
loginCallback() {
|
||||||
|
return Promise.resolve(undefined);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
],
|
||||||
|
imports: [RouterTestingModule]
|
||||||
|
}).compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(AuthenticationConfirmationComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@@ -0,0 +1,41 @@
|
|||||||
|
/*!
|
||||||
|
* @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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { from, of } from 'rxjs';
|
||||||
|
import { catchError, first, map } from 'rxjs/operators';
|
||||||
|
import { AuthService } from '../../auth.service';
|
||||||
|
|
||||||
|
const ROUTE_DEFAULT = '/';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
template: '',
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
|
})
|
||||||
|
export class AuthenticationConfirmationComponent {
|
||||||
|
constructor(private auth: AuthService, private _router: Router) {
|
||||||
|
const routeStored$ = from(this.auth.loginCallback()).pipe(
|
||||||
|
map((route) => route || ROUTE_DEFAULT),
|
||||||
|
catchError(() => of(ROUTE_DEFAULT))
|
||||||
|
);
|
||||||
|
|
||||||
|
routeStored$.pipe(first()).subscribe((route) => {
|
||||||
|
this._router.navigateByUrl(route, { replaceUrl: true });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@@ -50,7 +50,6 @@ import { PipeModule } from './pipes/pipe.module';
|
|||||||
|
|
||||||
import { AlfrescoApiService } from './services/alfresco-api.service';
|
import { AlfrescoApiService } from './services/alfresco-api.service';
|
||||||
import { TranslationService } from './services/translation.service';
|
import { TranslationService } from './services/translation.service';
|
||||||
import { startupServiceFactory } from './services/startup-service-factory';
|
|
||||||
import { SortingPickerModule } from './sorting-picker/sorting-picker.module';
|
import { SortingPickerModule } from './sorting-picker/sorting-picker.module';
|
||||||
import { IconModule } from './icon/icon.module';
|
import { IconModule } from './icon/icon.module';
|
||||||
import { TranslateLoaderService } from './services/translate-loader.service';
|
import { TranslateLoaderService } from './services/translate-loader.service';
|
||||||
@@ -66,6 +65,17 @@ import { LegacyApiClientModule } from './api-factories/legacy-api-client.module'
|
|||||||
import { RichTextEditorModule } from './rich-text-editor/rich-text-editor.module';
|
import { RichTextEditorModule } from './rich-text-editor/rich-text-editor.module';
|
||||||
import { HttpClientModule, HttpClientXsrfModule, HTTP_INTERCEPTORS } from '@angular/common/http';
|
import { HttpClientModule, HttpClientXsrfModule, HTTP_INTERCEPTORS } from '@angular/common/http';
|
||||||
import { AuthenticationService } from './services/authentication.service';
|
import { AuthenticationService } from './services/authentication.service';
|
||||||
|
import { loadAppConfig } from './app-config/app-config.loader';
|
||||||
|
import { AppConfigService } from './app-config/app-config.service';
|
||||||
|
import { StorageService } from './services/storage.service';
|
||||||
|
import { AlfrescoApiLoaderService, createAlfrescoApiInstance } from './api-factories/alfresco-api-v2-loader.service';
|
||||||
|
import { AlfrescoApiServiceWithAngularBasedHttpClient } from './api-factories/alfresco-api-service-with-angular-based-http-client';
|
||||||
|
|
||||||
|
interface Config {
|
||||||
|
readonly useAngularBasedHttpClientInAlfrescoJs: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultConfig: Config = { useAngularBasedHttpClientInAlfrescoJs: false };
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
@@ -149,7 +159,7 @@ import { AuthenticationService } from './services/authentication.service';
|
|||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class CoreModule {
|
export class CoreModule {
|
||||||
static forRoot(): ModuleWithProviders<CoreModule> {
|
static forRoot(config: Config = defaultConfig): ModuleWithProviders<CoreModule> {
|
||||||
return {
|
return {
|
||||||
ngModule: CoreModule,
|
ngModule: CoreModule,
|
||||||
providers: [
|
providers: [
|
||||||
@@ -158,11 +168,8 @@ export class CoreModule {
|
|||||||
{ provide: TranslateLoader, useClass: TranslateLoaderService },
|
{ provide: TranslateLoader, useClass: TranslateLoaderService },
|
||||||
{
|
{
|
||||||
provide: APP_INITIALIZER,
|
provide: APP_INITIALIZER,
|
||||||
useFactory: startupServiceFactory,
|
useFactory: loadAppConfig,
|
||||||
deps: [
|
deps: [ AppConfigService, StorageService ], multi: true
|
||||||
AlfrescoApiService
|
|
||||||
],
|
|
||||||
multi: true
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
provide: APP_INITIALIZER,
|
provide: APP_INITIALIZER,
|
||||||
@@ -176,8 +183,24 @@ export class CoreModule {
|
|||||||
deps: [VersionCompatibilityService],
|
deps: [VersionCompatibilityService],
|
||||||
multi: true
|
multi: true
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
provide: APP_INITIALIZER,
|
||||||
|
useFactory: createAlfrescoApiInstance,
|
||||||
|
deps: [ AlfrescoApiLoaderService ],
|
||||||
|
multi: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: APP_INITIALIZER,
|
||||||
|
useFactory: createAlfrescoApiInstance,
|
||||||
|
deps: [ AlfrescoApiLoaderService ],
|
||||||
|
multi: true
|
||||||
|
},
|
||||||
{ provide: HTTP_INTERCEPTORS, useClass: AuthenticationInterceptor, multi: true },
|
{ provide: HTTP_INTERCEPTORS, useClass: AuthenticationInterceptor, multi: true },
|
||||||
{ provide: Authentication, useClass: AuthenticationService }
|
{ provide: Authentication, useClass: AuthenticationService },
|
||||||
|
...(config.useAngularBasedHttpClientInAlfrescoJs
|
||||||
|
? [{ provide: AlfrescoApiService, useClass: AlfrescoApiServiceWithAngularBasedHttpClient }]
|
||||||
|
: []
|
||||||
|
)
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@@ -172,6 +172,7 @@
|
|||||||
"BASIC": "Basic Authentication",
|
"BASIC": "Basic Authentication",
|
||||||
"SSO": "SSO",
|
"SSO": "SSO",
|
||||||
"IMPLICIT-FLOW": "Implicit Flow",
|
"IMPLICIT-FLOW": "Implicit Flow",
|
||||||
|
"CODE-FLOW": "Code Flow",
|
||||||
"PROVIDER": "Provider",
|
"PROVIDER": "Provider",
|
||||||
"REQUIRED": "This field is required",
|
"REQUIRED": "This field is required",
|
||||||
"CS_URL_ERROR": "Content Services address doesn't match the URL format",
|
"CS_URL_ERROR": "Content Services address doesn't match the URL format",
|
||||||
|
@@ -20,6 +20,7 @@ export interface OauthConfigModel {
|
|||||||
clientId: string;
|
clientId: string;
|
||||||
scope: string;
|
scope: string;
|
||||||
implicitFlow: boolean;
|
implicitFlow: boolean;
|
||||||
|
codeFlow?: boolean;
|
||||||
redirectUri: string;
|
redirectUri: string;
|
||||||
silentLogin?: boolean;
|
silentLogin?: boolean;
|
||||||
secret?: string;
|
secret?: string;
|
||||||
|
@@ -47,23 +47,13 @@ export class AlfrescoApiService {
|
|||||||
return this.alfrescoApi;
|
return this.alfrescoApi;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(protected appConfig: AppConfigService, protected storageService: StorageService) {}
|
||||||
protected appConfig: AppConfigService,
|
|
||||||
protected storageService: StorageService) {
|
|
||||||
}
|
|
||||||
|
|
||||||
async load() {
|
async load(config: AlfrescoApiConfig): Promise<void> {
|
||||||
try {
|
this.currentAppConfig = config;
|
||||||
await this.appConfig.load();
|
|
||||||
this.storageService.prefix = this.appConfig.get<string>(AppConfigValues.STORAGE_PREFIX, '');
|
|
||||||
this.getCurrentAppConfig();
|
|
||||||
|
|
||||||
if (this.currentAppConfig.authType === 'OAUTH') {
|
if (config.authType === 'OAUTH') {
|
||||||
this.idpConfig = await this.appConfig.loadWellKnown(this.currentAppConfig.oauth2.host);
|
|
||||||
this.mapAlfrescoApiOpenIdConfig();
|
this.mapAlfrescoApiOpenIdConfig();
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
throw new Error('Something wrong happened when calling the app.config.json');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.initAlfrescoApiWithConfig();
|
this.initAlfrescoApiWithConfig();
|
||||||
@@ -73,7 +63,6 @@ export class AlfrescoApiService {
|
|||||||
async reset() {
|
async reset() {
|
||||||
this.getCurrentAppConfig();
|
this.getCurrentAppConfig();
|
||||||
if (this.currentAppConfig.authType === 'OAUTH') {
|
if (this.currentAppConfig.authType === 'OAUTH') {
|
||||||
this.idpConfig = await this.appConfig.loadWellKnown(this.currentAppConfig.oauth2.host);
|
|
||||||
this.mapAlfrescoApiOpenIdConfig();
|
this.mapAlfrescoApiOpenIdConfig();
|
||||||
}
|
}
|
||||||
this.initAlfrescoApiWithConfig();
|
this.initAlfrescoApiWithConfig();
|
||||||
@@ -88,7 +77,8 @@ export class AlfrescoApiService {
|
|||||||
return oauth;
|
return oauth;
|
||||||
}
|
}
|
||||||
|
|
||||||
private mapAlfrescoApiOpenIdConfig() {
|
private async mapAlfrescoApiOpenIdConfig() {
|
||||||
|
this.idpConfig = await this.appConfig.loadWellKnown(this.currentAppConfig.oauth2.host);
|
||||||
this.currentAppConfig.oauth2.tokenUrl = this.idpConfig.token_endpoint;
|
this.currentAppConfig.oauth2.tokenUrl = this.idpConfig.token_endpoint;
|
||||||
this.currentAppConfig.oauth2.authorizationUrl = this.idpConfig.authorization_endpoint;
|
this.currentAppConfig.oauth2.authorizationUrl = this.idpConfig.authorization_endpoint;
|
||||||
this.currentAppConfig.oauth2.logoutUrl = this.idpConfig.end_session_endpoint;
|
this.currentAppConfig.oauth2.logoutUrl = this.idpConfig.end_session_endpoint;
|
||||||
@@ -121,11 +111,15 @@ export class AlfrescoApiService {
|
|||||||
if (this.alfrescoApi && this.isDifferentConfig(this.lastConfig, this.currentAppConfig)) {
|
if (this.alfrescoApi && this.isDifferentConfig(this.lastConfig, this.currentAppConfig)) {
|
||||||
this.alfrescoApi.setConfig(this.currentAppConfig);
|
this.alfrescoApi.setConfig(this.currentAppConfig);
|
||||||
} else {
|
} else {
|
||||||
this.alfrescoApi = new AlfrescoApi(this.currentAppConfig);
|
this.alfrescoApi = this.createInstance(this.currentAppConfig);
|
||||||
}
|
}
|
||||||
this.lastConfig = this.currentAppConfig;
|
this.lastConfig = this.currentAppConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createInstance(config: AlfrescoApiConfig): AlfrescoApi {
|
||||||
|
return (this.alfrescoApi = new AlfrescoApi(config));
|
||||||
|
}
|
||||||
|
|
||||||
isDifferentConfig(lastConfig: AlfrescoApiConfig, newConfig: AlfrescoApiConfig) {
|
isDifferentConfig(lastConfig: AlfrescoApiConfig, newConfig: AlfrescoApiConfig) {
|
||||||
return JSON.stringify(lastConfig) !== JSON.stringify(newConfig);
|
return JSON.stringify(lastConfig) !== JSON.stringify(newConfig);
|
||||||
}
|
}
|
||||||
|
@@ -31,9 +31,8 @@ export class AuthBearerInterceptor implements HttpInterceptor {
|
|||||||
constructor(private injector: Injector, private authService: AuthenticationService) { }
|
constructor(private injector: Injector, private authService: AuthenticationService) { }
|
||||||
|
|
||||||
private loadExcludedUrlsRegex() {
|
private loadExcludedUrlsRegex() {
|
||||||
const excludedUrls: string[] = this.authService.getBearerExcludedUrls();
|
const excludedUrls = this.authService.getBearerExcludedUrls();
|
||||||
|
this.excludedUrlsRegex = excludedUrls.map((urlPattern) => new RegExp(urlPattern, 'i')) || [];
|
||||||
this.excludedUrlsRegex = [...excludedUrls].map((urlPattern) => new RegExp(urlPattern, 'i')) || [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
intercept(req: HttpRequest<any>, next: HttpHandler):
|
intercept(req: HttpRequest<any>, next: HttpHandler):
|
||||||
|
@@ -15,59 +15,32 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Authentication } from '@alfresco/adf-core/auth';
|
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { Observable, from, throwError, Observer, ReplaySubject, forkJoin } from 'rxjs';
|
import { forkJoin, from, Observable } from 'rxjs';
|
||||||
import { AlfrescoApiService } from './alfresco-api.service';
|
import { catchError, map, tap } from 'rxjs/operators';
|
||||||
import { CookieService } from './cookie.service';
|
|
||||||
import { LogService } from './log.service';
|
|
||||||
import { RedirectionModel } from '../models/redirection.model';
|
|
||||||
import { AppConfigService, AppConfigValues } from '../app-config/app-config.service';
|
import { AppConfigService, AppConfigValues } from '../app-config/app-config.service';
|
||||||
import { PeopleApi, UserProfileApi, UserRepresentation } from '@alfresco/js-api';
|
import { OauthConfigModel } from '../models/oauth-config.model';
|
||||||
import { map, catchError, tap } from 'rxjs/operators';
|
import { AlfrescoApiService } from './alfresco-api.service';
|
||||||
import { HttpHeaders } from '@angular/common/http';
|
import { BaseAuthenticationService } from './base-authentication.service';
|
||||||
|
import { CookieService } from './cookie.service';
|
||||||
import { JwtHelperService } from './jwt-helper.service';
|
import { JwtHelperService } from './jwt-helper.service';
|
||||||
|
import { LogService } from './log.service';
|
||||||
import { StorageService } from './storage.service';
|
import { StorageService } from './storage.service';
|
||||||
|
|
||||||
const REMEMBER_ME_COOKIE_KEY = 'ALFRESCO_REMEMBER_ME';
|
|
||||||
const REMEMBER_ME_UNTIL = 1000 * 60 * 60 * 24 * 30;
|
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class AuthenticationService extends Authentication {
|
export class AuthenticationService extends BaseAuthenticationService {
|
||||||
private redirectUrl: RedirectionModel = null;
|
readonly supportCodeFlow = false;
|
||||||
|
|
||||||
private bearerExcludedUrls: string[] = ['auth/realms', 'resources/', 'assets/'];
|
|
||||||
/**
|
|
||||||
* Emits login event
|
|
||||||
*/
|
|
||||||
onLogin: ReplaySubject<any> = new ReplaySubject<any>(1);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Emits logout event
|
|
||||||
*/
|
|
||||||
onLogout: ReplaySubject<any> = new ReplaySubject<any>(1);
|
|
||||||
|
|
||||||
_peopleApi: PeopleApi;
|
|
||||||
get peopleApi(): PeopleApi {
|
|
||||||
this._peopleApi = this._peopleApi ?? new PeopleApi(this.alfrescoApi.getInstance());
|
|
||||||
return this._peopleApi;
|
|
||||||
}
|
|
||||||
|
|
||||||
_profileApi: UserProfileApi;
|
|
||||||
get profileApi(): UserProfileApi {
|
|
||||||
this._profileApi = this._profileApi ?? new UserProfileApi(this.alfrescoApi.getInstance());
|
|
||||||
return this._profileApi;
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private appConfig: AppConfigService,
|
alfrescoApi: AlfrescoApiService,
|
||||||
private storageService: StorageService,
|
appConfig: AppConfigService,
|
||||||
private alfrescoApi: AlfrescoApiService,
|
cookie: CookieService,
|
||||||
private cookie: CookieService,
|
logService: LogService,
|
||||||
private logService: LogService) {
|
private storageService: StorageService
|
||||||
super();
|
) {
|
||||||
|
super(alfrescoApi, appConfig, cookie, logService);
|
||||||
this.alfrescoApi.alfrescoApiInitialized.subscribe(() => {
|
this.alfrescoApi.alfrescoApiInitialized.subscribe(() => {
|
||||||
this.alfrescoApi.getInstance().reply('logged-in', () => {
|
this.alfrescoApi.getInstance().reply('logged-in', () => {
|
||||||
this.onLogin.next();
|
this.onLogin.next();
|
||||||
@@ -114,15 +87,6 @@ export class AuthenticationService extends Authentication {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Does kerberos enabled?
|
|
||||||
*
|
|
||||||
* @returns True if enabled, false otherwise
|
|
||||||
*/
|
|
||||||
isKerberosEnabled(): boolean {
|
|
||||||
return this.appConfig.get<boolean>(AppConfigValues.AUTH_WITH_CREDENTIALS, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Does the provider support OAuth?
|
* Does the provider support OAuth?
|
||||||
*
|
*
|
||||||
@@ -132,37 +96,6 @@ export class AuthenticationService extends Authentication {
|
|||||||
return this.alfrescoApi.getInstance().isOauthConfiguration();
|
return this.alfrescoApi.getInstance().isOauthConfiguration();
|
||||||
}
|
}
|
||||||
|
|
||||||
isPublicUrl(): boolean {
|
|
||||||
return this.alfrescoApi.getInstance().isPublicUrl();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Does the provider support ECM?
|
|
||||||
*
|
|
||||||
* @returns True if supported, false otherwise
|
|
||||||
*/
|
|
||||||
isECMProvider(): boolean {
|
|
||||||
return this.alfrescoApi.getInstance().isEcmConfiguration();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Does the provider support BPM?
|
|
||||||
*
|
|
||||||
* @returns True if supported, false otherwise
|
|
||||||
*/
|
|
||||||
isBPMProvider(): boolean {
|
|
||||||
return this.alfrescoApi.getInstance().isBpmConfiguration();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Does the provider support both ECM and BPM?
|
|
||||||
*
|
|
||||||
* @returns True if both are supported, false otherwise
|
|
||||||
*/
|
|
||||||
isALLProvider(): boolean {
|
|
||||||
return this.alfrescoApi.getInstance().isEcmBpmConfiguration();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Logs the user in.
|
* Logs the user in.
|
||||||
*
|
*
|
||||||
@@ -172,18 +105,17 @@ export class AuthenticationService extends Authentication {
|
|||||||
* @returns Object with auth type ("ECM", "BPM" or "ALL") and auth ticket
|
* @returns Object with auth type ("ECM", "BPM" or "ALL") and auth ticket
|
||||||
*/
|
*/
|
||||||
login(username: string, password: string, rememberMe: boolean = false): Observable<{ type: string; ticket: any }> {
|
login(username: string, password: string, rememberMe: boolean = false): Observable<{ type: string; ticket: any }> {
|
||||||
return from(this.alfrescoApi.getInstance().login(username, password))
|
return from(this.alfrescoApi.getInstance().login(username, password)).pipe(
|
||||||
.pipe(
|
map((response: any) => {
|
||||||
map((response: any) => {
|
this.saveRememberMeCookie(rememberMe);
|
||||||
this.saveRememberMeCookie(rememberMe);
|
this.onLogin.next(response);
|
||||||
this.onLogin.next(response);
|
return {
|
||||||
return {
|
type: this.appConfig.get(AppConfigValues.PROVIDERS),
|
||||||
type: this.appConfig.get(AppConfigValues.PROVIDERS),
|
ticket: response
|
||||||
ticket: response
|
};
|
||||||
};
|
}),
|
||||||
}),
|
catchError((err) => this.handleError(err))
|
||||||
catchError((err) => this.handleError(err))
|
);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -193,31 +125,6 @@ export class AuthenticationService extends Authentication {
|
|||||||
this.alfrescoApi.getInstance().implicitLogin();
|
this.alfrescoApi.getInstance().implicitLogin();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Saves the "remember me" cookie as either a long-life cookie or a session cookie.
|
|
||||||
*
|
|
||||||
* @param rememberMe Enables a long-life cookie
|
|
||||||
*/
|
|
||||||
private saveRememberMeCookie(rememberMe: boolean): void {
|
|
||||||
let expiration = null;
|
|
||||||
|
|
||||||
if (rememberMe) {
|
|
||||||
expiration = new Date();
|
|
||||||
const time = expiration.getTime();
|
|
||||||
const expireTime = time + REMEMBER_ME_UNTIL;
|
|
||||||
expiration.setTime(expireTime);
|
|
||||||
}
|
|
||||||
this.cookie.setItem(REMEMBER_ME_COOKIE_KEY, '1', expiration, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks whether the "remember me" cookie was set or not.
|
|
||||||
*
|
|
||||||
* @returns True if set, false otherwise
|
|
||||||
*/
|
|
||||||
isRememberMeSet(): boolean {
|
|
||||||
return (this.cookie.getItem(REMEMBER_ME_COOKIE_KEY) !== null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Logs the user out.
|
* Logs the user out.
|
||||||
@@ -225,14 +132,13 @@ export class AuthenticationService extends Authentication {
|
|||||||
* @returns Response event called when logout is complete
|
* @returns Response event called when logout is complete
|
||||||
*/
|
*/
|
||||||
logout() {
|
logout() {
|
||||||
return from(this.callApiLogout())
|
return from(this.callApiLogout()).pipe(
|
||||||
.pipe(
|
tap((response) => {
|
||||||
tap((response) => {
|
this.onLogout.next(response);
|
||||||
this.onLogout.next(response);
|
return response;
|
||||||
return response;
|
}),
|
||||||
}),
|
catchError((err) => this.handleError(err))
|
||||||
catchError((err) => this.handleError(err))
|
);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private callApiLogout(): Promise<any> {
|
private callApiLogout(): Promise<any> {
|
||||||
@@ -242,37 +148,6 @@ export class AuthenticationService extends Authentication {
|
|||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the ECM ticket stored in the Storage.
|
|
||||||
*
|
|
||||||
* @returns The ticket or `null` if none was found
|
|
||||||
*/
|
|
||||||
getTicketEcm(): string | null {
|
|
||||||
return this.alfrescoApi.getInstance()?.getTicketEcm();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the BPM ticket stored in the Storage.
|
|
||||||
*
|
|
||||||
* @returns The ticket or `null` if none was found
|
|
||||||
*/
|
|
||||||
getTicketBpm(): string | null {
|
|
||||||
return this.alfrescoApi.getInstance()?.getTicketBpm();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the BPM ticket from the Storage in Base 64 format.
|
|
||||||
*
|
|
||||||
* @returns The ticket or `null` if none was found
|
|
||||||
*/
|
|
||||||
getTicketEcmBase64(): string | null {
|
|
||||||
const ticket = this.alfrescoApi.getInstance()?.getTicketEcm();
|
|
||||||
if (ticket) {
|
|
||||||
return 'Basic ' + btoa(ticket);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the user is logged in on an ECM provider.
|
* Checks if the user is logged in on an ECM provider.
|
||||||
*
|
*
|
||||||
@@ -303,76 +178,13 @@ export class AuthenticationService extends Authentication {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
isImplicitFlow(): boolean {
|
||||||
* Gets the ECM username.
|
const oauth2: OauthConfigModel = Object.assign({}, this.appConfig.get<OauthConfigModel>(AppConfigValues.OAUTHCONFIG, null));
|
||||||
*
|
return !!oauth2?.implicitFlow;
|
||||||
* @returns The ECM username
|
|
||||||
*/
|
|
||||||
getEcmUsername(): string {
|
|
||||||
return this.alfrescoApi.getInstance().getEcmUsername();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
isAuthCodeFlow(): boolean {
|
||||||
* Gets the BPM username
|
return false;
|
||||||
*
|
|
||||||
* @returns The BPM username
|
|
||||||
*/
|
|
||||||
getBpmUsername(): string {
|
|
||||||
return this.alfrescoApi.getInstance().getBpmUsername();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Sets the URL to redirect to after login.
|
|
||||||
*
|
|
||||||
* @param url URL to redirect to
|
|
||||||
*/
|
|
||||||
setRedirect(url: RedirectionModel) {
|
|
||||||
this.redirectUrl = url;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Gets the URL to redirect to after login.
|
|
||||||
*
|
|
||||||
* @returns The redirect URL
|
|
||||||
*/
|
|
||||||
getRedirect(): string {
|
|
||||||
const provider = this.appConfig.get<string>(AppConfigValues.PROVIDERS);
|
|
||||||
return this.hasValidRedirection(provider) ? this.redirectUrl.url : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets information about the user currently logged into APS.
|
|
||||||
*
|
|
||||||
* @returns User information
|
|
||||||
*/
|
|
||||||
getBpmLoggedUser(): Observable<UserRepresentation> {
|
|
||||||
return from(this.profileApi.getProfile());
|
|
||||||
}
|
|
||||||
|
|
||||||
private hasValidRedirection(provider: string): boolean {
|
|
||||||
return this.redirectUrl && (this.redirectUrl.provider === provider || this.hasSelectedProviderAll(provider));
|
|
||||||
}
|
|
||||||
|
|
||||||
private hasSelectedProviderAll(provider: string): boolean {
|
|
||||||
return this.redirectUrl && (this.redirectUrl.provider === 'ALL' || provider === 'ALL');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prints an error message in the console browser
|
|
||||||
*
|
|
||||||
* @param error Error message
|
|
||||||
* @returns Object representing the error message
|
|
||||||
*/
|
|
||||||
handleError(error: any): Observable<any> {
|
|
||||||
this.logService.error('Error when logging in', error);
|
|
||||||
return throwError(error || 'Server error');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the set of URLs that the token bearer is excluded from.
|
|
||||||
*
|
|
||||||
* @returns Array of URL strings
|
|
||||||
*/
|
|
||||||
getBearerExcludedUrls(): string[] {
|
|
||||||
return this.bearerExcludedUrls;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -384,60 +196,5 @@ export class AuthenticationService extends Authentication {
|
|||||||
return this.storageService.getItem(JwtHelperService.USER_ACCESS_TOKEN);
|
return this.storageService.getItem(JwtHelperService.USER_ACCESS_TOKEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
reset() {}
|
||||||
* Adds the auth token to an HTTP header using the 'bearer' scheme.
|
|
||||||
*
|
|
||||||
* @param headersArg Header that will receive the token
|
|
||||||
* @returns The new header with the token added
|
|
||||||
*/
|
|
||||||
addTokenToHeader(headersArg?: HttpHeaders): Observable<HttpHeaders> {
|
|
||||||
return new Observable((observer: Observer<any>) => {
|
|
||||||
let headers = headersArg;
|
|
||||||
if (!headers) {
|
|
||||||
headers = new HttpHeaders();
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
const header = this.getAuthHeaders(headers);
|
|
||||||
|
|
||||||
observer.next(header);
|
|
||||||
observer.complete();
|
|
||||||
} catch (error) {
|
|
||||||
observer.error(error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private getAuthHeaders(header: HttpHeaders): HttpHeaders {
|
|
||||||
const authType = this.appConfig.get<string>(AppConfigValues.AUTHTYPE, 'BASIC');
|
|
||||||
|
|
||||||
switch (authType) {
|
|
||||||
case 'OAUTH':
|
|
||||||
return this.addBearerToken(header);
|
|
||||||
case 'BASIC':
|
|
||||||
return this.addBasicAuth(header);
|
|
||||||
default:
|
|
||||||
return header;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private addBearerToken(header: HttpHeaders): HttpHeaders {
|
|
||||||
const token: string = this.getToken();
|
|
||||||
|
|
||||||
if (!token) {
|
|
||||||
return header;
|
|
||||||
}
|
|
||||||
|
|
||||||
return header.set('Authorization', 'bearer ' + token);
|
|
||||||
}
|
|
||||||
|
|
||||||
private addBasicAuth(header: HttpHeaders): HttpHeaders {
|
|
||||||
const ticket: string = this.getTicketEcmBase64();
|
|
||||||
|
|
||||||
if (!ticket) {
|
|
||||||
return header;
|
|
||||||
}
|
|
||||||
|
|
||||||
return header.set('Authorization', ticket);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -21,6 +21,7 @@ import { AlfrescoApiService } from './alfresco-api.service';
|
|||||||
import { StorageService } from './storage.service';
|
import { StorageService } from './storage.service';
|
||||||
import { UserPreferencesService } from './user-preferences.service';
|
import { UserPreferencesService } from './user-preferences.service';
|
||||||
import { DemoForm } from '../mock/form/demo-form.mock';
|
import { DemoForm } from '../mock/form/demo-form.mock';
|
||||||
|
import { AuthenticationService } from './authentication.service';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
@@ -29,11 +30,13 @@ export class CoreAutomationService {
|
|||||||
|
|
||||||
public forms = new DemoForm();
|
public forms = new DemoForm();
|
||||||
|
|
||||||
constructor(private appConfigService: AppConfigService,
|
constructor(
|
||||||
private alfrescoApiService: AlfrescoApiService,
|
private appConfigService: AppConfigService,
|
||||||
private userPreferencesService: UserPreferencesService,
|
private alfrescoApiService: AlfrescoApiService,
|
||||||
private storageService: StorageService) {
|
private userPreferencesService: UserPreferencesService,
|
||||||
}
|
private storageService: StorageService,
|
||||||
|
private auth: AuthenticationService
|
||||||
|
) {}
|
||||||
|
|
||||||
setup() {
|
setup() {
|
||||||
const adfProxy = window['adf'] || {};
|
const adfProxy = window['adf'] || {};
|
||||||
@@ -72,6 +75,7 @@ export class CoreAutomationService {
|
|||||||
|
|
||||||
adfProxy.apiReset = () => {
|
adfProxy.apiReset = () => {
|
||||||
this.alfrescoApiService.reset();
|
this.alfrescoApiService.reset();
|
||||||
|
this.auth.reset();
|
||||||
};
|
};
|
||||||
|
|
||||||
window['adf'] = adfProxy;
|
window['adf'] = adfProxy;
|
||||||
|
285
lib/core/src/lib/services/base-authentication.service.ts
Normal file
285
lib/core/src/lib/services/base-authentication.service.ts
Normal file
@@ -0,0 +1,285 @@
|
|||||||
|
/*!
|
||||||
|
* @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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { PeopleApi, UserProfileApi, UserRepresentation } from '@alfresco/js-api';
|
||||||
|
import { HttpHeaders } from '@angular/common/http';
|
||||||
|
import { from, Observable, Observer, ReplaySubject, throwError } from 'rxjs';
|
||||||
|
import { AppConfigService, AppConfigValues } from '../app-config/app-config.service';
|
||||||
|
import { RedirectionModel } from '../models/redirection.model';
|
||||||
|
import { AlfrescoApiService } from './alfresco-api.service';
|
||||||
|
import { CookieService } from './cookie.service';
|
||||||
|
import { LogService } from './log.service';
|
||||||
|
|
||||||
|
const REMEMBER_ME_COOKIE_KEY = 'ALFRESCO_REMEMBER_ME';
|
||||||
|
const REMEMBER_ME_UNTIL = 1000 * 60 * 60 * 24 * 30;
|
||||||
|
|
||||||
|
export abstract class BaseAuthenticationService {
|
||||||
|
protected bearerExcludedUrls: readonly string[] = ['resources/', 'assets/', 'auth/realms', 'idp/'];
|
||||||
|
protected redirectUrl: RedirectionModel = null;
|
||||||
|
|
||||||
|
onLogin = new ReplaySubject<any>(1);
|
||||||
|
onLogout = new ReplaySubject<any>(1);
|
||||||
|
|
||||||
|
_peopleApi: PeopleApi;
|
||||||
|
get peopleApi(): PeopleApi {
|
||||||
|
this._peopleApi = this._peopleApi ?? new PeopleApi(this.alfrescoApi.getInstance());
|
||||||
|
return this._peopleApi;
|
||||||
|
}
|
||||||
|
|
||||||
|
_profileApi: UserProfileApi;
|
||||||
|
get profileApi(): UserProfileApi {
|
||||||
|
this._profileApi = this._profileApi ?? new UserProfileApi(this.alfrescoApi.getInstance());
|
||||||
|
return this._profileApi;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
protected alfrescoApi: AlfrescoApiService,
|
||||||
|
protected appConfig: AppConfigService,
|
||||||
|
protected cookie: CookieService,
|
||||||
|
private logService: LogService
|
||||||
|
) {}
|
||||||
|
|
||||||
|
abstract readonly supportCodeFlow: boolean;
|
||||||
|
abstract getToken(): string;
|
||||||
|
abstract isLoggedIn(): boolean;
|
||||||
|
abstract isLoggedInWith(provider: string): boolean;
|
||||||
|
abstract isOauth(): boolean;
|
||||||
|
abstract isImplicitFlow(): boolean;
|
||||||
|
abstract isAuthCodeFlow(): boolean;
|
||||||
|
abstract login(username: string, password: string, rememberMe?: boolean): Observable<{ type: string; ticket: any }>;
|
||||||
|
abstract ssoImplicitLogin(): void;
|
||||||
|
abstract logout(): Observable<any>;
|
||||||
|
abstract isEcmLoggedIn(): boolean;
|
||||||
|
abstract isBpmLoggedIn(): boolean;
|
||||||
|
abstract reset(): void;
|
||||||
|
|
||||||
|
getBearerExcludedUrls(): readonly string[] {
|
||||||
|
return this.bearerExcludedUrls;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the auth token to an HTTP header using the 'bearer' scheme.
|
||||||
|
*
|
||||||
|
* @param headersArg Header that will receive the token
|
||||||
|
* @returns The new header with the token added
|
||||||
|
*/
|
||||||
|
addTokenToHeader(headersArg?: HttpHeaders): Observable<HttpHeaders> {
|
||||||
|
return new Observable((observer: Observer<any>) => {
|
||||||
|
let headers = headersArg;
|
||||||
|
if (!headers) {
|
||||||
|
headers = new HttpHeaders();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const header = this.getAuthHeaders(headers);
|
||||||
|
|
||||||
|
observer.next(header);
|
||||||
|
observer.complete();
|
||||||
|
} catch (error) {
|
||||||
|
observer.error(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private getAuthHeaders(header: HttpHeaders): HttpHeaders {
|
||||||
|
const authType = this.appConfig.get<string>(AppConfigValues.AUTHTYPE, 'BASIC');
|
||||||
|
|
||||||
|
switch (authType) {
|
||||||
|
case 'OAUTH':
|
||||||
|
return this.addBearerToken(header);
|
||||||
|
case 'BASIC':
|
||||||
|
return this.addBasicAuth(header);
|
||||||
|
default:
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private addBearerToken(header: HttpHeaders): HttpHeaders {
|
||||||
|
const token: string = this.getToken();
|
||||||
|
|
||||||
|
if (!token) {
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
|
||||||
|
return header.set('Authorization', 'bearer ' + token);
|
||||||
|
}
|
||||||
|
|
||||||
|
private addBasicAuth(header: HttpHeaders): HttpHeaders {
|
||||||
|
const ticket: string = this.getTicketEcmBase64();
|
||||||
|
|
||||||
|
if (!ticket) {
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
|
||||||
|
return header.set('Authorization', ticket);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the ECM username.
|
||||||
|
*
|
||||||
|
* @returns The ECM username
|
||||||
|
*/
|
||||||
|
getEcmUsername(): string {
|
||||||
|
return this.alfrescoApi.getInstance().getEcmUsername();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the BPM username
|
||||||
|
*
|
||||||
|
* @returns The BPM username
|
||||||
|
*/
|
||||||
|
getBpmUsername(): string {
|
||||||
|
return this.alfrescoApi.getInstance().getBpmUsername();
|
||||||
|
}
|
||||||
|
|
||||||
|
isPublicUrl(): boolean {
|
||||||
|
return this.alfrescoApi.getInstance().isPublicUrl();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does the provider support ECM?
|
||||||
|
*
|
||||||
|
* @returns True if supported, false otherwise
|
||||||
|
*/
|
||||||
|
isECMProvider(): boolean {
|
||||||
|
return this.alfrescoApi.getInstance().isEcmConfiguration();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does the provider support BPM?
|
||||||
|
*
|
||||||
|
* @returns True if supported, false otherwise
|
||||||
|
*/
|
||||||
|
isBPMProvider(): boolean {
|
||||||
|
return this.alfrescoApi.getInstance().isBpmConfiguration();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does the provider support both ECM and BPM?
|
||||||
|
*
|
||||||
|
* @returns True if both are supported, false otherwise
|
||||||
|
*/
|
||||||
|
isALLProvider(): boolean {
|
||||||
|
return this.alfrescoApi.getInstance().isEcmBpmConfiguration();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the ECM ticket stored in the Storage.
|
||||||
|
*
|
||||||
|
* @returns The ticket or `null` if none was found
|
||||||
|
*/
|
||||||
|
getTicketEcm(): string | null {
|
||||||
|
return this.alfrescoApi.getInstance()?.getTicketEcm();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the BPM ticket stored in the Storage.
|
||||||
|
*
|
||||||
|
* @returns The ticket or `null` if none was found
|
||||||
|
*/
|
||||||
|
getTicketBpm(): string | null {
|
||||||
|
return this.alfrescoApi.getInstance()?.getTicketBpm();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the BPM ticket from the Storage in Base 64 format.
|
||||||
|
*
|
||||||
|
* @returns The ticket or `null` if none was found
|
||||||
|
*/
|
||||||
|
getTicketEcmBase64(): string | null {
|
||||||
|
const ticket = this.alfrescoApi.getInstance()?.getTicketEcm();
|
||||||
|
if (ticket) {
|
||||||
|
return 'Basic ' + btoa(ticket);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets information about the user currently logged into APS.
|
||||||
|
*
|
||||||
|
* @returns User information
|
||||||
|
*/
|
||||||
|
getBpmLoggedUser(): Observable<UserRepresentation> {
|
||||||
|
return from(this.profileApi.getProfile());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prints an error message in the console browser
|
||||||
|
*
|
||||||
|
* @param error Error message
|
||||||
|
* @returns Object representing the error message
|
||||||
|
*/
|
||||||
|
handleError(error: any): Observable<any> {
|
||||||
|
this.logService.error('Error when logging in', error);
|
||||||
|
return throwError(error || 'Server error');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does kerberos enabled?
|
||||||
|
*
|
||||||
|
* @returns True if enabled, false otherwise
|
||||||
|
*/
|
||||||
|
isKerberosEnabled(): boolean {
|
||||||
|
return this.appConfig.get<boolean>(AppConfigValues.AUTH_WITH_CREDENTIALS, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves the "remember me" cookie as either a long-life cookie or a session cookie.
|
||||||
|
*
|
||||||
|
* @param rememberMe Enables a long-life cookie
|
||||||
|
*/
|
||||||
|
saveRememberMeCookie(rememberMe: boolean): void {
|
||||||
|
let expiration = null;
|
||||||
|
|
||||||
|
if (rememberMe) {
|
||||||
|
expiration = new Date();
|
||||||
|
const time = expiration.getTime();
|
||||||
|
const expireTime = time + REMEMBER_ME_UNTIL;
|
||||||
|
expiration.setTime(expireTime);
|
||||||
|
}
|
||||||
|
this.cookie.setItem(REMEMBER_ME_COOKIE_KEY, '1', expiration, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the "remember me" cookie was set or not.
|
||||||
|
*
|
||||||
|
* @returns True if set, false otherwise
|
||||||
|
*/
|
||||||
|
isRememberMeSet(): boolean {
|
||||||
|
return this.cookie.getItem(REMEMBER_ME_COOKIE_KEY) !== null;
|
||||||
|
}
|
||||||
|
|
||||||
|
setRedirect(url?: RedirectionModel) {
|
||||||
|
this.redirectUrl = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Gets the URL to redirect to after login.
|
||||||
|
*
|
||||||
|
* @returns The redirect URL
|
||||||
|
*/
|
||||||
|
getRedirect(): string {
|
||||||
|
const provider = this.appConfig.get<string>(AppConfigValues.PROVIDERS);
|
||||||
|
return this.hasValidRedirection(provider) ? this.redirectUrl.url : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private hasValidRedirection(provider: string): boolean {
|
||||||
|
return this.redirectUrl && (this.redirectUrl.provider === provider || this.hasSelectedProviderAll(provider));
|
||||||
|
}
|
||||||
|
|
||||||
|
private hasSelectedProviderAll(provider: string): boolean {
|
||||||
|
return this.redirectUrl && (this.redirectUrl.provider === 'ALL' || provider === 'ALL');
|
||||||
|
}
|
||||||
|
}
|
@@ -15,9 +15,12 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { AlfrescoApiService } from './alfresco-api.service';
|
import { AppConfigService, AppConfigValues } from '../app-config/app-config.service';
|
||||||
|
import { StorageService } from './storage.service';
|
||||||
|
|
||||||
// eslint-disable-next-line prefer-arrow/prefer-arrow-functions
|
export function loadAppConfig(appConfigService: AppConfigService, storageService: StorageService) {
|
||||||
export function startupServiceFactory(alfrescoApiService: AlfrescoApiService) {
|
return () =>
|
||||||
return () => alfrescoApiService.load();
|
appConfigService.load().then(() => {
|
||||||
|
storageService.prefix = appConfigService.get<string>(AppConfigValues.STORAGE_PREFIX, '');
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
@@ -122,6 +122,12 @@
|
|||||||
formControlName="implicitFlow">
|
formControlName="implicitFlow">
|
||||||
</mat-slide-toggle>
|
</mat-slide-toggle>
|
||||||
|
|
||||||
|
<ng-container *ngIf="supportsCodeFlow">
|
||||||
|
<label for="codeFlow">{{ 'CORE.HOST_SETTINGS.CODE-FLOW'| translate }}</label>
|
||||||
|
<mat-slide-toggle class="adf-full-width" name="codeFlow" [color]="'primary'"
|
||||||
|
formControlName="codeFlow">
|
||||||
|
</mat-slide-toggle>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
<mat-form-field class="adf-full-width">
|
<mat-form-field class="adf-full-width">
|
||||||
<mat-label>{{ 'CORE.HOST_SETTINGS.REDIRECT'| translate }}</mat-label>
|
<mat-label>{{ 'CORE.HOST_SETTINGS.REDIRECT'| translate }}</mat-label>
|
||||||
|
@@ -22,6 +22,7 @@ import { StorageService } from '../services/storage.service';
|
|||||||
import { AlfrescoApiService } from '../services/alfresco-api.service';
|
import { AlfrescoApiService } from '../services/alfresco-api.service';
|
||||||
import { OauthConfigModel } from '../models/oauth-config.model';
|
import { OauthConfigModel } from '../models/oauth-config.model';
|
||||||
import { ENTER } from '@angular/cdk/keycodes';
|
import { ENTER } from '@angular/cdk/keycodes';
|
||||||
|
import { AuthenticationService } from '../services/authentication.service';
|
||||||
|
|
||||||
export const HOST_REGEX = '^(http|https):\/\/.*[^/]$';
|
export const HOST_REGEX = '^(http|https):\/\/.*[^/]$';
|
||||||
|
|
||||||
@@ -60,11 +61,13 @@ export class HostSettingsComponent implements OnInit {
|
|||||||
@Output()
|
@Output()
|
||||||
success = new EventEmitter<boolean>();
|
success = new EventEmitter<boolean>();
|
||||||
|
|
||||||
constructor(private formBuilder: UntypedFormBuilder,
|
constructor(
|
||||||
private storageService: StorageService,
|
private formBuilder: UntypedFormBuilder,
|
||||||
private alfrescoApiService: AlfrescoApiService,
|
private storageService: StorageService,
|
||||||
private appConfig: AppConfigService) {
|
private alfrescoApiService: AlfrescoApiService,
|
||||||
}
|
private appConfig: AppConfigService,
|
||||||
|
private auth: AuthenticationService
|
||||||
|
) {}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
if (this.providers.length === 1) {
|
if (this.providers.length === 1) {
|
||||||
@@ -149,6 +152,7 @@ export class HostSettingsComponent implements OnInit {
|
|||||||
secret: oauth.secret,
|
secret: oauth.secret,
|
||||||
silentLogin: oauth.silentLogin,
|
silentLogin: oauth.silentLogin,
|
||||||
implicitFlow: oauth.implicitFlow,
|
implicitFlow: oauth.implicitFlow,
|
||||||
|
codeFlow: oauth.codeFlow,
|
||||||
publicUrls: [oauth.publicUrls]
|
publicUrls: [oauth.publicUrls]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -188,6 +192,7 @@ export class HostSettingsComponent implements OnInit {
|
|||||||
this.storageService.setItem(AppConfigValues.AUTHTYPE, values.authType);
|
this.storageService.setItem(AppConfigValues.AUTHTYPE, values.authType);
|
||||||
|
|
||||||
this.alfrescoApiService.reset();
|
this.alfrescoApiService.reset();
|
||||||
|
this.auth.reset();
|
||||||
this.alfrescoApiService.getInstance().invalidateSession();
|
this.alfrescoApiService.getInstance().invalidateSession();
|
||||||
this.success.emit(true);
|
this.success.emit(true);
|
||||||
}
|
}
|
||||||
@@ -231,6 +236,10 @@ export class HostSettingsComponent implements OnInit {
|
|||||||
return this.form.get('authType').value === 'OAUTH';
|
return this.form.get('authType').value === 'OAUTH';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get supportsCodeFlow(): boolean {
|
||||||
|
return this.auth.supportCodeFlow;
|
||||||
|
}
|
||||||
|
|
||||||
get providersControl(): UntypedFormControl {
|
get providersControl(): UntypedFormControl {
|
||||||
return this.form.get('providersControl') as UntypedFormControl;
|
return this.form.get('providersControl') as UntypedFormControl;
|
||||||
}
|
}
|
||||||
@@ -267,6 +276,10 @@ export class HostSettingsComponent implements OnInit {
|
|||||||
return this.oauthConfig.get('implicitFlow') as UntypedFormControl;
|
return this.oauthConfig.get('implicitFlow') as UntypedFormControl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get codeFlow(): UntypedFormControl {
|
||||||
|
return this.oauthConfig.get('codeFlow') as UntypedFormControl;
|
||||||
|
}
|
||||||
|
|
||||||
get silentLogin(): UntypedFormControl {
|
get silentLogin(): UntypedFormControl {
|
||||||
return this.oauthConfig.get('silentLogin') as UntypedFormControl;
|
return this.oauthConfig.get('silentLogin') as UntypedFormControl;
|
||||||
}
|
}
|
||||||
|
@@ -55,3 +55,4 @@ export * from './lib/testing';
|
|||||||
|
|
||||||
export * from './lib/material.module';
|
export * from './lib/material.module';
|
||||||
export * from './lib/core.module';
|
export * from './lib/core.module';
|
||||||
|
export { AuthModule } from './lib/auth/auth.module';
|
||||||
|
Reference in New Issue
Block a user