diff --git a/.github/actions/e2e/action.yml b/.github/actions/e2e/action.yml
index 8f3c3f44f9..116bb8c9c5 100644
--- a/.github/actions/e2e/action.yml
+++ b/.github/actions/e2e/action.yml
@@ -153,7 +153,7 @@ runs:
echo $PROXY_HOST_BPM
echo "GIT_HASH=$GIT_HASH" >> $GITHUB_ENV
- - name: run test
+ - name: run test
id: e2e_run
if: ${{ steps.determine-affected.outputs.isAffected == 'true' }}
env:
diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml
index df76626c4d..f2bb331279 100644
--- a/.github/workflows/pull-request.yml
+++ b/.github/workflows/pull-request.yml
@@ -392,15 +392,6 @@ jobs:
check-cs-env: "true"
check-ps-cloud-env: "true"
deps: "testing"
- - description: "Process Cloud: People"
- test-id: "process-services-cloud"
- folder: "process-services-cloud/people"
- provider: "ALL"
- auth: "OAUTH"
- apa-proxy: true
- check-cs-env: "true"
- check-ps-cloud-env: "true"
- deps: "testing"
- description: "Process Cloud: Process"
test-id: "process-services-cloud"
folder: "process-services-cloud/process"
diff --git a/.vscode/launch.json b/.vscode/launch.json
index fb054eac83..08f1196558 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -10,7 +10,7 @@
"name": "e2e",
"program": "${workspaceFolder}/node_modules/protractor/bin/protractor",
"args": [
- "`${workspaceFolder}/.vscode/closest-config-finder.sh ${file} e2e/protractor.conf.js`",
+ "./e2e/protractor.conf.js",
"--specs=${file}"
],
"envFile": "${workspaceFolder}/.env",
diff --git a/cspell.json b/cspell.json
index 885040b502..9e3dd28159 100644
--- a/cspell.json
+++ b/cspell.json
@@ -141,7 +141,8 @@
"webscript",
"Whitespaces",
"xdescribe",
- "xsrf"
+ "xsrf",
+ "BPMECM"
],
"dictionaries": [
"html",
diff --git a/demo-shell/src/app/app.component.ts b/demo-shell/src/app/app.component.ts
index 416e56d109..2273758e1d 100644
--- a/demo-shell/src/app/app.component.ts
+++ b/demo-shell/src/app/app.component.ts
@@ -18,11 +18,11 @@
import { Component, ViewEncapsulation, OnInit } from '@angular/core';
import {
AuthenticationService,
- AlfrescoApiService,
PageTitleService
} from '@alfresco/adf-core';
import { Router } from '@angular/router';
import { MatDialog } from '@angular/material/dialog';
+import { AdfHttpClient } from '@alfresco/adf-core/api';
@Component({
selector: 'app-root',
@@ -33,7 +33,7 @@ import { MatDialog } from '@angular/material/dialog';
export class AppComponent implements OnInit {
constructor(private pageTitleService: PageTitleService,
- private alfrescoApiService: AlfrescoApiService,
+ private adfHttpClient: AdfHttpClient,
private authenticationService: AuthenticationService,
private router: Router,
private dialogRef: MatDialog) {
@@ -43,7 +43,7 @@ export class AppComponent implements OnInit {
ngOnInit() {
this.pageTitleService.setTitle('title');
- this.alfrescoApiService.getInstance().on('error', (error) => {
+ this.adfHttpClient.on('error', (error) => {
if (error.status === 401) {
if (!this.authenticationService.isLoggedIn()) {
this.dialogRef.closeAll();
diff --git a/demo-shell/src/app/app.module.ts b/demo-shell/src/app/app.module.ts
index d911d172dd..2822b58c51 100644
--- a/demo-shell/src/app/app.module.ts
+++ b/demo-shell/src/app/app.module.ts
@@ -74,7 +74,7 @@ import { UserInfoComponent } from './components/app-layout/user-info/user-info.c
environment.e2e ? NoopAnimationsModule : BrowserAnimationsModule,
ReactiveFormsModule,
RouterModule.forRoot(appRoutes, { useHash: true, relativeLinkResolution: 'legacy' }),
- ...(environment.oidc ? [AuthModule.forRoot({ useHash: true })] : []),
+ AuthModule.forRoot({ useHash: true }),
FormsModule,
HttpClientModule,
MaterialModule,
diff --git a/demo-shell/src/app/components/app-layout/user-info/user-info.component.ts b/demo-shell/src/app/components/app-layout/user-info/user-info.component.ts
index 4d29c42294..894e4ba329 100644
--- a/demo-shell/src/app/components/app-layout/user-info/user-info.component.ts
+++ b/demo-shell/src/app/components/app-layout/user-info/user-info.component.ts
@@ -17,7 +17,13 @@
import { EcmUserModel, PeopleContentService } from '@alfresco/adf-content-services';
import { BpmUserModel, PeopleProcessService } from '@alfresco/adf-process-services';
-import { AuthenticationService, IdentityUserModel, IdentityUserService, UserInfoMode } from '@alfresco/adf-core';
+import {
+ AuthenticationService,
+ BasicAlfrescoAuthService,
+ IdentityUserModel,
+ IdentityUserService,
+ UserInfoMode
+} from '@alfresco/adf-core';
import { Component, OnInit, Input } from '@angular/core';
import { MenuPositionX, MenuPositionY } from '@angular/material/menu';
import { Observable, of } from 'rxjs';
@@ -46,6 +52,7 @@ export class UserInfoComponent implements OnInit {
constructor(private peopleContentService: PeopleContentService,
private peopleProcessService: PeopleProcessService,
private identityUserService: IdentityUserService,
+ private basicAlfrescoAuthService: BasicAlfrescoAuthService,
private authService: AuthenticationService) {
}
@@ -77,7 +84,7 @@ export class UserInfoComponent implements OnInit {
}
get isLoggedIn(): boolean {
- if (this.authService.isKerberosEnabled()) {
+ if (this.basicAlfrescoAuthService.isKerberosEnabled()) {
return true;
}
return this.authService.isLoggedIn();
@@ -96,15 +103,15 @@ export class UserInfoComponent implements OnInit {
}
private isAllLoggedIn() {
- return (this.authService.isEcmLoggedIn() && this.authService.isBpmLoggedIn()) || (this.authService.isALLProvider() && this.authService.isKerberosEnabled());
+ return (this.authService.isEcmLoggedIn() && this.authService.isBpmLoggedIn()) || (this.authService.isALLProvider() && this.basicAlfrescoAuthService.isKerberosEnabled());
}
private isBpmLoggedIn() {
- return this.authService.isBpmLoggedIn() || (this.authService.isECMProvider() && this.authService.isKerberosEnabled());
+ return this.authService.isBpmLoggedIn() || (this.authService.isECMProvider() && this.basicAlfrescoAuthService.isKerberosEnabled());
}
private isEcmLoggedIn() {
- return this.authService.isEcmLoggedIn() || (this.authService.isECMProvider() && this.authService.isKerberosEnabled());
+ return this.authService.isEcmLoggedIn() || (this.authService.isECMProvider() && this.basicAlfrescoAuthService.isKerberosEnabled());
}
}
diff --git a/demo-shell/src/app/components/settings/host-settings.component.html b/demo-shell/src/app/components/settings/host-settings.component.html
index 3eaa05348d..9207275c67 100644
--- a/demo-shell/src/app/components/settings/host-settings.component.html
+++ b/demo-shell/src/app/components/settings/host-settings.component.html
@@ -71,7 +71,7 @@
-
+
Code Flow
diff --git a/demo-shell/src/app/components/settings/host-settings.component.ts b/demo-shell/src/app/components/settings/host-settings.component.ts
index d7f7f9dfb5..601a21d358 100644
--- a/demo-shell/src/app/components/settings/host-settings.component.ts
+++ b/demo-shell/src/app/components/settings/host-settings.component.ts
@@ -65,7 +65,7 @@ export class HostSettingsComponent implements OnInit {
private storageService: StorageService,
private alfrescoApiService: AlfrescoApiService,
private appConfig: AppConfigService,
- private auth: AuthenticationService
+ private authenticationService: AuthenticationService
) {}
ngOnInit() {
@@ -191,8 +191,8 @@ export class HostSettingsComponent implements OnInit {
this.storageService.setItem(AppConfigValues.AUTHTYPE, values.authType);
this.alfrescoApiService.reset();
- this.auth.reset();
- this.alfrescoApiService.getInstance().invalidateSession();
+ this.authenticationService.reset();
+ this.authenticationService.logout();
this.success.emit(true);
}
@@ -235,10 +235,6 @@ export class HostSettingsComponent implements OnInit {
return this.form.get('authType').value === 'OAUTH';
}
- get supportsCodeFlow(): boolean {
- return this.auth.supportCodeFlow;
- }
-
get providersControl(): UntypedFormControl {
return this.form.get('providersControl') as UntypedFormControl;
}
diff --git a/e2e/core/viewer/viewer-content-services-component.e2e.ts b/e2e/content-services/components/viewer-content-services-component.e2e.ts
similarity index 99%
rename from e2e/core/viewer/viewer-content-services-component.e2e.ts
rename to e2e/content-services/components/viewer-content-services-component.e2e.ts
index d3d24a9c21..f38cdb292c 100644
--- a/e2e/core/viewer/viewer-content-services-component.e2e.ts
+++ b/e2e/content-services/components/viewer-content-services-component.e2e.ts
@@ -21,7 +21,7 @@ import { ContentServicesPage } from '../../core/pages/content-services.page';
import { FileModel } from '../../models/ACS/file.model';
import { NavigationBarPage } from '../../core/pages/navigation-bar.page';
import { VersionManagePage } from '../pages/version-manager.page';
-import { MetadataViewPage } from '../pages/metadata-view.page';
+import { MetadataViewPage } from '../../core/pages/metadata-view.page';
describe('Content Services Viewer', () => {
const acsUser = new UserModel();
diff --git a/e2e/content-services/components/viewer-vesion.e2e.ts b/e2e/content-services/components/viewer-vesion.e2e.ts
new file mode 100644
index 0000000000..635246b01c
--- /dev/null
+++ b/e2e/content-services/components/viewer-vesion.e2e.ts
@@ -0,0 +1,94 @@
+/*!
+ * @license
+ * Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { browser } from 'protractor';
+import { createApiService, FileBrowserUtil, LoginPage, UploadActions, UserModel, UsersActions, ViewerPage } from '@alfresco/adf-testing';
+import { ContentServicesPage } from '../../core/pages/content-services.page';
+import { FileModel } from '../../models/ACS/file.model';
+import { NavigationBarPage } from '../../core/pages/navigation-bar.page';
+import { VersionManagePage } from '../pages/version-manager.page';
+
+describe('Viewer', () => {
+
+ const navigationBarPage = new NavigationBarPage();
+ const viewerPage = new ViewerPage();
+ const loginPage = new LoginPage();
+ const contentServicesPage = new ContentServicesPage();
+
+ const apiService = createApiService();
+ const uploadActions = new UploadActions(apiService);
+ const usersActions = new UsersActions(apiService);
+
+ const versionManagePage = new VersionManagePage();
+ const acsUser = new UserModel();
+ let txtFileUploaded;
+
+ const txtFileInfo = new FileModel({
+ name: browser.params.resources.Files.ADF_DOCUMENTS.TXT.file_name,
+ location: browser.params.resources.Files.ADF_DOCUMENTS.TXT.file_path
+ });
+
+ const fileModelVersionTwo = new FileModel({
+ name: browser.params.resources.Files.ADF_DOCUMENTS.TXT.file_name,
+ location: browser.params.resources.Files.ADF_DOCUMENTS.TXT.file_location
+ });
+
+ beforeAll(async () => {
+ await apiService.loginWithProfile('admin');
+ await usersActions.createUser(acsUser);
+
+ await apiService.login(acsUser.username, acsUser.password);
+
+ txtFileUploaded = await uploadActions.uploadFile(txtFileInfo.location, txtFileInfo.name, '-my-');
+
+ await loginPage.login(acsUser.username, acsUser.password);
+ });
+
+ afterAll(async () => {
+ await apiService.loginWithProfile('admin');
+ await uploadActions.deleteFileOrFolder(txtFileUploaded.entry.id);
+ await navigationBarPage.clickLogoutButton();
+ });
+
+ beforeEach(async () => {
+ await contentServicesPage.goToDocumentList();
+ await contentServicesPage.doubleClickRow(txtFileUploaded.entry.name);
+ await viewerPage.waitTillContentLoaded();
+ });
+
+ afterEach(async () => {
+ await viewerPage.clickCloseButton();
+ });
+
+ it('[C362242] Should the Viewer be able to view a previous version of a file', async () => {
+ await contentServicesPage.versionManagerContent(txtFileInfo.name);
+ await versionManagePage.showNewVersionButton.click();
+ await versionManagePage.uploadNewVersionFile(fileModelVersionTwo.location);
+ await versionManagePage.closeVersionDialog();
+ await contentServicesPage.doubleClickRow(txtFileUploaded.entry.name);
+ await viewerPage.waitTillContentLoaded();
+ await viewerPage.clickInfoButton();
+ await viewerPage.clickOnTab('Versions');
+ await versionManagePage.viewFileVersion('1.0');
+ await viewerPage.expectUrlToContain('1.0');
+ });
+
+ it('[C362265] Should the Viewer be able to download a previous version of a file', async () => {
+ await viewerPage.clickDownloadButton();
+ await FileBrowserUtil.isFileDownloaded(txtFileInfo.name);
+ });
+});
diff --git a/e2e/content-services/document-list/document-list-pagination.e2e.ts b/e2e/content-services/document-list/document-list-pagination.e2e.ts
index 97bf66840c..bef68ea475 100644
--- a/e2e/content-services/document-list/document-list-pagination.e2e.ts
+++ b/e2e/content-services/document-list/document-list-pagination.e2e.ts
@@ -31,7 +31,7 @@ import { NavigationBarPage } from '../../core/pages/navigation-bar.page';
import { FolderModel } from '../../models/ACS/folder.model';
import { browser } from 'protractor';
import { FileModel } from '../../models/ACS/file.model';
-import { UploadDialogPage } from '../../core/pages/dialog/upload-dialog.page';
+import { UploadDialogPage } from '../pages/upload-dialog.page';
describe('Document List - Pagination', () => {
const pagination = {
diff --git a/e2e/core/pages/dialog/upload-dialog.page.ts b/e2e/content-services/pages/upload-dialog.page.ts
similarity index 100%
rename from e2e/core/pages/dialog/upload-dialog.page.ts
rename to e2e/content-services/pages/upload-dialog.page.ts
diff --git a/e2e/core/pages/version-manager.page.ts b/e2e/content-services/pages/version-manager.page.ts
similarity index 100%
rename from e2e/core/pages/version-manager.page.ts
rename to e2e/content-services/pages/version-manager.page.ts
diff --git a/e2e/content-services/upload/cancel-upload.e2e.ts b/e2e/content-services/upload/cancel-upload.e2e.ts
index 996ff6f578..a3e68a6dd0 100644
--- a/e2e/content-services/upload/cancel-upload.e2e.ts
+++ b/e2e/content-services/upload/cancel-upload.e2e.ts
@@ -18,7 +18,7 @@
import { browser } from 'protractor';
import { createApiService, LoginPage, UploadActions, UserModel, UsersActions } from '@alfresco/adf-testing';
import { ContentServicesPage } from '../../core/pages/content-services.page';
-import { UploadDialogPage } from '../../core/pages/dialog/upload-dialog.page';
+import { UploadDialogPage } from '../pages/upload-dialog.page';
import { UploadTogglesPage } from '../../core/pages/dialog/upload-toggles.page';
import { FileModel } from '../../models/ACS/file.model';
diff --git a/e2e/content-services/upload/excluded-file.e2e.ts b/e2e/content-services/upload/excluded-file.e2e.ts
index ad79d89352..08622a54ae 100644
--- a/e2e/content-services/upload/excluded-file.e2e.ts
+++ b/e2e/content-services/upload/excluded-file.e2e.ts
@@ -24,7 +24,7 @@ import { createApiService,
UsersActions
} from '@alfresco/adf-testing';
import { ContentServicesPage } from '../../core/pages/content-services.page';
-import { UploadDialogPage } from '../../core/pages/dialog/upload-dialog.page';
+import { UploadDialogPage } from '../pages/upload-dialog.page';
import { UploadTogglesPage } from '../../core/pages/dialog/upload-toggles.page';
import { FileModel } from '../../models/ACS/file.model';
import { NavigationBarPage } from '../../core/pages/navigation-bar.page';
diff --git a/e2e/content-services/upload/upload-dialog.e2e.ts b/e2e/content-services/upload/upload-dialog.e2e.ts
index 6640b58253..9265c572cf 100644
--- a/e2e/content-services/upload/upload-dialog.e2e.ts
+++ b/e2e/content-services/upload/upload-dialog.e2e.ts
@@ -17,11 +17,11 @@
import { createApiService, LoginPage, UploadActions, UserModel, UsersActions } from '@alfresco/adf-testing';
import { ContentServicesPage } from '../../core/pages/content-services.page';
-import { UploadDialogPage } from '../../core/pages/dialog/upload-dialog.page';
+import { UploadDialogPage } from '../pages/upload-dialog.page';
import { UploadTogglesPage } from '../../core/pages/dialog/upload-toggles.page';
import { FileModel } from '../../models/ACS/file.model';
import { browser } from 'protractor';
-import { VersionManagePage } from '../../core/pages/version-manager.page';
+import { VersionManagePage } from '../pages/version-manager.page';
describe('Upload component', () => {
diff --git a/e2e/content-services/upload/uploader-component.e2e.ts b/e2e/content-services/upload/uploader-component.e2e.ts
index 2e6d94a5b0..80b80a8caf 100644
--- a/e2e/content-services/upload/uploader-component.e2e.ts
+++ b/e2e/content-services/upload/uploader-component.e2e.ts
@@ -19,7 +19,7 @@ import { browser, by, element } from 'protractor';
import { createApiService, DropActions, LoginPage, StringUtil, UploadActions, UserModel, UsersActions } from '@alfresco/adf-testing';
import { ContentServicesPage } from '../../core/pages/content-services.page';
-import { UploadDialogPage } from '../../core/pages/dialog/upload-dialog.page';
+import { UploadDialogPage } from '../pages/upload-dialog.page';
import { UploadTogglesPage } from '../../core/pages/dialog/upload-toggles.page';
import { FileModel } from '../../models/ACS/file.model';
import { NavigationBarPage } from '../../core/pages/navigation-bar.page';
diff --git a/e2e/content-services/upload/user-permission.e2e.ts b/e2e/content-services/upload/user-permission.e2e.ts
index ac9fe598a9..8a952eec68 100644
--- a/e2e/content-services/upload/user-permission.e2e.ts
+++ b/e2e/content-services/upload/user-permission.e2e.ts
@@ -18,7 +18,7 @@
import { browser } from 'protractor';
import { createApiService, LoginPage, SnackbarPage, StringUtil, UserModel, UsersActions } from '@alfresco/adf-testing';
import { ContentServicesPage } from '../../core/pages/content-services.page';
-import { UploadDialogPage } from '../../core/pages/dialog/upload-dialog.page';
+import { UploadDialogPage } from '../pages/upload-dialog.page';
import { NavigationBarPage } from '../../core/pages/navigation-bar.page';
import { FileModel } from '../../models/ACS/file.model';
import CONSTANTS = require('../../util/constants');
diff --git a/e2e/content-services/upload/version-actions.e2e.ts b/e2e/content-services/upload/version-actions.e2e.ts
index 043cb68d0d..9bcd9cd371 100644
--- a/e2e/content-services/upload/version-actions.e2e.ts
+++ b/e2e/content-services/upload/version-actions.e2e.ts
@@ -28,9 +28,9 @@ import { createApiService,
import { browser, by, element } from 'protractor';
import { FileModel } from '../../models/ACS/file.model';
import { ContentServicesPage } from '../../core/pages/content-services.page';
-import { UploadDialogPage } from '../../core/pages/dialog/upload-dialog.page';
+import { UploadDialogPage } from '../pages/upload-dialog.page';
import { NavigationBarPage } from '../../core/pages/navigation-bar.page';
-import { VersionManagePage } from '../../core/pages/version-manager.page';
+import { VersionManagePage } from '../pages/version-manager.page';
describe('Version component actions', () => {
diff --git a/e2e/content-services/upload/version-permissions.e2e.ts b/e2e/content-services/upload/version-permissions.e2e.ts
index 5203df7963..7dae21aaf7 100644
--- a/e2e/content-services/upload/version-permissions.e2e.ts
+++ b/e2e/content-services/upload/version-permissions.e2e.ts
@@ -26,8 +26,8 @@ import {
UsersActions
} from '@alfresco/adf-testing';
import { NavigationBarPage } from '../../core/pages/navigation-bar.page';
-import { VersionManagePage } from '../../core/pages/version-manager.page';
-import { UploadDialogPage } from '../../core/pages/dialog/upload-dialog.page';
+import { VersionManagePage } from '../pages/version-manager.page';
+import { UploadDialogPage } from '../pages/upload-dialog.page';
import { ContentServicesPage } from '../../core/pages/content-services.page';
import { FileModel } from '../../models/ACS/file.model';
import CONSTANTS = require('../../util/constants');
diff --git a/e2e/content-services/upload/version-properties.e2e.ts b/e2e/content-services/upload/version-properties.e2e.ts
index 726fbc2ee0..2430500a48 100644
--- a/e2e/content-services/upload/version-properties.e2e.ts
+++ b/e2e/content-services/upload/version-properties.e2e.ts
@@ -24,7 +24,7 @@ import { createApiService,
UsersActions, ViewerPage
} from '@alfresco/adf-testing';
import { ContentServicesPage } from '../../core/pages/content-services.page';
-import { VersionManagePage } from '../../core/pages/version-manager.page';
+import { VersionManagePage } from '../pages/version-manager.page';
import { FileModel } from '../../models/ACS/file.model';
import { NavigationBarPage } from '../../core/pages/navigation-bar.page';
diff --git a/e2e/content-services/upload/version-smoke-tests.e2e.ts b/e2e/content-services/upload/version-smoke-tests.e2e.ts
index c6f5439b09..3b6930be86 100644
--- a/e2e/content-services/upload/version-smoke-tests.e2e.ts
+++ b/e2e/content-services/upload/version-smoke-tests.e2e.ts
@@ -18,7 +18,7 @@
import { browser } from 'protractor';
import { createApiService, LoginPage, UploadActions, UserModel, UsersActions } from '@alfresco/adf-testing';
import { ContentServicesPage } from '../../core/pages/content-services.page';
-import { VersionManagePage } from '../../core/pages/version-manager.page';
+import { VersionManagePage } from '../pages/version-manager.page';
import { FileModel } from '../../models/ACS/file.model';
import { NavigationBarPage } from '../../core/pages/navigation-bar.page';
diff --git a/e2e/process-services-cloud/task-list/apps-section-cloud.e2e.ts b/e2e/process-services-cloud/task-list/apps-section-cloud.e2e.ts
deleted file mode 100644
index 59b5664474..0000000000
--- a/e2e/process-services-cloud/task-list/apps-section-cloud.e2e.ts
+++ /dev/null
@@ -1,79 +0,0 @@
-/*!
- * @license
- * Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { createApiService, Application, AppListCloudPage, IdentityService, LocalStorageUtil, LoginPage } from '@alfresco/adf-testing';
-import { browser } from 'protractor';
-import { NavigationBarPage } from '../../core/pages/navigation-bar.page';
-
-describe('Applications list', () => {
-
- const simpleApp = browser.params.resources.ACTIVITI_CLOUD_APPS.SIMPLE_APP.name;
-
- const loginSSOPage = new LoginPage();
- const navigationBarPage = new NavigationBarPage();
- const appListCloudPage = new AppListCloudPage();
-
- const apiService = createApiService();
- const applicationsService = new Application(apiService);
- const identityService = new IdentityService(apiService);
-
- let testUser;
- const appNames = [];
-
- beforeAll(async () => {
- await apiService.loginWithProfile('identityAdmin');
- testUser = await identityService.createIdentityUserWithRole( [identityService.ROLES.ACTIVITI_USER, identityService.ROLES.ACTIVITI_DEVOPS]);
-
- await loginSSOPage.login(testUser.username, testUser.password);
- await apiService.login(testUser.username, testUser.password);
-
- const applications = await applicationsService.getApplicationsByStatus('RUNNING');
-
- applications.list.entries.forEach(app => {
- appNames.push(app.entry.name.toLowerCase());
- });
-
- await LocalStorageUtil.setConfigField('alfresco-deployed-apps', '[]');
- await LocalStorageUtil.apiReset();
- });
-
- afterAll(async () => {
- await apiService.loginWithProfile('identityAdmin');
- await identityService.deleteIdentityUser(testUser.idIdentityService);
- });
-
- it('[C310373] Should all the app with running state be displayed on dashboard when alfresco-deployed-apps is not used in config file', async () => {
- await navigationBarPage.navigateToProcessServicesCloudPage();
- await appListCloudPage.checkApsContainer();
-
- const list = await appListCloudPage.getNameOfTheApplications();
-
- await expect(JSON.stringify(list)).toEqual(JSON.stringify(appNames));
- });
-
- it('[C289910] Should the app be displayed on dashboard when is deployed on APS', async () => {
- await browser.refresh();
- await navigationBarPage.navigateToProcessServicesCloudPage();
- await appListCloudPage.checkApsContainer();
-
- await appListCloudPage.checkAppIsDisplayed(simpleApp);
- await appListCloudPage.checkAppIsDisplayed(browser.params.resources.ACTIVITI_CLOUD_APPS.CANDIDATE_BASE_APP.name);
- await appListCloudPage.checkAppIsDisplayed(browser.params.resources.ACTIVITI_CLOUD_APPS.SUB_PROCESS_APP.name);
-
- await expect(await appListCloudPage.countAllApps()).toEqual(3);
- });
-});
diff --git a/e2e/protractor.conf.js b/e2e/protractor.conf.js
index 729ca318c0..2d0c6a5161 100644
--- a/e2e/protractor.conf.js
+++ b/e2e/protractor.conf.js
@@ -285,7 +285,7 @@ exports.config = {
// @ts-ignore
if (browser.params.testConfig.appConfig.authType === 'OAUTH') {
-
+ Logger.info(`Configure demo shell OAUTH`);
// @ts-ignore
await LocalStorageUtil.setStorageItem('identityHost', browser.params.testConfig.appConfig.identityHost);
// @ts-ignore
diff --git a/e2e/search/components/search-number-range.e2e.ts b/e2e/search/components/search-number-range.e2e.ts
index 5223f73045..58a9b67907 100644
--- a/e2e/search/components/search-number-range.e2e.ts
+++ b/e2e/search/components/search-number-range.e2e.ts
@@ -270,7 +270,7 @@ describe('Search Number Range Filter', () => {
for (const currentResult of results) {
const currentSize = await BrowserActions.getAttribute(currentResult, 'title');
if (currentSize && currentSize.trim() !== '') {
- await expect(currentSize === '0').toBe(true);
+ await expect((currentSize === '0' || currentSize === '1')).toBe(true);
}
}
});
diff --git a/e2e/test.config.js b/e2e/test.config.js
index bd1effdba9..34bab52016 100644
--- a/e2e/test.config.js
+++ b/e2e/test.config.js
@@ -9,14 +9,14 @@ const HOST = process.env.URL_HOST_ADF;
const LOG = process.env.E2E_LOG_LEVEL;
-const HOST_ECM = process.env.PROXY_HOST_ECM || HOST || 'ecm';
-const HOST_BPM = process.env.PROXY_HOST_BPM || HOST || 'bpm';
+const HOST_ECM = process.env.PROXY_HOST_ECM || process.env.PROXY_HOST_ADF || HOST || 'ecm';
+const HOST_BPM = process.env.PROXY_HOST_BPM || process.env.PROXY_HOST_ADF || HOST || 'bpm';
+const HOST_SSO = process.env.HOST_SSO || process.env.PROXY_HOST_ADF || HOST || 'oauth';
+const IDENTITY_HOST = process.env.IDENTITY_HOST || process.env.HOST_SSO + '/auth/admin/realms/alfresco';
const PROVIDER = process.env.PROVIDER ? process.env.PROVIDER : 'ALL';
const AUTH_TYPE = process.env.AUTH_TYPE ? process.env.AUTH_TYPE : 'BASIC';
-const HOST_SSO = process.env.HOST_SSO || process.env.PROXY_HOST_ADF || HOST || 'oauth';
-const IDENTITY_HOST = process.env.IDENTITY_HOST || process.env.HOST_SSO + '/auth/admin/realms/alfresco';
const OAUTH_CLIENT_ID = process.env.OAUTH_CLIENDID || 'alfresco';
const IDENTITY_ADMIN_EMAIL = process.env.IDENTITY_ADMIN_EMAIL || "defaultadmin";
diff --git a/lib/content-services/src/lib/common/services/content.service.spec.ts b/lib/content-services/src/lib/common/services/content.service.spec.ts
index c2a7dadce5..74e3825412 100644
--- a/lib/content-services/src/lib/common/services/content.service.spec.ts
+++ b/lib/content-services/src/lib/common/services/content.service.spec.ts
@@ -21,16 +21,11 @@ import { AppConfigService, AuthenticationService, StorageService, CoreTestingMod
import { Node, PermissionsInfo } from '@alfresco/js-api';
import { TranslateModule } from '@ngx-translate/core';
-declare let jasmine: any;
-
describe('ContentService', () => {
let contentService: ContentService;
let authService: AuthenticationService;
let storage: StorageService;
- let node: any;
-
- const nodeId = 'fake-node-id';
beforeEach(() => {
TestBed.configureTestingModule({
@@ -44,14 +39,6 @@ describe('ContentService', () => {
storage = TestBed.inject(StorageService);
storage.clear();
- node = {
- entry: {
- id: nodeId
- }
- };
-
- jasmine.Ajax.install();
-
const appConfig: AppConfigService = TestBed.inject(AppConfigService);
appConfig.config = {
ecmHost: 'http://localhost:9876/ecm',
@@ -59,24 +46,6 @@ describe('ContentService', () => {
};
});
- afterEach(() => {
- jasmine.Ajax.uninstall();
- });
-
- it('should return a valid content URL', (done) => {
- authService.login('fake-username', 'fake-password').subscribe(() => {
- expect(contentService.getContentUrl(node)).toContain('/ecm/alfresco/api/' +
- '-default-/public/alfresco/versions/1/nodes/fake-node-id/content?attachment=false&alf_ticket=fake-post-ticket');
- done();
- });
-
- jasmine.Ajax.requests.mostRecent().respondWith({
- status: 201,
- contentType: 'application/json',
- responseText: JSON.stringify({ entry: { id: 'fake-post-ticket', userId: 'admin' } })
- });
- });
-
describe('AllowableOperations', () => {
it('should hasAllowableOperations be false if allowableOperation is not present in the node', () => {
diff --git a/lib/content-services/src/lib/common/services/discovery-api.service.ts b/lib/content-services/src/lib/common/services/discovery-api.service.ts
index a5db003450..8cefad2f7c 100644
--- a/lib/content-services/src/lib/common/services/discovery-api.service.ts
+++ b/lib/content-services/src/lib/common/services/discovery-api.service.ts
@@ -18,16 +18,27 @@
import { Injectable } from '@angular/core';
import { from, Observable, throwError, Subject } from 'rxjs';
import { catchError, map, switchMap, filter, take } from 'rxjs/operators';
-import { RepositoryInfo, SystemPropertiesRepresentation } from '@alfresco/js-api';
+import {
+ RepositoryInfo,
+ SystemPropertiesRepresentation,
+ DiscoveryApi,
+ AboutApi,
+ SystemPropertiesApi
+} from '@alfresco/js-api';
-import { BpmProductVersionModel, AuthenticationService } from '@alfresco/adf-core';
-import { ApiClientsService } from '@alfresco/adf-core/api';
+import { AlfrescoApiService, BpmProductVersionModel, AuthenticationService } from '@alfresco/adf-core';
@Injectable({
providedIn: 'root'
})
export class DiscoveryApiService {
+ private _discoveryApi: DiscoveryApi;
+ get discoveryApi(): DiscoveryApi {
+ this._discoveryApi = this._discoveryApi ?? new DiscoveryApi(this.alfrescoApiService.getInstance());
+ return this._discoveryApi;
+ }
+
/**
* Gets product information for Content Services.
*/
@@ -35,15 +46,17 @@ export class DiscoveryApiService {
constructor(
private authenticationService: AuthenticationService,
- private apiClientsService: ApiClientsService
+ private alfrescoApiService: AlfrescoApiService
) {
- this.authenticationService.onLogin
- .pipe(
+ this.authenticationService.onLogin.subscribe(() => {
+ this.alfrescoApiService.alfrescoApiInitialized.pipe(
filter(() => this.authenticationService.isEcmLoggedIn()),
take(1),
switchMap(() => this.getEcmProductInfo())
)
- .subscribe((info) => this.ecmProductInfo$.next(info));
+ .subscribe((info) => this.ecmProductInfo$.next(info));
+
+ });
}
@@ -53,9 +66,8 @@ export class DiscoveryApiService {
* @returns ProductVersionModel containing product details
*/
getEcmProductInfo(): Observable {
- const discoveryApi = this.apiClientsService.get('DiscoveryClient.discovery');
- return from(discoveryApi.getRepositoryInformation())
+ return from(this.discoveryApi.getRepositoryInformation())
.pipe(
map((res) => res.entry.repository),
catchError((err) => throwError(err))
@@ -68,7 +80,7 @@ export class DiscoveryApiService {
* @returns ProductVersionModel containing product details
*/
getBpmProductInfo(): Observable {
- const aboutApi = this.apiClientsService.get('ActivitiClient.about');
+ const aboutApi = new AboutApi(this.alfrescoApiService.getInstance());
return from(aboutApi.getAppVersion())
.pipe(
@@ -78,7 +90,7 @@ export class DiscoveryApiService {
}
getBPMSystemProperties(): Observable {
- const systemPropertiesApi = this.apiClientsService.get('ActivitiClient.system-properties');
+ const systemPropertiesApi = new SystemPropertiesApi(this.alfrescoApiService.getInstance());
return from(systemPropertiesApi.getProperties())
.pipe(
diff --git a/lib/content-services/src/lib/common/services/people-content.service.spec.ts b/lib/content-services/src/lib/common/services/people-content.service.spec.ts
index f8efb236d7..3bc542fa8c 100644
--- a/lib/content-services/src/lib/common/services/people-content.service.spec.ts
+++ b/lib/content-services/src/lib/common/services/people-content.service.spec.ts
@@ -25,7 +25,6 @@ import {
import {
AlfrescoApiService,
AlfrescoApiServiceMock,
- AuthenticationService,
CoreTestingModule
} from '@alfresco/adf-core';
import { PeopleContentQueryRequestModel, PeopleContentService } from './people-content.service';
@@ -34,7 +33,6 @@ import { TestBed } from '@angular/core/testing';
describe('PeopleContentService', () => {
let peopleContentService: PeopleContentService;
- let authenticationService: AuthenticationService;
beforeEach(() => {
TestBed.configureTestingModule({
@@ -47,7 +45,6 @@ describe('PeopleContentService', () => {
]
});
- authenticationService = TestBed.inject(AuthenticationService);
peopleContentService = TestBed.inject(PeopleContentService);
});
@@ -130,17 +127,6 @@ describe('PeopleContentService', () => {
expect(getCurrentPersonSpy.calls.count()).toEqual(1);
});
- it('should reset the admin cache upon logout', async () => {
- spyOn(peopleContentService.peopleApi, 'getPerson').and.returnValue(Promise.resolve({ entry: fakeEcmAdminUser } as any));
-
- const user = await peopleContentService.getCurrentUserInfo().toPromise();
- expect(user.id).toEqual('fake-id');
- expect(peopleContentService.isCurrentUserAdmin()).toBe(true);
-
- authenticationService.onLogout.next(true);
- expect(peopleContentService.isCurrentUserAdmin()).toBe(false);
- });
-
it('should not change current user on every getPerson call', async () => {
const getCurrentPersonSpy = spyOn(peopleContentService.peopleApi, 'getPerson').and.returnValue(Promise.resolve({entry: fakeEcmAdminUser} as any));
await peopleContentService.getCurrentUserInfo().toPromise();
diff --git a/lib/content-services/src/lib/directives/library-favorite.directive.spec.ts b/lib/content-services/src/lib/directives/library-favorite.directive.spec.ts
index 9af3a8f133..f908211679 100644
--- a/lib/content-services/src/lib/directives/library-favorite.directive.spec.ts
+++ b/lib/content-services/src/lib/directives/library-favorite.directive.spec.ts
@@ -18,8 +18,7 @@
import { Component, ViewChild } from '@angular/core';
import { LibraryFavoriteDirective } from './library-favorite.directive';
import { TestBed, ComponentFixture } from '@angular/core/testing';
-import { TranslateModule } from '@ngx-translate/core';
-import { AlfrescoApiServiceMock, CoreModule, AlfrescoApiService } from '@alfresco/adf-core';
+import { CoreTestingModule } from '@alfresco/adf-core';
import { LibraryEntity } from '../interfaces/library-entity.interface';
@Component({
@@ -40,10 +39,7 @@ describe('LibraryFavoriteDirective', () => {
beforeEach(() => {
TestBed.configureTestingModule({
- imports: [TranslateModule.forRoot(), CoreModule.forRoot()],
- providers: [
- { provide: AlfrescoApiService, useClass: AlfrescoApiServiceMock }
- ],
+ imports: [CoreTestingModule],
declarations: [TestComponent, LibraryFavoriteDirective]
});
fixture = TestBed.createComponent(TestComponent);
diff --git a/lib/content-services/src/lib/document-list/components/document-list.component.spec.ts b/lib/content-services/src/lib/document-list/components/document-list.component.spec.ts
index 9ee89ac841..00adf1b094 100644
--- a/lib/content-services/src/lib/document-list/components/document-list.component.spec.ts
+++ b/lib/content-services/src/lib/document-list/components/document-list.component.spec.ts
@@ -18,7 +18,6 @@
import { CUSTOM_ELEMENTS_SCHEMA, SimpleChange, QueryList, Component, ViewChild, SimpleChanges } from '@angular/core';
import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';
import {
- AlfrescoApiService,
DataColumnListComponent,
DataColumnComponent,
DataColumn,
@@ -27,7 +26,8 @@ import {
ObjectDataTableAdapter,
ShowHeaderMode,
ThumbnailService,
- AppConfigService
+ AppConfigService,
+ AuthenticationService
} from '@alfresco/adf-core';
import { ContentService } from '../../common/services/content.service';
import { Subject, of, throwError } from 'rxjs';
@@ -70,7 +70,6 @@ const mockDialog = {
describe('DocumentList', () => {
let documentList: DocumentListComponent;
let documentListService: DocumentListService;
- let apiService: AlfrescoApiService;
let customResourcesService: CustomResourcesService;
let thumbnailService: ThumbnailService;
let contentService: ContentService;
@@ -82,6 +81,7 @@ describe('DocumentList', () => {
let spyFavorite: any;
let spyFolder: any;
let spyFolderNode: any;
+ let authenticationService: AuthenticationService;
beforeEach(() => {
TestBed.configureTestingModule({
@@ -99,11 +99,11 @@ describe('DocumentList', () => {
documentList = fixture.componentInstance;
documentListService = TestBed.inject(DocumentListService);
- apiService = TestBed.inject(AlfrescoApiService);
customResourcesService = TestBed.inject(CustomResourcesService);
thumbnailService = TestBed.inject(ThumbnailService);
contentService = TestBed.inject(ContentService);
appConfigService = TestBed.inject(AppConfigService);
+ authenticationService = TestBed.inject(AuthenticationService);
spyFolder = spyOn(documentListService, 'getFolder').and.returnValue(of({ list: {} }));
spyFolderNode = spyOn(documentListService, 'getFolderNode').and.returnValue(of(new NodeEntry({ entry: new Node() })));
@@ -611,7 +611,7 @@ describe('DocumentList', () => {
title: 'FileAction'
});
- spyOn(apiService.getInstance(), 'getEcmUsername').and.returnValue('lockOwner');
+ spyOn(authenticationService, 'getEcmUsername').and.returnValue('lockOwner');
documentList.actions = [documentMenu];
@@ -642,7 +642,7 @@ describe('DocumentList', () => {
title: 'FileAction'
});
- spyOn(apiService.getInstance(), 'getEcmUsername').and.returnValue('jerryTheKillerCow');
+ spyOn(authenticationService, 'getEcmUsername').and.returnValue('jerryTheKillerCow');
documentList.actions = [documentMenu];
diff --git a/lib/content-services/src/lib/document-list/services/lock.service.spec.ts b/lib/content-services/src/lib/document-list/services/lock.service.spec.ts
index dce5fdd7d9..f42dbe0eee 100644
--- a/lib/content-services/src/lib/document-list/services/lock.service.spec.ts
+++ b/lib/content-services/src/lib/document-list/services/lock.service.spec.ts
@@ -17,7 +17,7 @@
import { TestBed } from '@angular/core/testing';
import { LockService } from './lock.service';
-import { CoreTestingModule, AlfrescoApiService } from '@alfresco/adf-core';
+import { CoreTestingModule, AuthenticationService } from '@alfresco/adf-core';
import { Node } from '@alfresco/js-api';
import { TranslateModule } from '@ngx-translate/core';
import { addDays, subDays } from 'date-fns';
@@ -25,7 +25,7 @@ import { addDays, subDays } from 'date-fns';
describe('PeopleProcessService', () => {
let service: LockService;
- let apiService: AlfrescoApiService;
+ let authenticationService: AuthenticationService;
const fakeNodeUnlocked: Node = { name: 'unlocked', isLocked: false, isFile: true } as Node;
const fakeFolderNode: Node = { name: 'unlocked', isLocked: false, isFile: false, isFolder: true } as Node;
@@ -39,7 +39,7 @@ describe('PeopleProcessService', () => {
]
});
service = TestBed.inject(LockService);
- apiService = TestBed.inject(AlfrescoApiService);
+ authenticationService = TestBed.inject(AuthenticationService);
});
it('should return false when no lock is configured', () => {
@@ -145,22 +145,22 @@ describe('PeopleProcessService', () => {
} as Node;
it('should return false when the user is the lock owner', () => {
- spyOn(apiService.getInstance(), 'getEcmUsername').and.returnValue('lock-owner-user');
+ spyOn(authenticationService, 'getEcmUsername').and.returnValue('lock-owner-user');
expect(service.isLocked(nodeOwnerAllowedLock)).toBeFalsy();
});
it('should return true when the user is not the lock owner', () => {
- spyOn(apiService.getInstance(), 'getEcmUsername').and.returnValue('banana-user');
+ spyOn(authenticationService, 'getEcmUsername').and.returnValue('banana-user');
expect(service.isLocked(nodeOwnerAllowedLock)).toBeTruthy();
});
it('should return false when the user is not the lock owner but the lock is expired', () => {
- spyOn(apiService.getInstance(), 'getEcmUsername').and.returnValue('banana-user');
+ spyOn(authenticationService, 'getEcmUsername').and.returnValue('banana-user');
expect(service.isLocked(nodeOwnerAllowedLockWithExpiredDate)).toBeFalsy();
});
it('should return true when is not the lock owner and the expiration date is valid', () => {
- spyOn(apiService.getInstance(), 'getEcmUsername').and.returnValue('banana-user');
+ spyOn(authenticationService, 'getEcmUsername').and.returnValue('banana-user');
expect(service.isLocked(nodeOwnerAllowedLockWithActiveExpiration)).toBeTruthy();
});
});
diff --git a/lib/content-services/src/lib/testing/content.testing.module.ts b/lib/content-services/src/lib/testing/content.testing.module.ts
index 2992be2150..e3a7c8bf54 100644
--- a/lib/content-services/src/lib/testing/content.testing.module.ts
+++ b/lib/content-services/src/lib/testing/content.testing.module.ts
@@ -27,7 +27,8 @@ import {
AlfrescoApiServiceMock,
AppConfigServiceMock,
TranslationMock,
- CookieServiceMock
+ CookieServiceMock,
+ AuthModule
} from '@alfresco/adf-core';
import { ContentModule } from '../content.module';
import { TranslateModule } from '@ngx-translate/core';
@@ -37,6 +38,7 @@ import { MatIconTestingModule } from '@angular/material/icon/testing';
@NgModule({
imports: [
+ AuthModule.forRoot({ useHash: true }),
NoopAnimationsModule,
RouterTestingModule,
TranslateModule,
diff --git a/lib/core/api/src/index.ts b/lib/core/api/src/index.ts
index d65fe15513..fe326e1e82 100644
--- a/lib/core/api/src/index.ts
+++ b/lib/core/api/src/index.ts
@@ -15,9 +15,6 @@
* limitations under the License.
*/
-export * from './lib/api-client.factory';
-export * from './lib/api-clients.service';
-export * from './lib/clients';
export * from './lib/types';
export * from './lib/adf-http-client.service';
export * from './lib/interfaces';
diff --git a/lib/core/api/src/lib/adf-http-client.service.spec.ts b/lib/core/api/src/lib/adf-http-client.service.spec.ts
index 09c9956263..1967958402 100644
--- a/lib/core/api/src/lib/adf-http-client.service.spec.ts
+++ b/lib/core/api/src/lib/adf-http-client.service.spec.ts
@@ -321,4 +321,86 @@ describe('AdfHttpClient', () => {
req.flush(null, { status: 200, statusText: 'Ok' });
});
+ it('should set Content-type to multipart/form-data if contentTypes array contains only multipart/form-data element', () => {
+ const options: RequestOptions = {
+ path: '',
+ httpMethod: 'POST',
+ contentTypes: ['multipart/form-data'],
+ queryParams: {
+ lastModifiedFrom: new Date('2022-08-17T00:00:00.000Z')
+ }
+ };
+
+ angularHttpClient.request('http://example.com', options, securityOptions, emitters).catch(error =>
+ fail(error)
+ );
+
+ const req = controller.expectOne('http://example.com?lastModifiedFrom=2022-08-17T00%3A00%3A00.000Z');
+
+ expect(req.request.headers.get('Content-Type')).toEqual('multipart/form-data');
+
+ req.flush(null, { status: 200, statusText: 'Ok' });
+ });
+
+ it('should set Content-type header to application/json if contentTypes array contains application/json', () => {
+ const options: RequestOptions = {
+ path: '',
+ httpMethod: 'POST',
+ contentTypes: ['multipart/form-data', 'application/json'],
+ queryParams: {
+ lastModifiedFrom: new Date('2022-08-17T00:00:00.000Z')
+ }
+ };
+
+ angularHttpClient.request('http://example.com', options, securityOptions, emitters).catch(error =>
+ fail(error)
+ );
+
+ const req = controller.expectOne('http://example.com?lastModifiedFrom=2022-08-17T00%3A00%3A00.000Z');
+
+ expect(req.request.headers.get('Content-Type')).toEqual('application/json');
+
+ req.flush(null, { status: 200, statusText: 'Ok' });
+ });
+
+ it('should set Content-type to application/json if contentTypes is not passed to the request options', () => {
+ const options: RequestOptions = {
+ path: '',
+ httpMethod: 'POST',
+ queryParams: {
+ lastModifiedFrom: new Date('2022-08-17T00:00:00.000Z')
+ }
+ };
+
+ angularHttpClient.request('http://example.com', options, securityOptions, emitters).catch(error =>
+ fail(error)
+ );
+
+ const req = controller.expectOne('http://example.com?lastModifiedFrom=2022-08-17T00%3A00%3A00.000Z');
+
+ expect(req.request.headers.get('Content-Type')).toEqual('application/json');
+
+ req.flush(null, { status: 200, statusText: 'Ok' });
+ });
+
+ it('should set Accept header to application/json if accepts is not passed to the request options', () => {
+ const options: RequestOptions = {
+ path: '',
+ httpMethod: 'POST',
+ queryParams: {
+ lastModifiedFrom: new Date('2022-08-17T00:00:00.000Z')
+ }
+ };
+
+ angularHttpClient.request('http://example.com', options, securityOptions, emitters).catch(error =>
+ fail(error)
+ );
+
+ const req = controller.expectOne('http://example.com?lastModifiedFrom=2022-08-17T00%3A00%3A00.000Z');
+
+ expect(req.request.headers.get('Accept')).toEqual('application/json');
+
+ req.flush(null, { status: 200, statusText: 'Ok' });
+ });
+
});
diff --git a/lib/core/api/src/lib/adf-http-client.service.ts b/lib/core/api/src/lib/adf-http-client.service.ts
index 973bdaf333..f000839817 100644
--- a/lib/core/api/src/lib/adf-http-client.service.ts
+++ b/lib/core/api/src/lib/adf-http-client.service.ts
@@ -57,17 +57,10 @@ export class AdfHttpClient implements ee.Emitter,JsApiHttpClient {
on: ee.EmitterMethod;
off: ee.EmitterMethod;
once: ee.EmitterMethod;
+ _disableCsrf: boolean;
+
emit: (type: string, ...args: any[]) => void;
- private _disableCsrf = false;
-
- private defaultSecurityOptions = {
- withCredentials: true,
- isBpmRequest: false,
- authentications: {},
- defaultHeaders: {}
- };
-
get disableCsrf(): boolean {
return this._disableCsrf;
}
@@ -76,8 +69,14 @@ export class AdfHttpClient implements ee.Emitter,JsApiHttpClient {
this._disableCsrf = disableCsrf;
}
- constructor(private httpClient: HttpClient
- ) {
+ private defaultSecurityOptions = {
+ withCredentials: true,
+ isBpmRequest: false,
+ authentications: {},
+ defaultHeaders: {}
+ };
+
+ constructor(private httpClient: HttpClient) {
ee(this);
}
@@ -217,7 +216,7 @@ export class AdfHttpClient implements ee.Emitter,JsApiHttpClient {
}
eventEmitter.emit('error', err);
- apiClientEmitter.emit('error', err);
+ apiClientEmitter.emit('error', { ...err, response: { req: err } });
if (err.status === 401) {
eventEmitter.emit('unauthorized');
@@ -232,10 +231,10 @@ export class AdfHttpClient implements ee.Emitter,JsApiHttpClient {
// for backwards compatibility to handle cases in code where we try read response.error.response.body;
const error = {
- response: {...err, body: err.error}
+ ...err, body: err.error
};
- const alfrescoApiError = new AlfrescoApiResponseError(msg, err.status, error.response);
+ const alfrescoApiError = new AlfrescoApiResponseError(msg, err.status, error);
return throwError(alfrescoApiError);
}),
takeUntil(abort$)
@@ -252,7 +251,7 @@ export class AdfHttpClient implements ee.Emitter,JsApiHttpClient {
}
private static getBody(options: RequestOptions): any {
- const contentType = options.contentType;
+ const contentType = options.contentType ? options.contentType : AdfHttpClient.jsonPreferredMime(options.contentTypes);
const isFormData = contentType === 'multipart/form-data';
const isFormUrlEncoded = contentType === 'application/x-www-form-urlencoded';
const body = options.bodyParam;
@@ -269,20 +268,58 @@ export class AdfHttpClient implements ee.Emitter,JsApiHttpClient {
}
private getHeaders(options: RequestOptions): HttpHeaders {
+ const contentType = options.contentType || AdfHttpClient.jsonPreferredMime(options.contentTypes);
+ const accept = options.accept || AdfHttpClient.jsonPreferredMime(options.accepts);
+
const optionsHeaders = {
...options.headerParams,
- ...(options.accept && {Accept: options.accept}),
- ...((options.contentType) && {'Content-Type': options.contentType})
+ ...(accept && {Accept: accept}),
+ ...((contentType) && {'Content-Type': contentType})
};
if (!this.disableCsrf) {
this.setCsrfToken(optionsHeaders);
-
}
return new HttpHeaders(optionsHeaders);
}
+ /**
+ * Chooses a content type from the given array, with JSON preferred; i.e. return JSON if included, otherwise return the first.
+ *
+ * @param contentTypes a contentType array
+ * @returns The chosen content type, preferring JSON.
+ */
+ private static jsonPreferredMime(contentTypes: readonly string[]): string {
+ if (!contentTypes?.length) {
+ return 'application/json';
+ }
+
+ for (let i = 0; i < contentTypes.length; i++) {
+ if (AdfHttpClient.isJsonMime(contentTypes[i])) {
+ return contentTypes[i];
+ }
+ }
+ return contentTypes[0];
+ }
+
+ /**
+ * Checks whether the given content type represents JSON.
+ * JSON content type examples:
+ *
+ * - application/json
+ * - application/json; charset=UTF8
+ * - APPLICATION/JSON
+ *
+ *
+ * @param contentType The MIME content type to check.
+ * @returns true
if contentType
represents JSON, otherwise false
.
+ */
+ private static isJsonMime(contentType: string): boolean {
+ return Boolean(contentType?.match(/^application\/json(;.*)?$/i));
+ }
+
+
private setCsrfToken(optionsHeaders: any) {
const token = this.createCSRFToken();
optionsHeaders['X-CSRF-TOKEN'] = token;
diff --git a/lib/core/api/src/lib/api-client.factory.ts b/lib/core/api/src/lib/api-client.factory.ts
deleted file mode 100644
index 6ddd1f8aef..0000000000
--- a/lib/core/api/src/lib/api-client.factory.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-/*!
- * @license
- * Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { InjectionToken } from '@angular/core';
-import { Constructor } from './types';
-
-export interface ApiClientFactory {
- create(apiClass: Constructor): T;
-}
-
-export const API_CLIENT_FACTORY_TOKEN = new InjectionToken('api-client-factory');
diff --git a/lib/core/api/src/lib/api-clients.service.spec.ts b/lib/core/api/src/lib/api-clients.service.spec.ts
deleted file mode 100644
index c240b5db6b..0000000000
--- a/lib/core/api/src/lib/api-clients.service.spec.ts
+++ /dev/null
@@ -1,66 +0,0 @@
-/*!
- * @license
- * Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { AboutApi } from '@alfresco/js-api';
-import { TestBed } from '@angular/core/testing';
-import { ApiClientFactory, API_CLIENT_FACTORY_TOKEN } from './api-client.factory';
-import { ApiClientsService } from './api-clients.service';
-import { Constructor } from './types';
-
-class MockApiClientFactory implements ApiClientFactory {
- create(apiClass: Constructor): T {
- return new apiClass();
- }
-}
-
-describe('ApiService', () => {
- let apiService: ApiClientsService;
-
- beforeEach(() => {
- TestBed.configureTestingModule({
- providers: [
- ApiClientsService,
- { provide: API_CLIENT_FACTORY_TOKEN, useClass: MockApiClientFactory }
- ]
- });
- apiService = TestBed.inject(ApiClientsService);
- });
-
- it('should add api to registry', () => {
- apiService.register('ActivitiClient.about', AboutApi);
-
- expect(apiService.get('ActivitiClient.about') instanceof AboutApi).toBeTruthy();
- });
-
- it('should throw error if we try to get unregisterd API', () => {
- expect(() => apiService.get('ActivitiClient.about')).toThrowError();
-
- apiService.register('ActivitiClient.about', AboutApi);
-
- expect(() => apiService.get('ActivitiClient.about')).not.toThrowError();
- });
-
- it('should create only single instance of API', () => {
- apiService.register('ActivitiClient.about', AboutApi);
-
- const a = apiService.get('ActivitiClient.about');
- const b = apiService.get('ActivitiClient.about');
-
- expect(a).toBe(b);
- });
-
-});
diff --git a/lib/core/api/src/lib/api-clients.service.ts b/lib/core/api/src/lib/api-clients.service.ts
deleted file mode 100644
index 3c01fc56fb..0000000000
--- a/lib/core/api/src/lib/api-clients.service.ts
+++ /dev/null
@@ -1,66 +0,0 @@
-/*!
- * @license
- * Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { Inject, Injectable } from '@angular/core';
-import { ApiClientFactory, API_CLIENT_FACTORY_TOKEN } from './api-client.factory';
-import { Constructor, Dictionary } from './types';
-
-/* eslint-disable */
-
-declare global {
- // eslint-disable-next-line @typescript-eslint/no-namespace
- namespace AlfrescoCore {
- interface ApiRegistry {
- }
- }
-}
-/* eslint-enable */
-
-@Injectable()
-export class ApiClientsService {
-
- constructor(@Inject(API_CLIENT_FACTORY_TOKEN) private apiCreateFactory: ApiClientFactory) {
- }
-
- private registry: Dictionary> = {};
- private instances: Partial = {};
-
- get(apiName: T): AlfrescoCore.ApiRegistry[T] {
-
- const apiClass = this.registry[apiName];
-
- if (!apiClass) {
- throw new Error(`Api not registred: ${apiName}`);
- }
-
- return this.instances[apiName] as AlfrescoCore.ApiRegistry[T] ?? this.instantiateApi(apiName);
- }
-
-
- register(apiName: T, api: Constructor): void {
- this.registry[apiName] = api;
- }
-
- private instantiateApi(apiName: T): AlfrescoCore.ApiRegistry[T] {
- const apiClass = this.registry[apiName];
- const instance = this.apiCreateFactory.create(apiClass);
- this.instances[apiName] = instance;
-
- return instance;
- }
-}
-
diff --git a/lib/core/api/src/lib/clients/activiti/activiti-client.module.ts b/lib/core/api/src/lib/clients/activiti/activiti-client.module.ts
deleted file mode 100644
index f906aafdcd..0000000000
--- a/lib/core/api/src/lib/clients/activiti/activiti-client.module.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-/*!
- * @license
- * Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { AboutApi, SystemPropertiesApi } from '@alfresco/js-api';
-import { NgModule } from '@angular/core';
-import { ApiClientsService } from '../../api-clients.service';
-
-@NgModule()
-export class ActivitiClientModule {
- constructor(private apiClientsService: ApiClientsService) {
- this.apiClientsService.register('ActivitiClient.about', AboutApi);
- this.apiClientsService.register('ActivitiClient.system-properties', SystemPropertiesApi);
- }
-}
diff --git a/lib/core/api/src/lib/clients/activiti/activiti-client.types.ts b/lib/core/api/src/lib/clients/activiti/activiti-client.types.ts
deleted file mode 100644
index 4a768c6c28..0000000000
--- a/lib/core/api/src/lib/clients/activiti/activiti-client.types.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-/*!
- * @license
- * Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { AboutApi, SystemPropertiesApi } from '@alfresco/js-api';
-
-declare global {
- // eslint-disable-next-line @typescript-eslint/no-namespace
- namespace AlfrescoCore {
- interface ApiRegistry {
- ['ActivitiClient.about']: AboutApi;
- ['ActivitiClient.system-properties']: SystemPropertiesApi;
- }
- }
-}
-
diff --git a/lib/core/api/src/lib/clients/alfresco-js-clients.module.ts b/lib/core/api/src/lib/clients/alfresco-js-clients.module.ts
deleted file mode 100644
index cf0ce91346..0000000000
--- a/lib/core/api/src/lib/clients/alfresco-js-clients.module.ts
+++ /dev/null
@@ -1,38 +0,0 @@
-/*!
- * @license
- * Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { HttpClientModule, HttpClientXsrfModule } from '@angular/common/http';
-import { NgModule } from '@angular/core';
-import { ApiClientsService } from '../api-clients.service';
-import { ActivitiClientModule } from './activiti/activiti-client.module';
-import { DiscoveryClientModule } from './discovery/discovery-client.module';
-
-@NgModule({
- imports: [
- HttpClientModule,
- HttpClientXsrfModule.withOptions({
- cookieName: 'CSRF-TOKEN',
- headerName: 'X-CSRF-TOKEN'
- }),
- ActivitiClientModule,
- DiscoveryClientModule
- ],
- providers: [
- ApiClientsService
- ]
-})
-export class AlfrescoJsClientsModule { }
diff --git a/lib/core/api/src/lib/clients/discovery/discovery-client.module.ts b/lib/core/api/src/lib/clients/discovery/discovery-client.module.ts
deleted file mode 100644
index 242e09620b..0000000000
--- a/lib/core/api/src/lib/clients/discovery/discovery-client.module.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-/*!
- * @license
- * Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { DiscoveryApi } from '@alfresco/js-api';
-import { NgModule } from '@angular/core';
-import { ApiClientsService } from '../../api-clients.service';
-
-@NgModule()
-export class DiscoveryClientModule {
- constructor(private apiClientsService: ApiClientsService) {
- this.apiClientsService.register('DiscoveryClient.discovery', DiscoveryApi);
- }
-}
diff --git a/lib/core/api/src/lib/clients/discovery/discovery-client.types.ts b/lib/core/api/src/lib/clients/discovery/discovery-client.types.ts
deleted file mode 100644
index 64c7f6e52b..0000000000
--- a/lib/core/api/src/lib/clients/discovery/discovery-client.types.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-/*!
- * @license
- * Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { DiscoveryApi } from '@alfresco/js-api';
-
-declare global {
- // eslint-disable-next-line @typescript-eslint/no-namespace
- namespace AlfrescoCore {
- interface ApiRegistry {
- ['DiscoveryClient.discovery']: DiscoveryApi;
- }
- }
-}
diff --git a/lib/core/api/src/lib/interfaces.ts b/lib/core/api/src/lib/interfaces.ts
index a4e3d043e3..de95500bc7 100644
--- a/lib/core/api/src/lib/interfaces.ts
+++ b/lib/core/api/src/lib/interfaces.ts
@@ -15,22 +15,41 @@
* limitations under the License.
*/
+export interface SecurityOptions {
+ readonly withCredentials?: boolean;
+ readonly authentications?: Authentication;
+ readonly defaultHeaders?: Record;
+}
+
+export interface Oauth2 {
+ refreshToken?: string;
+ accessToken?: string;
+}
+
+export interface BasicAuth {
+ username?: string;
+ password?: string;
+ ticket?: string;
+}
+
+export interface Authentication {
+ basicAuth?: BasicAuth;
+ oauth2?: Oauth2;
+ cookie?: string;
+ type?: string;
+}
+
export interface RequestOptions {
httpMethod?: string;
+ pathParams?: any;
queryParams?: any;
headerParams?: any;
formParams?: any;
bodyParam?: any;
returnType?: any;
responseType?: string;
+ accepts?: string[];
+ contentTypes?: string[];
readonly accept?: string;
readonly contentType?: string;
}
-
-export interface SecurityOptions {
- readonly isBpmRequest: boolean;
- readonly enableCsrf?: boolean;
- readonly withCredentials?: boolean;
- readonly authentications: any;
- readonly defaultHeaders: Record;
-}
diff --git a/lib/core/auth/src/authentication-interceptor/authentication.interceptor.spec.ts b/lib/core/auth/src/authentication-interceptor/authentication.interceptor.spec.ts
index 18bb81aba6..07d0bdbe79 100644
--- a/lib/core/auth/src/authentication-interceptor/authentication.interceptor.spec.ts
+++ b/lib/core/auth/src/authentication-interceptor/authentication.interceptor.spec.ts
@@ -22,7 +22,7 @@ import { Authentication } from '../authentication';
import { AuthenticationInterceptor, SHOULD_ADD_AUTH_TOKEN } from './authentication.interceptor';
class MockAuthentication extends Authentication {
- addTokenToHeader(httpHeaders: HttpHeaders): Observable {
+ addTokenToHeader(_: string, httpHeaders: HttpHeaders): Observable {
return of(httpHeaders);
}
}
diff --git a/lib/core/auth/src/authentication-interceptor/authentication.interceptor.ts b/lib/core/auth/src/authentication-interceptor/authentication.interceptor.ts
index 94be3ade92..8e194eb40b 100644
--- a/lib/core/auth/src/authentication-interceptor/authentication.interceptor.ts
+++ b/lib/core/auth/src/authentication-interceptor/authentication.interceptor.ts
@@ -43,7 +43,7 @@ export class AuthenticationInterceptor implements HttpInterceptor {
Observable | HttpUserEvent> {
if (req.context.get(SHOULD_ADD_AUTH_TOKEN)) {
- return this.authService.addTokenToHeader(req.headers).pipe(
+ return this.authService.addTokenToHeader(req.url, req.headers).pipe(
mergeMap((headersWithBearer) => {
const headerWithContentType = this.appendJsonContentType(headersWithBearer);
const kcReq = req.clone({ headers: headerWithContentType});
diff --git a/lib/core/auth/src/authentication.ts b/lib/core/auth/src/authentication.ts
index 05bf002682..87da959e1a 100644
--- a/lib/core/auth/src/authentication.ts
+++ b/lib/core/auth/src/authentication.ts
@@ -19,5 +19,5 @@ import { HttpHeaders } from '@angular/common/http';
import { Observable } from 'rxjs';
export abstract class Authentication {
- public abstract addTokenToHeader(headers: HttpHeaders): Observable;
+ public abstract addTokenToHeader(requestUrl: string, headers: HttpHeaders): Observable;
}
diff --git a/lib/core/src/lib/api-factories/alfresco-api-v2-loader.service.ts b/lib/core/src/lib/api-factories/alfresco-api-v2-loader.service.ts
index 836c53d9b1..7c8b653102 100644
--- a/lib/core/src/lib/api-factories/alfresco-api-v2-loader.service.ts
+++ b/lib/core/src/lib/api-factories/alfresco-api-v2-loader.service.ts
@@ -19,6 +19,8 @@ import { AlfrescoApiConfig } from '@alfresco/js-api';
import { Injectable } from '@angular/core';
import { AppConfigService, AppConfigValues } from '../app-config/app-config.service';
import { AlfrescoApiService } from '../services/alfresco-api.service';
+import { StorageService } from '../common/services/storage.service';
+import { AuthenticationService, BasicAlfrescoAuthService } from '../auth';
/**
* Create a factory to resolve an api service instance
@@ -34,10 +36,22 @@ export function createAlfrescoApiInstance(angularAlfrescoApiService: AlfrescoApi
providedIn: 'root'
})
export class AlfrescoApiLoaderService {
- constructor(private readonly appConfig: AppConfigService, private readonly apiService: AlfrescoApiService) {}
+ constructor(private readonly appConfig: AppConfigService,
+ private readonly apiService: AlfrescoApiService,
+ private readonly basicAlfrescoAuthService: BasicAlfrescoAuthService,
+ private readonly authService: AuthenticationService,
+ private storageService: StorageService) {
+ }
async init(): Promise {
await this.appConfig.load();
+
+ this.authService.onLogin.subscribe(async () => {
+ if (this.authService.isOauth() && (this.authService.isALLProvider() || this.authService.isECMProvider())) {
+ await this.basicAlfrescoAuthService.requireAlfTicket();
+ }
+ });
+
return this.initAngularAlfrescoApi();
}
@@ -59,6 +73,8 @@ export class AlfrescoApiLoaderService {
disableCsrf: this.appConfig.get(AppConfigValues.DISABLECSRF),
withCredentials: this.appConfig.get(AppConfigValues.AUTH_WITH_CREDENTIALS, false),
domainPrefix: this.appConfig.get(AppConfigValues.STORAGE_PREFIX),
+ ticketEcm: this.storageService.getItem(AppConfigValues.CONTENT_TICKET_STORAGE_LABEL),
+ ticketBpm: this.storageService.getItem(AppConfigValues.PROCESS_TICKET_STORAGE_LABEL),
oauth2: oauth
});
diff --git a/lib/core/src/lib/api-factories/legacy-api-client.factory.ts b/lib/core/src/lib/api-factories/legacy-api-client.factory.ts
deleted file mode 100644
index 7f87468678..0000000000
--- a/lib/core/src/lib/api-factories/legacy-api-client.factory.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-/*!
- * @license
- * Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { ApiClientFactory, Constructor } from '@alfresco/adf-core/api';
-import { Injectable } from '@angular/core';
-import { AlfrescoApiService } from '../services/alfresco-api.service';
-
-@Injectable()
-export class LegacyClientFactory implements ApiClientFactory {
- constructor(private alfrescoApiService: AlfrescoApiService) { }
-
- create(apiClass: Constructor): T {
- return new apiClass(this.alfrescoApiService.getInstance());
- }
-}
diff --git a/lib/core/src/lib/api-factories/legacy-api-client.module.ts b/lib/core/src/lib/api-factories/legacy-api-client.module.ts
deleted file mode 100644
index e645fff543..0000000000
--- a/lib/core/src/lib/api-factories/legacy-api-client.module.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-/*!
- * @license
- * Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { NgModule } from '@angular/core';
-import { API_CLIENT_FACTORY_TOKEN } from '@alfresco/adf-core/api';
-import { LegacyClientFactory } from './legacy-api-client.factory';
-
-
-@NgModule({
- providers: [
- { provide: API_CLIENT_FACTORY_TOKEN, useClass: LegacyClientFactory }
- ]
-})
-export class LegacyApiClientModule { }
diff --git a/lib/core/src/lib/app-config/app-config.loader.ts b/lib/core/src/lib/app-config/app-config.loader.ts
index d04bde43e1..11dde868f7 100644
--- a/lib/core/src/lib/app-config/app-config.loader.ts
+++ b/lib/core/src/lib/app-config/app-config.loader.ts
@@ -28,8 +28,14 @@ import { AdfHttpClient } from '@alfresco/adf-core/api';
* @returns factory function
*/
export function loadAppConfig(appConfigService: AppConfigService, storageService: StorageService, adfHttpClient: AdfHttpClient) {
- return () => appConfigService.load().then(() => {
+
+ const init = () => {
adfHttpClient.disableCsrf = appConfigService.get(AppConfigValues.DISABLECSRF, true);
storageService.prefix = appConfigService.get(AppConfigValues.STORAGE_PREFIX, '');
- });
-}
+
+ appConfigService.select(AppConfigValues.STORAGE_PREFIX).subscribe((property) => {
+ storageService.prefix = property;
+ });
+ };
+ return () => appConfigService.load(init);
+};
diff --git a/lib/core/src/lib/app-config/app-config.service.spec.ts b/lib/core/src/lib/app-config/app-config.service.spec.ts
index d7352fba3e..a6b045da5f 100644
--- a/lib/core/src/lib/app-config/app-config.service.spec.ts
+++ b/lib/core/src/lib/app-config/app-config.service.spec.ts
@@ -188,4 +188,14 @@ describe('AppConfigService', () => {
expect(appConfigService.get('files.excluded')[0]).toBe('excluded');
});
+
+ it('should execute callback function if is passed to the load method', async () => {
+ const fakeCallBack = jasmine.createSpy('fakeCallBack');
+ fakeCallBack.and.returnValue(()=>{});
+
+ await appConfigService.load(fakeCallBack);
+
+ expect(fakeCallBack).toHaveBeenCalled();
+ });
+
});
diff --git a/lib/core/src/lib/app-config/app-config.service.ts b/lib/core/src/lib/app-config/app-config.service.ts
index b272bcc50f..314522a5ad 100644
--- a/lib/core/src/lib/app-config/app-config.service.ts
+++ b/lib/core/src/lib/app-config/app-config.service.ts
@@ -18,13 +18,14 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { ObjectUtils } from '../common/utils/object-utils';
-import { Observable, Subject } from 'rxjs';
+import { Observable, ReplaySubject } 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
export enum AppConfigValues {
APP_CONFIG_LANGUAGES_KEY = 'languages',
@@ -44,7 +45,9 @@ export enum AppConfigValues {
AUTH_WITH_CREDENTIALS = 'auth.withCredentials',
APPLICATION = 'application',
STORAGE_PREFIX = 'application.storagePrefix',
- NOTIFY_DURATION = 'notificationDefaultDuration'
+ NOTIFY_DURATION = 'notificationDefaultDuration',
+ CONTENT_TICKET_STORAGE_LABEL = 'ticket-ECM',
+ PROCESS_TICKET_STORAGE_LABEL = 'ticket-BPM'
}
// eslint-disable-next-line no-shadow
@@ -71,11 +74,15 @@ export class AppConfigService {
};
status: Status = Status.INIT;
- protected onLoadSubject: Subject;
+ protected onLoadSubject: ReplaySubject;
onLoad: Observable;
+ get isLoaded() {
+ return this.status === Status.LOADED;
+ }
+
constructor(protected http: HttpClient, protected extensionService: ExtensionService) {
- this.onLoadSubject = new Subject();
+ this.onLoadSubject = new ReplaySubject();
this.onLoad = this.onLoadSubject.asObservable();
extensionService.setup$.subscribe((config) => {
@@ -92,7 +99,7 @@ export class AppConfigService {
select(property: string): Observable {
return this.onLoadSubject
.pipe(
- map((config) => config[property]),
+ map((config) => ObjectUtils.getValue(config, property)),
distinctUntilChanged()
);
}
@@ -160,8 +167,7 @@ export class AppConfigService {
this.onLoadSubject.next(this.config);
}
- protected onDataLoaded(data: any) {
- this.config = Object.assign({}, this.config, data || {});
+ protected onDataLoaded() {
this.onLoadSubject.next(this.config);
this.extensionService.setup$
@@ -182,9 +188,10 @@ export class AppConfigService {
/**
* Loads the config file.
*
+ * @param callback an optional callback to execute when configuration is loaded
* @returns Notification when loading is complete
*/
- load(): Promise {
+ load(callback?: (...args: any[]) => any): Promise {
return new Promise((resolve) => {
const configUrl = `app.config.json?v=${Date.now()}`;
@@ -193,8 +200,10 @@ export class AppConfigService {
this.http.get(configUrl).subscribe(
(data: any) => {
this.status = Status.LOADED;
+ this.config = Object.assign({}, this.config, data || {});
+ callback?.();
resolve(data);
- this.onDataLoaded(data);
+ this.onDataLoaded();
},
() => {
// eslint-disable-next-line no-console
@@ -227,6 +236,8 @@ export class AppConfigService {
resolve(res);
},
error: (err: any) => {
+ // eslint-disable-next-line no-console
+ console.error('hostIdp not correctly configured or unreachable');
reject(err);
}
});
@@ -262,4 +273,5 @@ export class AppConfigService {
return result;
}
+
}
diff --git a/lib/core/src/lib/auth/authentication-interceptor/auth-bearer.interceptor.spec.ts b/lib/core/src/lib/auth/authentication-interceptor/auth-bearer.interceptor.spec.ts
index 29145608a7..d2f252eb51 100644
--- a/lib/core/src/lib/auth/authentication-interceptor/auth-bearer.interceptor.spec.ts
+++ b/lib/core/src/lib/auth/authentication-interceptor/auth-bearer.interceptor.spec.ts
@@ -17,9 +17,10 @@
import { HttpClient, HttpHandler, HttpRequest } from '@angular/common/http';
import { TestBed } from '@angular/core/testing';
-import { Observable, of } from 'rxjs';
+import { EMPTY, Observable, of } from 'rxjs';
import { AuthBearerInterceptor } from './auth-bearer.interceptor';
import { AuthenticationService } from '../services/authentication.service';
+import { RedirectAuthService } from '../oidc/redirect-auth.service';
const mockNext: HttpHandler = {
handle: () => new Observable(subscriber => {
@@ -40,7 +41,8 @@ describe('AuthBearerInterceptor', () => {
HttpClient,
HttpHandler,
AuthBearerInterceptor,
- AuthenticationService
+ AuthenticationService,
+ { provide: RedirectAuthService, useValue: { onLogin: EMPTY } }
]
});
@@ -85,7 +87,7 @@ describe('AuthBearerInterceptor', () => {
});
it('should interceptor add auth token to every URL if excluded URLs array is empty', () => {
- spyOn(authService, 'getBearerExcludedUrls').and.returnValue([]);
+ spyOnProperty(interceptor, 'bearerExcludedUrls').and.returnValue([]);
const mockUrls = [
'http://example.com/auth/realms/testpath',
diff --git a/lib/core/src/lib/auth/authentication-interceptor/auth-bearer.interceptor.ts b/lib/core/src/lib/auth/authentication-interceptor/auth-bearer.interceptor.ts
index afb8c0b4be..e89309b807 100644
--- a/lib/core/src/lib/auth/authentication-interceptor/auth-bearer.interceptor.ts
+++ b/lib/core/src/lib/auth/authentication-interceptor/auth-bearer.interceptor.ts
@@ -16,40 +16,37 @@
*/
import { throwError as observableThrowError, Observable } from 'rxjs';
-import { Injectable, Injector } from '@angular/core';
+import { Injectable } from '@angular/core';
import {
HttpHandler, HttpInterceptor, HttpRequest,
HttpSentEvent, HttpHeaderResponse, HttpProgressEvent, HttpResponse, HttpUserEvent, HttpHeaders
} from '@angular/common/http';
-import { AuthenticationService } from '../services/authentication.service';
import { catchError, mergeMap } from 'rxjs/operators';
+import { AuthenticationService } from '../services/authentication.service';
@Injectable()
export class AuthBearerInterceptor implements HttpInterceptor {
- private excludedUrlsRegex: RegExp[];
+ private _bearerExcludedUrls: readonly string[] = ['resources/', 'assets/', 'auth/realms', 'idp/'];
- constructor(private injector: Injector, private authService: AuthenticationService) { }
+ private excludedUrlsRegex: RegExp[];
+
+ constructor(private authenticationService: AuthenticationService) { }
private loadExcludedUrlsRegex() {
- const excludedUrls = this.authService.getBearerExcludedUrls();
+ const excludedUrls = this.bearerExcludedUrls;
this.excludedUrlsRegex = excludedUrls.map((urlPattern) => new RegExp(`^https?://[^/]+/${urlPattern}`, 'i')) || [];
}
intercept(req: HttpRequest, next: HttpHandler):
Observable | HttpUserEvent> {
- this.authService = this.injector.get(AuthenticationService);
-
- if (!this.authService?.getBearerExcludedUrls()) {
- return next.handle(req);
- }
if (!this.excludedUrlsRegex) {
this.loadExcludedUrlsRegex();
}
- const urlRequest = req.url;
- const shallPass: boolean = this.excludedUrlsRegex.some((regex) => regex.test(urlRequest));
+ const requestUrl = req.url;
+ const shallPass: boolean = this.excludedUrlsRegex.some((regex) => regex.test(requestUrl));
if (shallPass) {
return next.handle(req)
.pipe(
@@ -57,10 +54,10 @@ export class AuthBearerInterceptor implements HttpInterceptor {
);
}
- return this.authService.addTokenToHeader(req.headers)
+ return this.authenticationService.addTokenToHeader(requestUrl, req.headers)
.pipe(
mergeMap((headersWithBearer) => {
- const headerWithContentType = this.appendJsonContentType(headersWithBearer);
+ const headerWithContentType = this.appendJsonContentType(headersWithBearer, req.body);
const kcReq = req.clone({ headers: headerWithContentType});
return next.handle(kcReq)
.pipe(
@@ -70,7 +67,7 @@ export class AuthBearerInterceptor implements HttpInterceptor {
);
}
- private appendJsonContentType(headers: HttpHeaders): HttpHeaders {
+ private appendJsonContentType(headers: HttpHeaders, reqBody: any): HttpHeaders {
// prevent adding any content type, to properly handle formData with boundary browser generated value,
// as adding any Content-Type its going to break the upload functionality
@@ -79,11 +76,15 @@ export class AuthBearerInterceptor implements HttpInterceptor {
return headers.delete('Content-Type');
}
- if (!headers.get('Content-Type')) {
+ if (!headers.get('Content-Type') && !(reqBody instanceof FormData)) {
return headers.set('Content-Type', 'application/json;charset=UTF-8');
}
return headers;
}
+ protected get bearerExcludedUrls(): readonly string[] {
+ return this._bearerExcludedUrls;
+ }
+
}
diff --git a/lib/core/src/lib/auth/basic-auth/basic-alfresco-auth.service.ts b/lib/core/src/lib/auth/basic-auth/basic-alfresco-auth.service.ts
new file mode 100644
index 0000000000..0808fa31e0
--- /dev/null
+++ b/lib/core/src/lib/auth/basic-auth/basic-alfresco-auth.service.ts
@@ -0,0 +1,374 @@
+/*!
+ * @license
+ * Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { Injectable } from '@angular/core';
+import { AppConfigService, AppConfigValues } from '../../app-config/app-config.service';
+import { Authentication } from '../interfaces/authentication.interface';
+import { CookieService } from '../../common/services/cookie.service';
+import { ContentAuth } from './content-auth';
+import { ProcessAuth } from './process-auth';
+import { catchError, map } from 'rxjs/operators';
+import { from, Observable } from 'rxjs';
+import { RedirectionModel } from '../models/redirection.model';
+import { BaseAuthenticationService } from '../services/base-authentication.service';
+import { LogService } from '../../common';
+import { HttpHeaders } from '@angular/common/http';
+
+const REMEMBER_ME_COOKIE_KEY = 'ALFRESCO_REMEMBER_ME';
+const REMEMBER_ME_UNTIL = 1000 * 60 * 60 * 24 * 30;
+
+@Injectable({
+ providedIn: 'root'
+})
+export class BasicAlfrescoAuthService extends BaseAuthenticationService {
+
+ protected redirectUrl: RedirectionModel = null;
+
+ authentications: Authentication = {
+ basicAuth: {
+ ticket: ''
+ },
+ type: 'basic'
+ };
+
+ constructor(
+ logService: LogService,
+ appConfig: AppConfigService,
+ cookie: CookieService,
+ private contentAuth: ContentAuth,
+ private processAuth: ProcessAuth
+ ) {
+ super(appConfig, cookie, logService);
+
+ this.appConfig.onLoad
+ .subscribe(() => {
+ if (!this.isOauth() && this.isLoggedIn()) {
+ this.onLogin.next('logged-in');
+ }
+ });
+
+ this.contentAuth.onLogout.pipe(map((event) => {
+ this.onLogout.next(event);
+ }));
+ this.contentAuth.onLogin.pipe(map((event) => {
+ this.onLogin.next(event);
+ }));
+ this.contentAuth.onError.pipe(map((event) => {
+ this.onError.next(event);
+ }));
+ this.processAuth.onLogout.pipe(map((event) => {
+ this.onLogout.next(event);
+ }));
+ this.processAuth.onLogin.pipe(map((event) => {
+ this.onLogin.next(event);
+ }));
+ this.processAuth.onError.pipe(map((event) => {
+ this.onError.next(event);
+ }));
+ }
+
+ /**
+ * Logs the user in.
+ *
+ * @param username Username for the login
+ * @param password Password for the login
+ * @param rememberMe Stores the user's login details if true
+ * @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 }> {
+ return from(this.executeLogin(username, password)).pipe(
+ map((response: any) => {
+ this.saveRememberMeCookie(rememberMe);
+ this.onLogin.next(response);
+ return {
+ type: this.appConfig.get(AppConfigValues.PROVIDERS),
+ ticket: response
+ };
+ }),
+ catchError((err) => this.handleError(err))
+ );
+ }
+
+ /**
+ * login Alfresco API
+ *
+ * @param username username to login
+ * @param password password to login
+ * @returns A promise that returns {new authentication ticket} if resolved and {error} if rejected.
+ */
+ async executeLogin(username: string, password: string): Promise {
+ if (!this.isCredentialValid(username) || !this.isCredentialValid(password)) {
+ return Promise.reject(new Error('missing username or password'));
+ }
+
+ if (username) {
+ username = username.trim();
+ }
+
+ if (this.isBPMProvider()) {
+ try {
+ return await this.processAuth.login(username, password);
+ } catch (e) {
+ return Promise.reject(e);
+ }
+
+ } else if (this.isECMProvider()) {
+ try {
+ return await this.contentAuth.login(username, password);
+ } catch (e) {
+ return Promise.reject(e);
+ }
+
+ } else if (this.isALLProvider()) {
+ return this.loginBPMECM(username, password);
+ } else {
+ return Promise.reject(new Error('Unknown configuration'));
+ }
+
+ }
+
+ private loginBPMECM(username: string, password: string): Promise {
+ const contentPromise = this.contentAuth.login(username, password);
+ const processPromise = this.processAuth.login(username, password);
+
+ return new Promise((resolve, reject) => {
+ Promise.all([contentPromise, processPromise]).then(
+ (data) => {
+ this.onLogin.next('success');
+ resolve(data);
+ },
+ (error) => {
+ this.contentAuth.invalidateSession();
+ this.processAuth.invalidateSession();
+
+ if (error.status === 401) {
+ this.onError.next('unauthorized');
+ }
+ this.onError.next('error');
+ reject(error);
+ });
+ });
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * 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);
+ }
+
+ isCredentialValid(credential: string): boolean {
+ return credential !== undefined && credential !== null && credential !== '';
+ }
+
+ getToken(): string {
+ if (this.isBPMProvider()) {
+ return this.processAuth.getToken();
+ } else if (this.isECMProvider()) {
+ return this.contentAuth.getToken();
+ } else if (this.isALLProvider()) {
+ return this.contentAuth.getToken();
+ } else {
+ return '';
+ }
+ }
+
+ /**
+ * @deprecated
+ * @returns content auth token
+ */
+ getTicketEcm(): string {
+ return this.contentAuth.getToken();
+ }
+
+ /**
+ * @deprecated
+ * @returns process auth token
+ */
+ getTicketBpm(): string {
+ return this.processAuth.getToken();
+ }
+
+ isBpmLoggedIn(): boolean {
+ return this.processAuth.isLoggedIn();
+ }
+
+ isEcmLoggedIn(): boolean {
+ return this.contentAuth.isLoggedIn();
+ }
+
+ isLoggedIn(): boolean {
+ const authWithCredentials = this.isKerberosEnabled();
+
+ if (this.isBPMProvider()) {
+ return this.processAuth.isLoggedIn();
+ } else if (this.isECMProvider()) {
+ return authWithCredentials ? true : this.contentAuth.isLoggedIn();
+ } else if (this.isALLProvider()) {
+ return authWithCredentials ? true : (this.contentAuth.isLoggedIn() && this.processAuth.isLoggedIn());
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * logout Alfresco API
+ */
+ async logout(): Promise {
+ if (this.isBPMProvider()) {
+ return this.processAuth.logout();
+ } else if (this.isECMProvider()) {
+ return this.contentAuth.logout();
+ } else if (this.isALLProvider()) {
+ return this.logoutBPMECM();
+ }
+ return Promise.resolve();
+ }
+
+ private logoutBPMECM(): Promise {
+ const contentPromise = this.contentAuth.logout();
+ const processPromise = this.processAuth.logout();
+
+ return new Promise((resolve, reject) => {
+ Promise.all([contentPromise, processPromise]).then(
+ () => {
+ this.contentAuth.ticket = undefined;
+ this.processAuth.ticket = undefined;
+ this.onLogout.next('logout');
+ resolve('logout');
+ },
+ (error) => {
+ if (error.status === 401) {
+ this.onError.next('unauthorized');
+ }
+ this.onError.next('error');
+ reject(error);
+ });
+ });
+
+ }
+
+ reset(): void {
+ }
+
+ /**
+ * Gets the URL to redirect to after login.
+ *
+ * @returns The redirect URL
+ */
+ getRedirect(): string {
+ const provider = this.appConfig.get(AppConfigValues.PROVIDERS);
+ return this.hasValidRedirection(provider) ? this.redirectUrl.url : null;
+ }
+
+ setRedirect(url?: RedirectionModel) {
+ this.redirectUrl = url;
+ }
+
+ 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');
+ }
+
+ getBpmUsername(): string {
+ return this.processAuth.getUsername();
+ }
+
+ getEcmUsername(): string {
+ return this.contentAuth.getUsername();
+ }
+
+ getUsername(): string {
+ if (this.isBPMProvider()) {
+ return this.processAuth.getUsername();
+ } else if (this.isECMProvider()) {
+ return this.contentAuth.getUsername();
+ } else {
+ return this.contentAuth.getUsername();
+ }
+ }
+
+ /**
+ * Does kerberos enabled?
+ *
+ * @returns True if enabled, false otherwise
+ */
+ isKerberosEnabled(): boolean {
+ return this.appConfig.get(AppConfigValues.AUTH_WITH_CREDENTIALS, false);
+ }
+
+ getAuthHeaders(requestUrl: string, header: HttpHeaders): HttpHeaders {
+ return this.addBasicAuth(requestUrl, header);
+ }
+
+ private addBasicAuth(requestUrl: string, header: HttpHeaders): HttpHeaders {
+ const ticket = this.getTicketEcmBase64(requestUrl);
+
+ if (!ticket) {
+ return header;
+ }
+
+ return header.set('Authorization', ticket);
+ }
+
+ async requireAlfTicket(): Promise {
+ return this.contentAuth.requireAlfTicket();
+ }
+
+ /**
+ * Gets the BPM ticket from the Storage in Base 64 format.
+ *
+ * @param requestUrl the request url
+ * @returns The ticket or `null` if none was found
+ */
+ private getTicketEcmBase64(requestUrl: string): string | null {
+ let ticket = null;
+
+ const contextRootBpm = this.appConfig.get(AppConfigValues.CONTEXTROOTBPM) || 'activiti-app';
+ const contextRoot = this.appConfig.get(AppConfigValues.CONTEXTROOTECM) || 'alfresco';
+
+ if (contextRoot && requestUrl.indexOf(contextRoot) !== -1) {
+ ticket = 'Basic ' + btoa(this.contentAuth.getToken());
+ } else if (contextRootBpm && requestUrl.indexOf(contextRootBpm) !== -1) {
+ ticket = 'Basic ' + this.processAuth.getToken();
+ }
+
+ return ticket;
+ }
+}
diff --git a/lib/core/src/lib/auth/basic-auth/content-auth.ts b/lib/core/src/lib/auth/basic-auth/content-auth.ts
new file mode 100644
index 0000000000..c7918316b0
--- /dev/null
+++ b/lib/core/src/lib/auth/basic-auth/content-auth.ts
@@ -0,0 +1,220 @@
+/*!
+ * @license
+ * Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { Injectable } from '@angular/core';
+import { AdfHttpClient } from '@alfresco/adf-core/api';
+import { AppConfigService, AppConfigValues } from '../../app-config/app-config.service';
+import { StorageService } from '../../common/services/storage.service';
+import { ReplaySubject, Subject } from 'rxjs';
+import { Authentication } from '../interfaces/authentication.interface';
+
+export interface TicketBody {
+ userId?: string;
+ password?: string;
+}
+
+export interface TicketEntry {
+ entry: {
+ id?: string;
+ userId?: string;
+ };
+}
+
+@Injectable({
+ providedIn: 'root'
+})
+export class ContentAuth {
+
+ onLogin = new ReplaySubject(1);
+ onLogout = new ReplaySubject(1);
+ onError = new Subject();
+
+ ticket: string;
+ config = {
+ ticketEcm: null
+ };
+
+ authentications: Authentication = {
+ basicAuth: {
+ ticket: ''
+ },
+ type: 'basic'
+ };
+
+ get basePath(): string {
+ const contextRootEcm = this.appConfigService.get(AppConfigValues.CONTEXTROOTECM) || 'alfresco';
+ return this.appConfigService.get(AppConfigValues.ECMHOST) + '/' + contextRootEcm + '/api/-default-/public/authentication/versions/1';
+ }
+
+ constructor(private appConfigService: AppConfigService,
+ private adfHttpClient: AdfHttpClient,
+ private storageService: StorageService) {
+ this.appConfigService.onLoad.subscribe(() => {
+ this.setConfig();
+ });
+ }
+
+ private setConfig() {
+ if (this.storageService.getItem(AppConfigValues.CONTENT_TICKET_STORAGE_LABEL)) {
+ this.setTicket(this.storageService.getItem(AppConfigValues.CONTENT_TICKET_STORAGE_LABEL));
+ }
+
+ }
+
+ saveUsername(username: string) {
+ this.storageService.setItem('ACS_USERNAME', username);
+ }
+
+ getUsername() {
+ return this.storageService.getItem('ACS_USERNAME');
+ }
+
+ /**
+ * login Alfresco API
+ *
+ * @param username username to login
+ * @param password password to login
+ * @returns A promise that returns {new authentication ticket} if resolved and {error} if rejected.
+ */
+ login(username: string, password: string): Promise {
+ this.authentications.basicAuth.username = username;
+ this.authentications.basicAuth.password = password;
+
+ const loginRequest: any = {};
+
+ loginRequest.userId = this.authentications.basicAuth.username;
+ loginRequest.password = this.authentications.basicAuth.password;
+
+ return new Promise((resolve, reject) => {
+ this.createTicket(loginRequest)
+ .then((data: any) => {
+ this.saveUsername(username);
+ this.setTicket(data.entry.id);
+ this.adfHttpClient.emit('success');
+ this.onLogin.next('success');
+ resolve(data.entry.id);
+ })
+ .catch((error) => {
+ this.saveUsername('');
+ if (error.status === 401) {
+ this.adfHttpClient.emit('unauthorized', error);
+ this.onError.next('unauthorized');
+ } else if (error.status === 403) {
+ this.adfHttpClient.emit('forbidden', error);
+ this.onError.next('forbidden');
+ } else {
+ this.adfHttpClient.emit('error', error);
+ this.onError.next('error');
+ }
+ reject(error);
+ });
+ });
+ }
+
+ /**
+ * logout Alfresco API
+ *
+ * @returns A promise that returns { authentication ticket} if resolved and {error} if rejected.
+ */
+ logout(): Promise {
+ this.saveUsername('');
+ return new Promise((resolve, reject) => {
+ this.deleteTicket().then(
+ () => {
+ this.invalidateSession();
+ this.adfHttpClient.emit('logout');
+ this.onLogout.next('logout');
+ resolve('logout');
+ },
+ (error) => {
+ if (error.status === 401) {
+ this.adfHttpClient.emit('unauthorized');
+ this.onError.next('unauthorized');
+ }
+ this.adfHttpClient.emit('error');
+ this.onError.next('error');
+ reject(error);
+ });
+ });
+ }
+
+ /**
+ * Set the current Ticket
+ *
+ * @param ticket a string representing the ticket
+ */
+ setTicket(ticket: string) {
+ this.authentications.basicAuth.username = 'ROLE_TICKET';
+ this.authentications.basicAuth.password = ticket;
+ this.config.ticketEcm = ticket;
+ this.storageService.setItem(AppConfigValues.CONTENT_TICKET_STORAGE_LABEL, ticket);
+ this.ticket = ticket;
+ }
+
+ /**
+ * @returns the current Ticket
+ */
+ getToken(): string {
+ if (!this.ticket) {
+ this.onError.next('error');
+ }
+
+ return this.ticket;
+ }
+
+ invalidateSession() {
+ this.storageService.removeItem(AppConfigValues.CONTENT_TICKET_STORAGE_LABEL);
+ this.authentications.basicAuth.username = null;
+ this.authentications.basicAuth.password = null;
+ this.config.ticketEcm = null;
+ this.ticket = null;
+ }
+
+ /**
+ * @returns If the client is logged in return true
+ */
+ isLoggedIn(): boolean {
+ return !!this.ticket;
+ }
+
+ /**
+ * @returns return the Authentication
+ */
+ getAuthentication() {
+ return this.authentications;
+ }
+
+ createTicket(ticketBodyCreate: TicketBody): Promise {
+ if (ticketBodyCreate === null || ticketBodyCreate === undefined) {
+ this.onError.next((`Missing param ticketBodyCreate`));
+
+ throw new Error(`Missing param ticketBodyCreate`);
+ }
+
+ return this.adfHttpClient.post(this.basePath + '/tickets', {bodyParam: ticketBodyCreate});
+ }
+
+ async requireAlfTicket(): Promise {
+ const ticket = await this.adfHttpClient.get(this.basePath + '/tickets/-me-');
+ this.setTicket(ticket.entry.id);
+ }
+
+ deleteTicket(): Promise {
+ return this.adfHttpClient.delete(this.basePath + '/tickets/-me-');
+ }
+
+}
diff --git a/lib/core/src/lib/auth/basic-auth/process-auth.ts b/lib/core/src/lib/auth/basic-auth/process-auth.ts
new file mode 100644
index 0000000000..7374a160d9
--- /dev/null
+++ b/lib/core/src/lib/auth/basic-auth/process-auth.ts
@@ -0,0 +1,210 @@
+/*!
+ * @license
+ * Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { Injectable } from '@angular/core';
+import { AdfHttpClient } from '@alfresco/adf-core/api';
+import { Authentication } from '../interfaces/authentication.interface';
+import { AppConfigService, AppConfigValues } from '../../app-config/app-config.service';
+import { StorageService } from '../../common/services/storage.service';
+import { ReplaySubject, Subject } from 'rxjs';
+
+
+@Injectable({
+ providedIn: 'root'
+})
+export class ProcessAuth {
+
+ onLogin = new ReplaySubject(1);
+ onLogout = new ReplaySubject(1);
+ onError = new Subject();
+
+ ticket: string;
+ config = {
+ ticketBpm: null
+ };
+
+ authentications: Authentication = {
+ basicAuth: {ticket: ''}, type: 'activiti'
+ };
+
+ get basePath(): string {
+ const contextRootBpm = this.appConfigService.get(AppConfigValues.CONTEXTROOTBPM) || 'activiti-app';
+ return this.appConfigService.get(AppConfigValues.BPMHOST) + '/' + contextRootBpm;
+ }
+
+ constructor(private appConfigService: AppConfigService,
+ private adfHttpClient: AdfHttpClient,
+ private storageService: StorageService) {
+ this.appConfigService.onLoad.subscribe(() => {
+ this.setConfig();
+ });
+ }
+
+ private setConfig() {
+ this.ticket = undefined;
+
+ this.setTicket(this.storageService.getItem(AppConfigValues.PROCESS_TICKET_STORAGE_LABEL));
+ }
+
+ saveUsername(username: string) {
+ this.storageService.setItem('APS_USERNAME', username);
+ }
+
+ getUsername() {
+ return this.storageService.getItem('APS_USERNAME');
+ }
+
+ /**
+ * login Activiti API
+ *
+ * @param username Username to login
+ * @param password Password to login
+ * @returns A promise that returns {new authentication ticket} if resolved and {error} if rejected.
+ */
+ login(username: string, password: string): Promise {
+ this.authentications.basicAuth.username = username;
+ this.authentications.basicAuth.password = password;
+
+ const options = {
+ headerParams: {
+ 'Content-Type': 'application/x-www-form-urlencoded',
+ 'Cache-Control': 'no-cache'
+ },
+ formParams: {
+ j_username: this.authentications.basicAuth.username,
+ j_password: this.authentications.basicAuth.password,
+ _spring_security_remember_me: true,
+ submit: 'Login'
+ },
+ contentType: 'application/x-www-form-urlencoded',
+ accept: 'application/json'
+ };
+
+ const promise: any = new Promise((resolve, reject) => {
+ this.adfHttpClient.post(this.basePath + '/app/authentication', options).then(
+ () => {
+ this.saveUsername(username);
+ const ticket = this.basicAuth(this.authentications.basicAuth.username, this.authentications.basicAuth.password);
+ this.setTicket(ticket);
+ this.onLogin.next('success');
+ this.adfHttpClient.emit('success');
+ this.adfHttpClient.emit('logged-in');
+ resolve(ticket);
+ },
+ (error) => {
+ this.saveUsername('');
+ if (error.status === 401) {
+ this.adfHttpClient.emit('unauthorized', error);
+ this.onError.next('unauthorized');
+ } else if (error.status === 403) {
+ this.adfHttpClient.emit('forbidden', error);
+ this.onError.next('forbidden');
+ } else {
+ this.adfHttpClient.emit('error', error);
+ this.onError.next('error');
+ }
+ reject(error);
+ });
+ });
+
+ return promise;
+ }
+
+ /**
+ * logout Alfresco API
+ *
+ * @returns A promise that returns {new authentication ticket} if resolved and {error} if rejected.
+ */
+ async logout(): Promise {
+ this.saveUsername('');
+ return new Promise((resolve, reject) => {
+ this.adfHttpClient.get(this.basePath + `/app/logout`, {}).then(
+ () => {
+ this.invalidateSession();
+ this.onLogout.next('logout');
+ this.adfHttpClient.emit('logout');
+ resolve('logout');
+ },
+ (error) => {
+ if (error.status === 401) {
+ this.adfHttpClient.emit('unauthorized');
+ this.onError.next('unauthorized');
+ }
+ this.adfHttpClient.emit('error');
+ this.onError.next('error');
+ reject(error);
+ });
+ });
+ }
+
+ basicAuth(username: string, password: string): string {
+ const str: any = username + ':' + password;
+
+ let base64;
+
+ if (typeof Buffer === 'function') {
+ base64 = Buffer.from(str.toString(), 'binary').toString('base64');
+ } else {
+ base64 = btoa(str);
+ }
+
+ return `Basic ${base64}`;
+ }
+
+ /**
+ * Set the current Ticket
+ *
+ * @param ticket a string representing the ticket
+ */
+ setTicket(ticket: string) {
+ if (ticket && ticket !== 'null') {
+ this.authentications.basicAuth.ticket = ticket;
+ this.authentications.basicAuth.password = null;
+ this.config.ticketBpm = ticket;
+ this.storageService.setItem(AppConfigValues.PROCESS_TICKET_STORAGE_LABEL, ticket);
+ this.ticket = ticket;
+ }
+ }
+
+ invalidateSession() {
+ this.storageService.removeItem(AppConfigValues.PROCESS_TICKET_STORAGE_LABEL);
+ this.authentications.basicAuth.ticket = null;
+ this.authentications.basicAuth.password = null;
+ this.authentications.basicAuth.username = null;
+ this.config.ticketBpm = null;
+ this.ticket = null;
+ }
+
+ /**
+ * @returns the current Ticket
+ */
+ getToken(): string {
+ if (!this.ticket) {
+ this.onError.next('error');
+ return null;
+ }
+
+ return this.ticket;
+ }
+
+ /**
+ * @returns If the client is logged in return true
+ */
+ isLoggedIn(): boolean {
+ return !!this.ticket;
+ }
+}
diff --git a/lib/core/src/lib/auth/guard/auth-guard-base.ts b/lib/core/src/lib/auth/guard/auth-guard-base.ts
index b91bc0fbfd..036ed6386e 100644
--- a/lib/core/src/lib/auth/guard/auth-guard-base.ts
+++ b/lib/core/src/lib/auth/guard/auth-guard-base.ts
@@ -17,24 +17,35 @@
import { Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, CanActivateChild, UrlTree } from '@angular/router';
import { AuthenticationService } from '../services/authentication.service';
-import { AppConfigService, AppConfigValues } from '../../app-config/app-config.service';
+import {
+ AppConfigService,
+ AppConfigValues
+} from '../../app-config/app-config.service';
import { OauthConfigModel } from '../models/oauth-config.model';
import { MatDialog } from '@angular/material/dialog';
import { StorageService } from '../../common/services/storage.service';
import { Observable } from 'rxjs';
-import { inject } from '@angular/core';
+import { BasicAlfrescoAuthService } from '../basic-auth/basic-alfresco-auth.service';
+import { OidcAuthenticationService } from '../services/oidc-authentication.service';
+
export abstract class AuthGuardBase implements CanActivate, CanActivateChild {
- protected authenticationService = inject(AuthenticationService);
- protected router = inject(Router);
- protected appConfigService = inject(AppConfigService);
- protected dialog = inject(MatDialog);
- private storageService = inject(StorageService);
protected get withCredentials(): boolean {
return this.appConfigService.get('auth.withCredentials', false);
}
+ constructor(
+ protected authenticationService: AuthenticationService,
+ protected basicAlfrescoAuthService: BasicAlfrescoAuthService,
+ protected oidcAuthenticationService: OidcAuthenticationService,
+ protected router: Router,
+ protected appConfigService: AppConfigService,
+ protected dialog: MatDialog,
+ private storageService: StorageService
+ ) {
+ }
+
abstract checkLogin(
activeRoute: ActivatedRouteSnapshot,
redirectUrl: string
@@ -78,15 +89,17 @@ export abstract class AuthGuardBase implements CanActivate, CanActivateChild {
let urlToRedirect = `/${this.getLoginRoute()}`;
if (!this.authenticationService.isOauth()) {
- this.authenticationService.setRedirect({
+ this.basicAlfrescoAuthService.setRedirect({
provider: this.getProvider(),
url
});
urlToRedirect = `${urlToRedirect}?redirectUrl=${url}`;
return this.navigate(urlToRedirect);
- } else if (this.getOauthConfig().silentLogin && !this.authenticationService.isPublicUrl()) {
- this.authenticationService.ssoImplicitLogin();
+ } else if (this.getOauthConfig().silentLogin && !this.oidcAuthenticationService.isPublicUrl()) {
+ if (!this.oidcAuthenticationService.hasValidIdToken() || !this.oidcAuthenticationService.hasValidAccessToken()) {
+ this.oidcAuthenticationService.ssoImplicitLogin();
+ }
} else {
return this.navigate(urlToRedirect);
}
@@ -101,7 +114,13 @@ export abstract class AuthGuardBase implements CanActivate, CanActivateChild {
}
protected getOauthConfig(): OauthConfigModel {
- return this.appConfigService.oauth2;
+ return (
+ this.appConfigService &&
+ this.appConfigService.get(
+ AppConfigValues.OAUTHCONFIG,
+ null
+ )
+ );
}
protected getLoginRoute(): string {
@@ -113,12 +132,21 @@ export abstract class AuthGuardBase implements CanActivate, CanActivateChild {
}
protected isOAuthWithoutSilentLogin(): boolean {
- const oauth = this.appConfigService.oauth2;
- return this.authenticationService.isOauth() && !!oauth && !oauth.silentLogin;
+ const oauth = this.appConfigService.get(
+ AppConfigValues.OAUTHCONFIG,
+ null
+ );
+ return (
+ this.authenticationService.isOauth() && !!oauth && !oauth.silentLogin
+ );
}
protected isSilentLogin(): boolean {
- const oauth = this.appConfigService.oauth2;
+ const oauth = this.appConfigService.get(
+ AppConfigValues.OAUTHCONFIG,
+ null
+ );
+
return this.authenticationService.isOauth() && oauth && oauth.silentLogin;
}
}
diff --git a/lib/core/src/lib/auth/guard/auth-guard-bpm.service.spec.ts b/lib/core/src/lib/auth/guard/auth-guard-bpm.service.spec.ts
index 40288684c2..14619d26c5 100644
--- a/lib/core/src/lib/auth/guard/auth-guard-bpm.service.spec.ts
+++ b/lib/core/src/lib/auth/guard/auth-guard-bpm.service.spec.ts
@@ -23,11 +23,16 @@ import { RouterStateSnapshot, Router } from '@angular/router';
import { CoreTestingModule } from '../../testing/core.testing.module';
import { MatDialog } from '@angular/material/dialog';
import { TranslateModule } from '@ngx-translate/core';
+import { BasicAlfrescoAuthService } from '../basic-auth/basic-alfresco-auth.service';
+import { OidcAuthenticationService } from '../services/oidc-authentication.service';
describe('AuthGuardService BPM', () => {
let authGuard: AuthGuardBpm;
let authService: AuthenticationService;
+ let basicAlfrescoAuthService: BasicAlfrescoAuthService;
+ let oidcAuthenticationService: OidcAuthenticationService;
+
let router: Router;
let appConfigService: AppConfigService;
@@ -36,9 +41,21 @@ describe('AuthGuardService BPM', () => {
imports: [
TranslateModule.forRoot(),
CoreTestingModule
+ ],
+ providers: [
+ {
+ provide: OidcAuthenticationService, useValue: {
+ ssoImplicitLogin: () => { },
+ isPublicUrl: () => false,
+ hasValidIdToken: () => false,
+ isLoggedIn: () => false
+ }
+ }
]
});
localStorage.clear();
+ basicAlfrescoAuthService = TestBed.inject(BasicAlfrescoAuthService);
+ oidcAuthenticationService = TestBed.inject(OidcAuthenticationService);
authService = TestBed.inject(AuthenticationService);
authGuard = TestBed.inject(AuthGuardBpm);
router = TestBed.inject(Router);
@@ -53,8 +70,8 @@ describe('AuthGuardService BPM', () => {
spyOn(router, 'navigateByUrl').and.stub();
spyOn(authService, 'isBpmLoggedIn').and.returnValue(false);
spyOn(authService, 'isOauth').and.returnValue(true);
- spyOn(authService, 'isPublicUrl').and.returnValue(false);
- spyOn(authService, 'ssoImplicitLogin').and.stub();
+ spyOn(oidcAuthenticationService, 'isPublicUrl').and.returnValue(false);
+ spyOn(oidcAuthenticationService, 'ssoImplicitLogin').and.stub();
appConfigService.config.oauth2 = {
silentLogin: true,
@@ -69,7 +86,7 @@ describe('AuthGuardService BPM', () => {
const route = { url: 'abc' } as RouterStateSnapshot;
expect(await authGuard.canActivate(null, route)).toBeFalsy();
- expect(authService.ssoImplicitLogin).toHaveBeenCalledTimes(1);
+ expect(oidcAuthenticationService.ssoImplicitLogin).toHaveBeenCalledTimes(1);
});
it('if the alfresco js api is logged in should canActivate be true', async () => {
@@ -130,53 +147,53 @@ describe('AuthGuardService BPM', () => {
});
it('should set redirect url', () => {
- spyOn(authService, 'setRedirect').and.callThrough();
+ spyOn(basicAlfrescoAuthService, 'setRedirect').and.callThrough();
spyOn(router, 'navigateByUrl').and.stub();
const route = { url: 'some-url' } as RouterStateSnapshot;
authGuard.canActivate(null, route);
- expect(authService.setRedirect).toHaveBeenCalledWith({
+ expect(basicAlfrescoAuthService.setRedirect).toHaveBeenCalledWith({
provider: 'BPM', url: 'some-url'
});
- expect(authService.getRedirect()).toEqual('some-url');
+ expect(basicAlfrescoAuthService.getRedirect()).toEqual('some-url');
});
it('should set redirect navigation commands with query params', () => {
- spyOn(authService, 'setRedirect').and.callThrough();
+ spyOn(basicAlfrescoAuthService, 'setRedirect').and.callThrough();
spyOn(router, 'navigateByUrl').and.stub();
const route = { url: 'some-url;q=123' } as RouterStateSnapshot;
authGuard.canActivate(null, route);
- expect(authService.setRedirect).toHaveBeenCalledWith({
+ expect(basicAlfrescoAuthService.setRedirect).toHaveBeenCalledWith({
provider: 'BPM', url: 'some-url;q=123'
});
- expect(authService.getRedirect()).toEqual('some-url;q=123');
+ expect(basicAlfrescoAuthService.getRedirect()).toEqual('some-url;q=123');
});
it('should set redirect navigation commands with query params', () => {
- spyOn(authService, 'setRedirect').and.callThrough();
+ spyOn(basicAlfrescoAuthService, 'setRedirect').and.callThrough();
spyOn(router, 'navigateByUrl').and.stub();
const route = { url: '/' } as RouterStateSnapshot;
authGuard.canActivate(null, route);
- expect(authService.setRedirect).toHaveBeenCalledWith({
+ expect(basicAlfrescoAuthService.setRedirect).toHaveBeenCalledWith({
provider: 'BPM', url: '/'
});
- expect(authService.getRedirect()).toEqual('/');
+ expect(basicAlfrescoAuthService.getRedirect()).toEqual('/');
});
it('should get redirect url from config if there is one configured', () => {
appConfigService.config.loginRoute = 'fakeLoginRoute';
- spyOn(authService, 'setRedirect').and.callThrough();
+ spyOn(basicAlfrescoAuthService, 'setRedirect').and.callThrough();
spyOn(router, 'navigateByUrl').and.stub();
const route = { url: 'some-url' } as RouterStateSnapshot;
authGuard.canActivate(null, route);
- expect(authService.setRedirect).toHaveBeenCalledWith({
+ expect(basicAlfrescoAuthService.setRedirect).toHaveBeenCalledWith({
provider: 'BPM', url: 'some-url'
});
expect(router.navigateByUrl).toHaveBeenCalledWith(router.parseUrl('/fakeLoginRoute?redirectUrl=some-url'));
@@ -187,13 +204,13 @@ describe('AuthGuardService BPM', () => {
spyOn(materialDialog, 'closeAll');
- spyOn(authService, 'setRedirect').and.callThrough();
+ spyOn(basicAlfrescoAuthService, 'setRedirect').and.callThrough();
spyOn(router, 'navigateByUrl').and.stub();
const route = { url: 'some-url' } as RouterStateSnapshot;
authGuard.canActivate(null, route);
- expect(authService.setRedirect).toHaveBeenCalledWith({
+ expect(basicAlfrescoAuthService.setRedirect).toHaveBeenCalledWith({
provider: 'BPM', url: 'some-url'
});
diff --git a/lib/core/src/lib/auth/guard/auth-guard-bpm.service.ts b/lib/core/src/lib/auth/guard/auth-guard-bpm.service.ts
index 4d511832a9..59a1a3457b 100644
--- a/lib/core/src/lib/auth/guard/auth-guard-bpm.service.ts
+++ b/lib/core/src/lib/auth/guard/auth-guard-bpm.service.ts
@@ -16,13 +16,30 @@
*/
import { Injectable } from '@angular/core';
-import { ActivatedRouteSnapshot, UrlTree } from '@angular/router';
+import { ActivatedRouteSnapshot, Router, UrlTree } from '@angular/router';
+import { AppConfigService } from '../../app-config/app-config.service';
+import { AuthenticationService } from '../services/authentication.service';
import { AuthGuardBase } from './auth-guard-base';
+import { MatDialog } from '@angular/material/dialog';
+import { StorageService } from '../../common/services/storage.service';
+import { BasicAlfrescoAuthService } from '../basic-auth/basic-alfresco-auth.service';
+import { OidcAuthenticationService } from '../services/oidc-authentication.service';
@Injectable({
providedIn: 'root'
})
export class AuthGuardBpm extends AuthGuardBase {
+
+ constructor(authenticationService: AuthenticationService,
+ basicAlfrescoAuthService: BasicAlfrescoAuthService,
+ oidcAuthenticationService: OidcAuthenticationService,
+ router: Router,
+ appConfigService: AppConfigService,
+ dialog: MatDialog,
+ storageService: StorageService) {
+ super(authenticationService,basicAlfrescoAuthService, oidcAuthenticationService,router, appConfigService, dialog, storageService);
+ }
+
async checkLogin(_: ActivatedRouteSnapshot, redirectUrl: string): Promise {
if (this.authenticationService.isBpmLoggedIn() || this.withCredentials) {
return true;
diff --git a/lib/core/src/lib/auth/guard/auth-guard-ecm.service.spec.ts b/lib/core/src/lib/auth/guard/auth-guard-ecm.service.spec.ts
index 70856196fa..d4c26591db 100644
--- a/lib/core/src/lib/auth/guard/auth-guard-ecm.service.spec.ts
+++ b/lib/core/src/lib/auth/guard/auth-guard-ecm.service.spec.ts
@@ -23,11 +23,15 @@ import { RouterStateSnapshot, Router } from '@angular/router';
import { CoreTestingModule } from '../../testing/core.testing.module';
import { MatDialog } from '@angular/material/dialog';
import { TranslateModule } from '@ngx-translate/core';
+import { OidcAuthenticationService } from '../services/oidc-authentication.service';
+import { BasicAlfrescoAuthService } from '../basic-auth/basic-alfresco-auth.service';
describe('AuthGuardService ECM', () => {
let authGuard: AuthGuardEcm;
let authService: AuthenticationService;
+ let basicAlfrescoAuthService: BasicAlfrescoAuthService;
+ let oidcAuthenticationService: OidcAuthenticationService;
let router: Router;
let appConfigService: AppConfigService;
@@ -36,9 +40,21 @@ describe('AuthGuardService ECM', () => {
imports: [
TranslateModule.forRoot(),
CoreTestingModule
+ ],
+ providers: [
+ {
+ provide: OidcAuthenticationService, useValue: {
+ ssoImplicitLogin: () => { },
+ isPublicUrl: () => false,
+ hasValidIdToken: () => false,
+ isLoggedIn: () => false
+ }
+ }
]
});
localStorage.clear();
+ oidcAuthenticationService = TestBed.inject(OidcAuthenticationService);
+ basicAlfrescoAuthService = TestBed.inject(BasicAlfrescoAuthService);
authService = TestBed.inject(AuthenticationService);
authGuard = TestBed.inject(AuthGuardEcm);
router = TestBed.inject(Router);
@@ -98,8 +114,8 @@ describe('AuthGuardService ECM', () => {
it('should redirect url if the alfresco js api is NOT logged in and isOAuth with silentLogin', async () => {
spyOn(authService, 'isEcmLoggedIn').and.returnValue(false);
spyOn(authService, 'isOauth').and.returnValue(true);
- spyOn(authService, 'isPublicUrl').and.returnValue(false);
- spyOn(authService, 'ssoImplicitLogin').and.stub();
+ spyOn(oidcAuthenticationService, 'isPublicUrl').and.returnValue(false);
+ spyOn(oidcAuthenticationService, 'ssoImplicitLogin').and.stub();
appConfigService.config.oauth2 = {
silentLogin: true,
@@ -113,7 +129,7 @@ describe('AuthGuardService ECM', () => {
const route = {url : 'abc'} as RouterStateSnapshot;
expect(await authGuard.canActivate(null, route)).toBeFalsy();
- expect(authService.ssoImplicitLogin).toHaveBeenCalledTimes(1);
+ expect(oidcAuthenticationService.ssoImplicitLogin).toHaveBeenCalledTimes(1);
});
it('should not redirect url if NOT logged in and isOAuth but no silentLogin configured', async () => {
@@ -128,53 +144,53 @@ describe('AuthGuardService ECM', () => {
});
it('should set redirect navigation commands', () => {
- spyOn(authService, 'setRedirect').and.callThrough();
+ spyOn(basicAlfrescoAuthService, 'setRedirect').and.callThrough();
spyOn(router, 'navigateByUrl').and.stub();
const route = { url: 'some-url' } as RouterStateSnapshot;
authGuard.canActivate(null, route);
- expect(authService.setRedirect).toHaveBeenCalledWith({
+ expect(basicAlfrescoAuthService.setRedirect).toHaveBeenCalledWith({
provider: 'ECM', url: 'some-url'
});
- expect(authService.getRedirect()).toEqual('some-url');
+ expect(basicAlfrescoAuthService.getRedirect()).toEqual('some-url');
});
it('should set redirect navigation commands with query params', () => {
- spyOn(authService, 'setRedirect').and.callThrough();
+ spyOn(basicAlfrescoAuthService, 'setRedirect').and.callThrough();
spyOn(router, 'navigateByUrl').and.stub();
const route = { url: 'some-url;q=123' } as RouterStateSnapshot;
authGuard.canActivate(null, route);
- expect(authService.setRedirect).toHaveBeenCalledWith({
+ expect(basicAlfrescoAuthService.setRedirect).toHaveBeenCalledWith({
provider: 'ECM', url: 'some-url;q=123'
});
- expect(authService.getRedirect()).toEqual('some-url;q=123');
+ expect(basicAlfrescoAuthService.getRedirect()).toEqual('some-url;q=123');
});
it('should set redirect navigation commands with query params', () => {
- spyOn(authService, 'setRedirect').and.callThrough();
+ spyOn(basicAlfrescoAuthService, 'setRedirect').and.callThrough();
spyOn(router, 'navigateByUrl').and.stub();
const route = { url: '/' } as RouterStateSnapshot;
authGuard.canActivate(null, route);
- expect(authService.setRedirect).toHaveBeenCalledWith({
+ expect(basicAlfrescoAuthService.setRedirect).toHaveBeenCalledWith({
provider: 'ECM', url: '/'
});
- expect(authService.getRedirect()).toEqual('/');
+ expect(basicAlfrescoAuthService.getRedirect()).toEqual('/');
});
it('should get redirect url from config if there is one configured', () => {
appConfigService.config.loginRoute = 'fakeLoginRoute';
- spyOn(authService, 'setRedirect').and.callThrough();
+ spyOn(basicAlfrescoAuthService, 'setRedirect').and.callThrough();
spyOn(router, 'navigateByUrl').and.stub();
const route = { url: 'some-url' } as RouterStateSnapshot;
authGuard.canActivate(null, route);
- expect(authService.setRedirect).toHaveBeenCalledWith({
+ expect(basicAlfrescoAuthService.setRedirect).toHaveBeenCalledWith({
provider: 'ECM', url: 'some-url'
});
expect(router.navigateByUrl).toHaveBeenCalledWith(router.parseUrl('/fakeLoginRoute?redirectUrl=some-url'));
@@ -185,13 +201,13 @@ describe('AuthGuardService ECM', () => {
spyOn(materialDialog, 'closeAll');
- spyOn(authService, 'setRedirect').and.callThrough();
+ spyOn(basicAlfrescoAuthService, 'setRedirect').and.callThrough();
spyOn(router, 'navigateByUrl').and.stub();
const route = { url: 'some-url' } as RouterStateSnapshot;
authGuard.canActivate(null, route);
- expect(authService.setRedirect).toHaveBeenCalledWith({
+ expect(basicAlfrescoAuthService.setRedirect).toHaveBeenCalledWith({
provider: 'ECM', url: 'some-url'
});
diff --git a/lib/core/src/lib/auth/guard/auth-guard-ecm.service.ts b/lib/core/src/lib/auth/guard/auth-guard-ecm.service.ts
index 0154b91ec7..0621b528e1 100644
--- a/lib/core/src/lib/auth/guard/auth-guard-ecm.service.ts
+++ b/lib/core/src/lib/auth/guard/auth-guard-ecm.service.ts
@@ -16,13 +16,33 @@
*/
import { Injectable } from '@angular/core';
-import { ActivatedRouteSnapshot, UrlTree } from '@angular/router';
+import {
+ ActivatedRouteSnapshot, Router, UrlTree
+} from '@angular/router';
+import { AuthenticationService } from '../services/authentication.service';
+import { AppConfigService } from '../../app-config/app-config.service';
import { AuthGuardBase } from './auth-guard-base';
+import { MatDialog } from '@angular/material/dialog';
+import { StorageService } from '../../common/services/storage.service';
+import { BasicAlfrescoAuthService } from '../basic-auth/basic-alfresco-auth.service';
+import { OidcAuthenticationService } from '../services/oidc-authentication.service';
+
@Injectable({
providedIn: 'root'
})
export class AuthGuardEcm extends AuthGuardBase {
+
+ constructor(authenticationService: AuthenticationService,
+ basicAlfrescoAuthService: BasicAlfrescoAuthService,
+ oidcAuthenticationService: OidcAuthenticationService,
+ router: Router,
+ appConfigService: AppConfigService,
+ dialog: MatDialog,
+ storageService: StorageService) {
+ super(authenticationService, basicAlfrescoAuthService, oidcAuthenticationService, router, appConfigService, dialog, storageService);
+ }
+
async checkLogin(_: ActivatedRouteSnapshot, redirectUrl: string): Promise {
if (this.authenticationService.isEcmLoggedIn() || this.withCredentials) {
return true;
diff --git a/lib/core/src/lib/auth/guard/auth-guard.service.spec.ts b/lib/core/src/lib/auth/guard/auth-guard.service.spec.ts
index 54e1d50d4d..96ef8d0eb6 100644
--- a/lib/core/src/lib/auth/guard/auth-guard.service.spec.ts
+++ b/lib/core/src/lib/auth/guard/auth-guard.service.spec.ts
@@ -23,6 +23,8 @@ import { AuthenticationService } from '../services/authentication.service';
import { CoreTestingModule } from '../../testing/core.testing.module';
import { TranslateModule } from '@ngx-translate/core';
import { StorageService } from '../../common/services/storage.service';
+import { OidcAuthenticationService } from '../services/oidc-authentication.service';
+import { BasicAlfrescoAuthService } from '../basic-auth/basic-alfresco-auth.service';
describe('AuthGuardService', () => {
let state;
@@ -31,17 +33,30 @@ describe('AuthGuardService', () => {
let authGuard: AuthGuard;
let storageService: StorageService;
let appConfigService: AppConfigService;
+ let basicAlfrescoAuthService: BasicAlfrescoAuthService;
+ let oidcAuthenticationService: OidcAuthenticationService;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
TranslateModule.forRoot(),
CoreTestingModule
+ ],
+ providers: [
+ {
+ provide: OidcAuthenticationService, useValue: {
+ ssoImplicitLogin: () => { },
+ isPublicUrl: () => false,
+ hasValidIdToken: () => false
+ }
+ }
]
});
localStorage.clear();
state = { url: '' };
authService = TestBed.inject(AuthenticationService);
+ basicAlfrescoAuthService = TestBed.inject(BasicAlfrescoAuthService);
+ oidcAuthenticationService = TestBed.inject(OidcAuthenticationService);
router = TestBed.inject(Router);
authGuard = TestBed.inject(AuthGuard);
appConfigService = TestBed.inject(AppConfigService);
@@ -110,13 +125,13 @@ describe('AuthGuardService', () => {
});
it('should NOT redirect url if the User is NOT logged in and isOAuth but with silentLogin configured', async () => {
- spyOn(authService, 'ssoImplicitLogin').and.stub();
+ spyOn(oidcAuthenticationService, 'ssoImplicitLogin').and.stub();
spyOn(authService, 'isLoggedIn').and.returnValue(false);
spyOn(authService, 'isOauth').and.returnValue(true);
appConfigService.config.oauth2.silentLogin = true;
expect(await authGuard.canActivate(null, state)).toBeFalsy();
- expect(authService.ssoImplicitLogin).toHaveBeenCalledTimes(1);
+ expect(oidcAuthenticationService.ssoImplicitLogin).toHaveBeenCalledTimes(1);
});
it('should set redirect url', async () => {
@@ -124,11 +139,11 @@ describe('AuthGuardService', () => {
appConfigService.config.loginRoute = 'login';
spyOn(router, 'navigateByUrl');
- spyOn(authService, 'setRedirect');
+ spyOn(basicAlfrescoAuthService, 'setRedirect');
await authGuard.canActivate(null, state);
- expect(authService.setRedirect).toHaveBeenCalledWith({
+ expect(basicAlfrescoAuthService.setRedirect).toHaveBeenCalledWith({
provider: 'ALL', url: 'some-url'
});
expect(router.navigateByUrl).toHaveBeenCalledWith(router.parseUrl('/login?redirectUrl=some-url'));
@@ -140,11 +155,11 @@ describe('AuthGuardService', () => {
appConfigService.config.provider = 'ALL';
spyOn(router, 'navigateByUrl');
- spyOn(authService, 'setRedirect');
+ spyOn(basicAlfrescoAuthService, 'setRedirect');
await authGuard.canActivate(null, state);
- expect(authService.setRedirect).toHaveBeenCalledWith({
+ expect(basicAlfrescoAuthService.setRedirect).toHaveBeenCalledWith({
provider: 'ALL', url: 'some-url;q=query'
});
expect(router.navigateByUrl).toHaveBeenCalledWith(router.parseUrl('/login?redirectUrl=some-url;q=query'));
@@ -155,11 +170,11 @@ describe('AuthGuardService', () => {
appConfigService.config.loginRoute = 'fakeLoginRoute';
spyOn(router, 'navigateByUrl');
- spyOn(authService, 'setRedirect');
+ spyOn(basicAlfrescoAuthService, 'setRedirect');
await authGuard.canActivate(null, state);
- expect(authService.setRedirect).toHaveBeenCalledWith({
+ expect(basicAlfrescoAuthService.setRedirect).toHaveBeenCalledWith({
provider: 'ALL', url: 'some-url'
});
expect(router.navigateByUrl).toHaveBeenCalledWith(router.parseUrl('/fakeLoginRoute?redirectUrl=some-url'));
@@ -169,11 +184,11 @@ describe('AuthGuardService', () => {
state.url = '/';
spyOn(router, 'navigateByUrl');
- spyOn(authService, 'setRedirect');
+ spyOn(basicAlfrescoAuthService, 'setRedirect');
await authGuard.canActivate(null, state);
- expect(authService.setRedirect).toHaveBeenCalledWith({
+ expect(basicAlfrescoAuthService.setRedirect).toHaveBeenCalledWith({
provider: 'ALL', url: '/'
});
});
diff --git a/lib/core/src/lib/auth/guard/auth-guard.service.ts b/lib/core/src/lib/auth/guard/auth-guard.service.ts
index 9f6b1897d4..23eb64f47a 100644
--- a/lib/core/src/lib/auth/guard/auth-guard.service.ts
+++ b/lib/core/src/lib/auth/guard/auth-guard.service.ts
@@ -16,9 +16,16 @@
*/
import { Injectable } from '@angular/core';
-import { ActivatedRouteSnapshot, UrlTree } from '@angular/router';
+import { ActivatedRouteSnapshot, Router, UrlTree } from '@angular/router';
+import { AuthenticationService } from '../services/authentication.service';
+import { AppConfigService } from '../../app-config/app-config.service';
import { AuthGuardBase } from './auth-guard-base';
import { JwtHelperService } from '../services/jwt-helper.service';
+import { MatDialog } from '@angular/material/dialog';
+import { StorageService } from '../../common/services/storage.service';
+import { BasicAlfrescoAuthService } from '../basic-auth/basic-alfresco-auth.service';
+import { OidcAuthenticationService } from '../services/oidc-authentication.service';
+
@Injectable({
providedIn: 'root'
@@ -27,8 +34,15 @@ export class AuthGuard extends AuthGuardBase {
ticketChangeBind: any;
- constructor(private jwtHelperService: JwtHelperService) {
- super();
+ constructor(private jwtHelperService: JwtHelperService,
+ authenticationService: AuthenticationService,
+ basicAlfrescoAuthService: BasicAlfrescoAuthService,
+ oidcAuthenticationService: OidcAuthenticationService,
+ router: Router,
+ appConfigService: AppConfigService,
+ dialog: MatDialog,
+ storageService: StorageService) {
+ super(authenticationService, basicAlfrescoAuthService, oidcAuthenticationService, router, appConfigService, dialog, storageService);
this.ticketChangeBind = this.ticketChange.bind(this);
window.addEventListener('storage', this.ticketChangeBind);
diff --git a/lib/core/src/lib/auth/interfaces/authentication-service.interface.ts b/lib/core/src/lib/auth/interfaces/authentication-service.interface.ts
new file mode 100644
index 0000000000..2c9b924129
--- /dev/null
+++ b/lib/core/src/lib/auth/interfaces/authentication-service.interface.ts
@@ -0,0 +1,60 @@
+/*!
+ * @license
+ * Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { HttpHeaders } from '@angular/common/http';
+import ee from 'event-emitter';
+import { Observable } from 'rxjs';
+
+export interface AuthenticationServiceInterface {
+
+ onError: any;
+ onLogin: any;
+ onLogout: any;
+
+ on: ee.EmitterMethod;
+ off: ee.EmitterMethod;
+ once: ee.EmitterMethod;
+ emit: (type: string, ...args: any[]) => void;
+
+ getToken(): string;
+
+ isLoggedIn(): boolean;
+
+ isOauth(): boolean;
+
+ logout(): any;
+
+ isEcmLoggedIn(): boolean;
+
+ isBpmLoggedIn(): boolean;
+
+ isECMProvider(): boolean;
+
+ isBPMProvider(): boolean;
+
+ isALLProvider(): boolean;
+
+ getEcmUsername(): string;
+
+ getBpmUsername(): string;
+
+ getAuthHeaders(requestUrl: string, header: HttpHeaders): HttpHeaders;
+
+ addTokenToHeader(requestUrl: string, headersArg?: HttpHeaders): Observable;
+
+ reset(): void;
+}
diff --git a/lib/core/api/src/lib/clients/index.ts b/lib/core/src/lib/auth/interfaces/authentication.interface.ts
similarity index 76%
rename from lib/core/api/src/lib/clients/index.ts
rename to lib/core/src/lib/auth/interfaces/authentication.interface.ts
index 0855f4f09a..318fa3fe82 100644
--- a/lib/core/api/src/lib/clients/index.ts
+++ b/lib/core/src/lib/auth/interfaces/authentication.interface.ts
@@ -15,6 +15,14 @@
* limitations under the License.
*/
-export * from './activiti/activiti-client.types';
-export * from './alfresco-js-clients.module';
-export * from './discovery/discovery-client.types';
+export interface Authentication {
+ basicAuth?: BasicAuth;
+ cookie?: string;
+ type?: string;
+}
+
+export interface BasicAuth {
+ username?: string;
+ password?: string;
+ ticket?: string;
+}
diff --git a/lib/core/src/lib/auth/oidc/auth.module.ts b/lib/core/src/lib/auth/oidc/auth.module.ts
index d0907f20b8..354edad04a 100644
--- a/lib/core/src/lib/auth/oidc/auth.module.ts
+++ b/lib/core/src/lib/auth/oidc/auth.module.ts
@@ -19,17 +19,12 @@ import { APP_INITIALIZER, ModuleWithProviders, NgModule } from '@angular/core';
import { AuthConfig, AUTH_CONFIG, OAuthModule, OAuthService, OAuthStorage } from 'angular-oauth2-oidc';
import { AlfrescoApiNoAuthService } from '../../api-factories/alfresco-api-no-auth.service';
import { AlfrescoApiService } from '../../services/alfresco-api.service';
-import { AuthGuardBpm } from '../guard/auth-guard-bpm.service';
-import { AuthGuardEcm } from '../guard/auth-guard-ecm.service';
-import { AuthGuard } from '../guard/auth-guard.service';
import { AuthenticationService } from '../services/authentication.service';
import { StorageService } from '../../common/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';
@@ -51,10 +46,10 @@ export function loginFactory(oAuthService: OAuthService, storage: OAuthStorage,
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: AuthGuard, useClass: OidcAuthGuard },
+ // { provide: AuthGuardEcm, useClass: OidcAuthGuard },
+ // { provide: AuthGuardBpm, useClass: OidcAuthGuard },
+ { provide: AuthenticationService},
{ provide: AlfrescoApiService, useClass: AlfrescoApiNoAuthService },
{
provide: AUTH_CONFIG,
diff --git a/lib/core/src/lib/auth/oidc/auth.service.ts b/lib/core/src/lib/auth/oidc/auth.service.ts
index 4c3be8fc7f..be027665ab 100644
--- a/lib/core/src/lib/auth/oidc/auth.service.ts
+++ b/lib/core/src/lib/auth/oidc/auth.service.ts
@@ -22,6 +22,8 @@ import { Observable } from 'rxjs';
* Provide authentication/authorization through OAuth2/OIDC protocol.
*/
export abstract class AuthService {
+ abstract onLogin: Observable;
+
/** Subscribe to whether the user has valid Id/Access tokens. */
abstract authenticated$: Observable;
diff --git a/lib/core/src/lib/auth/oidc/oidc-authentication.service.ts b/lib/core/src/lib/auth/oidc/oidc-authentication.service.ts
deleted file mode 100644
index 0b0ceddc62..0000000000
--- a/lib/core/src/lib/auth/oidc/oidc-authentication.service.ts
+++ /dev/null
@@ -1,134 +0,0 @@
-/*!
- * @license
- * Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { Injectable, inject } from '@angular/core';
-import { OAuthEvent, OAuthService, OAuthStorage } from 'angular-oauth2-oidc';
-import { EMPTY, Observable } from 'rxjs';
-import { catchError, filter, map } from 'rxjs/operators';
-import { AppConfigValues } from '../../app-config/app-config.service';
-import { BaseAuthenticationService } from '../services/base-authentication.service';
-import { JwtHelperService } from '../services/jwt-helper.service';
-import { AuthConfigService } from '../oidc/auth-config.service';
-import { AuthService } from './auth.service';
-
-@Injectable({
- providedIn: 'root'
-})
-export class OIDCAuthenticationService extends BaseAuthenticationService {
- private authStorage = inject(OAuthStorage);
- private oauthService = inject(OAuthService);
- private readonly authConfig = inject(AuthConfigService);
- private readonly auth = inject(AuthService);
-
- readonly supportCodeFlow = true;
-
- constructor() {
- super();
- this.alfrescoApi.alfrescoApiInitialized.subscribe(() => {
- this.oauthService.events.pipe(
- filter((event)=> event.type === 'token_received')
- ).subscribe(()=>{
- this.onLogin.next({});
- });
- });
- }
-
- 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(): boolean {
- return !!this.appConfig.oauth2?.implicitFlow;
- }
-
- isAuthCodeFlow(): boolean {
- return !!this.appConfig.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))
- );
- }
-
- getEcmUsername(): string {
- return (this.oauthService.getIdentityClaims() as any).preferred_username;
- }
-
- getBpmUsername(): string {
- return (this.oauthService.getIdentityClaims() as any).preferred_username;
- }
-
- 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 = this.appConfig.oauth2;
-
- if (config.oidc && oauth2.silentLogin) {
- this.auth.login();
- }
- }
-
- once(event: string): Observable {
- return this.oauthService.events.pipe(filter(_event => _event.type === event));
- }
-}
diff --git a/lib/core/src/lib/auth/oidc/redirect-auth.service.ts b/lib/core/src/lib/auth/oidc/redirect-auth.service.ts
index c8591d0339..96cdc5a5e1 100644
--- a/lib/core/src/lib/auth/oidc/redirect-auth.service.ts
+++ b/lib/core/src/lib/auth/oidc/redirect-auth.service.ts
@@ -19,13 +19,16 @@ 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 { distinctUntilChanged, filter, map, shareReplay } from 'rxjs/operators';
import { AuthService } from './auth.service';
const isPromise = (value: T | Promise): value is Promise => value && typeof (value as Promise).then === 'function';
@Injectable()
export class RedirectAuthService extends AuthService {
+
+ onLogin: Observable;
+
private _loadDiscoveryDocumentPromise = Promise.resolve(false);
/** Subscribe to whether the user has valid Id/Access tokens. */
@@ -52,29 +55,32 @@ export class RedirectAuthService extends AuthService {
) {
super();
this.authConfig = authConfig;
- }
- init() {
this.oauthService.clearHashAfterLogin = true;
this.authenticated$ = this.oauthService.events.pipe(
- startWith(undefined),
map(() => this.authenticated),
distinctUntilChanged(),
shareReplay(1)
);
+ this.onLogin = this.authenticated$.pipe(
+ filter((authenticated) => authenticated),
+ map(() => undefined)
+ );
+
this.idpUnreachable$ = this.oauthService.events.pipe(
filter((event): event is OAuthErrorEvent => event.type === 'discovery_document_load_error'),
map((event) => event.reason as Error)
);
+ }
+ init() {
if (isPromise(this.authConfig)) {
return this.authConfig.then((config) => this.configureAuth(config));
}
return this.configureAuth(this.authConfig);
-
}
logout() {
diff --git a/lib/core/src/lib/auth/oidc/view/authentication-confirmation/authentication-confirmation.component.ts b/lib/core/src/lib/auth/oidc/view/authentication-confirmation/authentication-confirmation.component.ts
index 9492740b35..1bcf03eb95 100644
--- a/lib/core/src/lib/auth/oidc/view/authentication-confirmation/authentication-confirmation.component.ts
+++ b/lib/core/src/lib/auth/oidc/view/authentication-confirmation/authentication-confirmation.component.ts
@@ -24,7 +24,7 @@ import { AuthService } from '../../auth.service';
const ROUTE_DEFAULT = '/';
@Component({
- template: '',
+ template: '',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class AuthenticationConfirmationComponent {
diff --git a/lib/core/src/lib/auth/public-api.ts b/lib/core/src/lib/auth/public-api.ts
index b9cfb15d9c..0cea03f734 100644
--- a/lib/core/src/lib/auth/public-api.ts
+++ b/lib/core/src/lib/auth/public-api.ts
@@ -31,6 +31,10 @@ export * from './services/jwt-helper.service';
export * from './services/oauth2.service';
export * from './services/user-access.service';
+export * from './basic-auth/basic-alfresco-auth.service';
+export * from './basic-auth/process-auth';
+export * from './basic-auth/content-auth';
+
export * from './interfaces/identity-user.service.interface';
export * from './interfaces/identity-group.interface';
export * from './interfaces/openid-configuration.interface';
diff --git a/lib/core/src/lib/auth/services/authentication.service.spec.ts b/lib/core/src/lib/auth/services/authentication.service.spec.ts
index da88ee4263..16f35e5191 100644
--- a/lib/core/src/lib/auth/services/authentication.service.spec.ts
+++ b/lib/core/src/lib/auth/services/authentication.service.spec.ts
@@ -16,21 +16,23 @@
*/
import { fakeAsync, TestBed } from '@angular/core/testing';
-import { AlfrescoApiService } from '../../services/alfresco-api.service';
import { AuthenticationService } from './authentication.service';
import { CookieService } from '../../common/services/cookie.service';
import { AppConfigService } from '../../app-config/app-config.service';
import { setupTestBed } from '../../testing/setup-test-bed';
import { CoreTestingModule } from '../../testing/core.testing.module';
import { TranslateModule } from '@ngx-translate/core';
+import { BasicAlfrescoAuthService } from '../basic-auth/basic-alfresco-auth.service';
+import { OidcAuthenticationService } from './oidc-authentication.service';
declare let jasmine: any;
describe('AuthenticationService', () => {
- let apiService: AlfrescoApiService;
let authService: AuthenticationService;
+ let basicAlfrescoAuthService: BasicAlfrescoAuthService;
let appConfigService: AppConfigService;
let cookie: CookieService;
+ let oidcAuthenticationService: OidcAuthenticationService;
setupTestBed({
imports: [
@@ -42,8 +44,9 @@ describe('AuthenticationService', () => {
beforeEach(() => {
sessionStorage.clear();
localStorage.clear();
- apiService = TestBed.inject(AlfrescoApiService);
authService = TestBed.inject(AuthenticationService);
+ basicAlfrescoAuthService = TestBed.inject(BasicAlfrescoAuthService);
+ oidcAuthenticationService = TestBed.inject(OidcAuthenticationService);
cookie = TestBed.inject(CookieService);
cookie.clear();
@@ -73,6 +76,23 @@ describe('AuthenticationService', () => {
});
appConfigService.load();
});
+
+ it('should kerberos be disabled if is oauth', () => {
+ spyOn(authService, 'isOauth').and.returnValue(true);
+ expect(authService.isKerberosEnabled()).toEqual(false);
+ });
+
+ it('should kerberos not enabled if is oauth is false and basic auth return false', () => {
+ spyOn(authService, 'isOauth').and.returnValue(false);
+ spyOn(basicAlfrescoAuthService, 'isKerberosEnabled').and.returnValue(false);
+ expect(authService.isKerberosEnabled()).toEqual(false);
+ });
+
+ it('should kerberos be enabled if is oauth is false and basic auth return true', () => {
+ spyOn(authService, 'isOauth').and.returnValue(false);
+ spyOn(basicAlfrescoAuthService, 'isKerberosEnabled').and.returnValue(true);
+ expect(authService.isKerberosEnabled()).toEqual(true);
+ });
});
describe('when the setting is ECM', () => {
@@ -83,40 +103,30 @@ describe('AuthenticationService', () => {
appConfigService.config.auth = { withCredentials: false };
appConfigService.config.providers = 'ECM';
appConfigService.load();
- apiService.reset();
});
it('should not require cookie service enabled for ECM check', () => {
spyOn(cookie, 'isEnabled').and.returnValue(false);
- spyOn(authService, 'isRememberMeSet').and.returnValue(false);
+ spyOn(basicAlfrescoAuthService, 'isRememberMeSet').and.returnValue(false);
spyOn(authService, 'isECMProvider').and.returnValue(true);
spyOn(authService, 'isOauth').and.returnValue(false);
- spyOn(apiService, 'getInstance').and.callThrough();
expect(authService.isEcmLoggedIn()).toBeFalsy();
- expect(apiService.getInstance).toHaveBeenCalled();
- });
-
- it('should check if loggedin on ECM in case the provider is ECM', () => {
- spyOn(authService, 'isEcmLoggedIn').and.returnValue(true);
- expect(authService.isLoggedInWith('ECM')).toBe(true);
});
it('should require remember me set for ECM check', () => {
spyOn(cookie, 'isEnabled').and.returnValue(true);
- spyOn(authService, 'isRememberMeSet').and.returnValue(false);
+ spyOn(basicAlfrescoAuthService, 'isRememberMeSet').and.returnValue(false);
spyOn(authService, 'isECMProvider').and.returnValue(true);
spyOn(authService, 'isOauth').and.returnValue(false);
- spyOn(apiService, 'getInstance').and.callThrough();
expect(authService.isEcmLoggedIn()).toBeFalsy();
- expect(apiService.getInstance).not.toHaveBeenCalled();
});
it('[ECM] should return an ECM ticket after the login done', (done) => {
- const disposableLogin = authService.login('fake-username', 'fake-password').subscribe(() => {
+ const disposableLogin = basicAlfrescoAuthService.login('fake-username', 'fake-password').subscribe(() => {
expect(authService.isLoggedIn()).toBe(true);
- expect(authService.getTicketEcm()).toEqual('fake-post-ticket');
+ expect(authService.getToken()).toEqual('fake-post-ticket');
expect(authService.isEcmLoggedIn()).toBe(true);
disposableLogin.unsubscribe();
done();
@@ -130,7 +140,7 @@ describe('AuthenticationService', () => {
});
it('[ECM] should login in the ECM if no provider are defined calling the login', fakeAsync(() => {
- const disposableLogin = authService.login('fake-username', 'fake-password').subscribe((loginResponse) => {
+ const disposableLogin = basicAlfrescoAuthService.login('fake-username', 'fake-password').subscribe((loginResponse) => {
expect(loginResponse).toEqual(fakeECMLoginResponse);
disposableLogin.unsubscribe();
});
@@ -143,10 +153,10 @@ describe('AuthenticationService', () => {
}));
it('[ECM] should return a ticket undefined after logout', fakeAsync(() => {
- const disposableLogin = authService.login('fake-username', 'fake-password').subscribe(() => {
+ const disposableLogin = basicAlfrescoAuthService.login('fake-username', 'fake-password').subscribe(() => {
const disposableLogout = authService.logout().subscribe(() => {
expect(authService.isLoggedIn()).toBe(false);
- expect(authService.getTicketEcm()).toBe(null);
+ expect(authService.getToken()).toBe(null);
expect(authService.isEcmLoggedIn()).toBe(false);
disposableLogin.unsubscribe();
disposableLogout.unsubscribe();
@@ -170,21 +180,21 @@ describe('AuthenticationService', () => {
});
it('[ECM] should set/get redirectUrl when provider is ECM', () => {
- authService.setRedirect({ provider: 'ECM', url: 'some-url' });
+ basicAlfrescoAuthService.setRedirect({ provider: 'ECM', url: 'some-url' });
- expect(authService.getRedirect()).toEqual('some-url');
+ expect(basicAlfrescoAuthService.getRedirect()).toEqual('some-url');
});
it('[ECM] should set/get redirectUrl when provider is BPM', () => {
- authService.setRedirect({ provider: 'BPM', url: 'some-url' });
+ basicAlfrescoAuthService.setRedirect({ provider: 'BPM', url: 'some-url' });
- expect(authService.getRedirect()).toBeNull();
+ expect(basicAlfrescoAuthService.getRedirect()).toBeNull();
});
it('[ECM] should return null as redirectUrl when redirectUrl field is not set', () => {
- authService.setRedirect(null);
+ basicAlfrescoAuthService.setRedirect(null);
- expect(authService.getRedirect()).toBeNull();
+ expect(basicAlfrescoAuthService.getRedirect()).toBeNull();
});
it('[ECM] should return isECMProvider true', () => {
@@ -209,40 +219,30 @@ describe('AuthenticationService', () => {
beforeEach(() => {
appConfigService.config.providers = 'BPM';
appConfigService.load();
- apiService.reset();
});
it('should require remember me set for BPM check', () => {
spyOn(cookie, 'isEnabled').and.returnValue(true);
- spyOn(authService, 'isRememberMeSet').and.returnValue(false);
+ spyOn(basicAlfrescoAuthService, 'isRememberMeSet').and.returnValue(false);
spyOn(authService, 'isBPMProvider').and.returnValue(true);
spyOn(authService, 'isOauth').and.returnValue(false);
- spyOn(apiService, 'getInstance').and.callThrough();
expect(authService.isBpmLoggedIn()).toBeFalsy();
- expect(apiService.getInstance).not.toHaveBeenCalled();
- });
-
- it('should check if loggedin on BPM in case the provider is BPM', () => {
- spyOn(authService, 'isBpmLoggedIn').and.returnValue(true);
- expect(authService.isLoggedInWith('BPM')).toBe(true);
});
it('should not require cookie service enabled for BPM check', () => {
spyOn(cookie, 'isEnabled').and.returnValue(false);
- spyOn(authService, 'isRememberMeSet').and.returnValue(false);
+ spyOn(basicAlfrescoAuthService, 'isRememberMeSet').and.returnValue(false);
spyOn(authService, 'isBPMProvider').and.returnValue(true);
- spyOn(apiService, 'getInstance').and.callThrough();
expect(authService.isBpmLoggedIn()).toBeFalsy();
- expect(apiService.getInstance).toHaveBeenCalled();
});
it('[BPM] should return an BPM ticket after the login done', (done) => {
- const disposableLogin = authService.login('fake-username', 'fake-password').subscribe(() => {
+ const disposableLogin = basicAlfrescoAuthService.login('fake-username', 'fake-password').subscribe(() => {
expect(authService.isLoggedIn()).toBe(true);
// cspell: disable-next
- expect(authService.getTicketBpm()).toEqual('Basic ZmFrZS11c2VybmFtZTpmYWtlLXBhc3N3b3Jk');
+ expect(authService.getToken()).toEqual('Basic ZmFrZS11c2VybmFtZTpmYWtlLXBhc3N3b3Jk');
expect(authService.isBpmLoggedIn()).toBe(true);
disposableLogin.unsubscribe();
done();
@@ -255,10 +255,10 @@ describe('AuthenticationService', () => {
});
it('[BPM] should return a ticket undefined after logout', (done) => {
- const disposableLogin = authService.login('fake-username', 'fake-password').subscribe(() => {
+ const disposableLogin = basicAlfrescoAuthService.login('fake-username', 'fake-password').subscribe(() => {
const disposableLogout = authService.logout().subscribe(() => {
expect(authService.isLoggedIn()).toBe(false);
- expect(authService.getTicketBpm()).toBe(null);
+ expect(authService.getToken()).toBe(null);
expect(authService.isBpmLoggedIn()).toBe(false);
disposableLogout.unsubscribe();
disposableLogin.unsubscribe();
@@ -281,7 +281,7 @@ describe('AuthenticationService', () => {
},
(err: any) => {
expect(err).toBeDefined();
- expect(authService.getTicketBpm()).toBe(undefined);
+ expect(authService.getToken()).toBe(null);
done();
});
@@ -291,21 +291,21 @@ describe('AuthenticationService', () => {
});
it('[BPM] should set/get redirectUrl when provider is BPM', () => {
- authService.setRedirect({ provider: 'BPM', url: 'some-url' });
+ basicAlfrescoAuthService.setRedirect({ provider: 'BPM', url: 'some-url' });
- expect(authService.getRedirect()).toEqual('some-url');
+ expect(basicAlfrescoAuthService.getRedirect()).toEqual('some-url');
});
it('[BPM] should set/get redirectUrl when provider is ECM', () => {
- authService.setRedirect({ provider: 'ECM', url: 'some-url' });
+ basicAlfrescoAuthService.setRedirect({ provider: 'ECM', url: 'some-url' });
- expect(authService.getRedirect()).toBeNull();
+ expect(basicAlfrescoAuthService.getRedirect()).toBeNull();
});
it('[BPM] should return null as redirectUrl when redirectUrl field is not set', () => {
- authService.setRedirect(null);
+ basicAlfrescoAuthService.setRedirect(null);
- expect(authService.getRedirect()).toBeNull();
+ expect(basicAlfrescoAuthService.getRedirect()).toBeNull();
});
it('[BPM] should return isECMProvider false', () => {
@@ -326,11 +326,10 @@ describe('AuthenticationService', () => {
beforeEach(() => {
appConfigService.config.providers = 'ECM';
appConfigService.load();
- apiService.reset();
});
it('[ECM] should save the remember me cookie as a session cookie after successful login', (done) => {
- const disposableLogin = authService.login('fake-username', 'fake-password', false).subscribe(() => {
+ const disposableLogin = basicAlfrescoAuthService.login('fake-username', 'fake-password', false).subscribe(() => {
expect(cookie['ALFRESCO_REMEMBER_ME']).not.toBeUndefined();
expect(cookie['ALFRESCO_REMEMBER_ME'].expiration).toBeNull();
disposableLogin.unsubscribe();
@@ -345,7 +344,7 @@ describe('AuthenticationService', () => {
});
it('[ECM] should save the remember me cookie as a persistent cookie after successful login', (done) => {
- const disposableLogin = authService.login('fake-username', 'fake-password', true).subscribe(() => {
+ const disposableLogin = basicAlfrescoAuthService.login('fake-username', 'fake-password', true).subscribe(() => {
expect(cookie['ALFRESCO_REMEMBER_ME']).not.toBeUndefined();
expect(cookie['ALFRESCO_REMEMBER_ME'].expiration).not.toBeNull();
disposableLogin.unsubscribe();
@@ -361,7 +360,7 @@ describe('AuthenticationService', () => {
});
it('[ECM] should not save the remember me cookie after failed login', (done) => {
- const disposableLogin = authService.login('fake-username', 'fake-password').subscribe(
+ const disposableLogin = basicAlfrescoAuthService.login('fake-username', 'fake-password').subscribe(
() => {},
() => {
expect(cookie['ALFRESCO_REMEMBER_ME']).toBeUndefined();
@@ -390,15 +389,14 @@ describe('AuthenticationService', () => {
beforeEach(() => {
appConfigService.config.providers = 'ALL';
appConfigService.load();
- apiService.reset();
});
it('[ALL] should return both ECM and BPM tickets after the login done', (done) => {
- const disposableLogin = authService.login('fake-username', 'fake-password').subscribe(() => {
+ const disposableLogin = basicAlfrescoAuthService.login('fake-username', 'fake-password').subscribe(() => {
expect(authService.isLoggedIn()).toBe(true);
- expect(authService.getTicketEcm()).toEqual('fake-post-ticket');
+ expect(basicAlfrescoAuthService.getTicketEcm()).toEqual('fake-post-ticket');
// cspell: disable-next
- expect(authService.getTicketBpm()).toEqual('Basic ZmFrZS11c2VybmFtZTpmYWtlLXBhc3N3b3Jk');
+ expect(basicAlfrescoAuthService.getTicketBpm()).toEqual('Basic ZmFrZS11c2VybmFtZTpmYWtlLXBhc3N3b3Jk');
expect(authService.isBpmLoggedIn()).toBe(true);
expect(authService.isEcmLoggedIn()).toBe(true);
disposableLogin.unsubscribe();
@@ -417,13 +415,13 @@ describe('AuthenticationService', () => {
});
it('[ALL] should return login fail if only ECM call fail', (done) => {
- const disposableLogin = authService.login('fake-username', 'fake-password').subscribe(
+ const disposableLogin = basicAlfrescoAuthService.login('fake-username', 'fake-password').subscribe(
() => {},
() => {
expect(authService.isLoggedIn()).toBe(false, 'isLoggedIn');
- expect(authService.getTicketEcm()).toBe(null, 'getTicketEcm');
+ expect(authService.getToken()).toBe(null, 'getTicketEcm');
// cspell: disable-next
- expect(authService.getTicketBpm()).toBe(null, 'getTicketBpm');
+ expect(authService.getToken()).toBe(null, 'getTicketBpm');
expect(authService.isEcmLoggedIn()).toBe(false, 'isEcmLoggedIn');
disposableLogin.unsubscribe();
done();
@@ -439,12 +437,12 @@ describe('AuthenticationService', () => {
});
it('[ALL] should return login fail if only BPM call fail', (done) => {
- const disposableLogin = authService.login('fake-username', 'fake-password').subscribe(
+ const disposableLogin = basicAlfrescoAuthService.login('fake-username', 'fake-password').subscribe(
() => {},
() => {
expect(authService.isLoggedIn()).toBe(false);
- expect(authService.getTicketEcm()).toBe(null);
- expect(authService.getTicketBpm()).toBe(null);
+ expect(authService.getToken()).toBe(null);
+ expect(authService.getToken()).toBe(null);
expect(authService.isBpmLoggedIn()).toBe(false);
disposableLogin.unsubscribe();
done();
@@ -462,12 +460,12 @@ describe('AuthenticationService', () => {
});
it('[ALL] should return ticket undefined when the credentials are wrong', (done) => {
- const disposableLogin = authService.login('fake-username', 'fake-password').subscribe(
+ const disposableLogin = basicAlfrescoAuthService.login('fake-username', 'fake-password').subscribe(
() => {},
() => {
expect(authService.isLoggedIn()).toBe(false);
- expect(authService.getTicketEcm()).toBe(null);
- expect(authService.getTicketBpm()).toBe(null);
+ expect(authService.getToken()).toBe(null);
+ expect(authService.getToken()).toBe(null);
expect(authService.isBpmLoggedIn()).toBe(false);
expect(authService.isEcmLoggedIn()).toBe(false);
disposableLogin.unsubscribe();
@@ -483,30 +481,6 @@ describe('AuthenticationService', () => {
});
});
- it('[ALL] should set/get redirectUrl when provider is ALL', () => {
- authService.setRedirect({ provider: 'ALL', url: 'some-url' });
-
- expect(authService.getRedirect()).toEqual('some-url');
- });
-
- it('[ALL] should set/get redirectUrl when provider is BPM', () => {
- authService.setRedirect({ provider: 'BPM', url: 'some-url' });
-
- expect(authService.getRedirect()).toEqual('some-url');
- });
-
- it('[ALL] should set/get redirectUrl when provider is ECM', () => {
- authService.setRedirect({ provider: 'ECM', url: 'some-url' });
-
- expect(authService.getRedirect()).toEqual('some-url');
- });
-
- it('[ALL] should return null as redirectUrl when redirectUrl field is not set', () => {
- authService.setRedirect(null);
-
- expect(authService.getRedirect()).toBeNull();
- });
-
it('[ALL] should return isECMProvider false', () => {
expect(authService.isECMProvider()).toBe(false);
});
@@ -519,4 +493,22 @@ describe('AuthenticationService', () => {
expect(authService.isALLProvider()).toBe(true);
});
});
+
+ describe('getUsername', () => {
+ it('should get the username of the authenticated user if isOAuth is true', () => {
+ spyOn(authService, 'isOauth').and.returnValue(true);
+ spyOn(oidcAuthenticationService, 'getUsername').and.returnValue('mike.portnoy');
+ const username = authService.getUsername();
+ expect(username).toEqual('mike.portnoy');
+ });
+
+ it('should get the username of the authenticated user if isOAuth is false', () => {
+ spyOn(authService, 'isOauth').and.returnValue(false);
+ spyOn(oidcAuthenticationService, 'getUsername').and.returnValue('mike.portnoy');
+ spyOn(basicAlfrescoAuthService, 'getUsername').and.returnValue('john.petrucci');
+ const username = authService.getUsername();
+ expect(username).toEqual('john.petrucci');
+ });
+
+ });
});
diff --git a/lib/core/src/lib/auth/services/authentication.service.ts b/lib/core/src/lib/auth/services/authentication.service.ts
index b7d00256d9..0aa1e00c04 100644
--- a/lib/core/src/lib/auth/services/authentication.service.ts
+++ b/lib/core/src/lib/auth/services/authentication.service.ts
@@ -15,184 +15,195 @@
* limitations under the License.
*/
-import { Injectable, inject } from '@angular/core';
-import { Observable, from } from 'rxjs';
-import { AppConfigValues } from '../../app-config/app-config.service';
-import { map, catchError, tap } from 'rxjs/operators';
-import { JwtHelperService } from './jwt-helper.service';
-import { StorageService } from '../../common/services/storage.service';
-import { BaseAuthenticationService } from './base-authentication.service';
+import { Injectable, Injector } from '@angular/core';
+import { OidcAuthenticationService } from './oidc-authentication.service';
+import { BasicAlfrescoAuthService } from '../basic-auth/basic-alfresco-auth.service';
+import { Observable, Subject, from } from 'rxjs';
+import { HttpHeaders } from '@angular/common/http';
+import { AuthenticationServiceInterface } from '../interfaces/authentication-service.interface';
+import ee from 'event-emitter';
+import { RedirectAuthService } from '../oidc/redirect-auth.service';
@Injectable({
providedIn: 'root'
})
-export class AuthenticationService extends BaseAuthenticationService {
- private storageService = inject(StorageService);
- readonly supportCodeFlow = false;
+export class AuthenticationService implements AuthenticationServiceInterface, ee.Emitter {
- constructor() {
- super();
- this.alfrescoApi.alfrescoApiInitialized.subscribe(() => {
- this.alfrescoApi.getInstance().reply('logged-in', () => {
- this.onLogin.next();
- });
- });
- }
+ onLogin: Subject = new Subject();
+ onLogout: Subject = new Subject();
- /**
- * Checks if the user logged in.
- *
- * @returns True if logged in, false otherwise
- */
- isLoggedIn(): boolean {
- if (!this.isOauth() && this.cookie.isEnabled() && !this.isRememberMeSet()) {
- return false;
- }
- return this.alfrescoApi.getInstance().isLoggedIn();
- }
+ constructor(
+ private injector: Injector,
+ private redirectAuthService: RedirectAuthService
+ ) {
+ this.redirectAuthService.onLogin.subscribe(
+ (value) => this.onLogin.next(value)
+ );
- isLoggedInWith(provider: string): boolean {
- if (provider === 'BPM') {
- return this.isBpmLoggedIn();
- } else if (provider === 'ECM') {
- return this.isEcmLoggedIn();
+ this.basicAlfrescoAuthService.onLogin.subscribe(
+ (value) => this.onLogin.next(value)
+ );
+
+ if (this.isOauth()) {
+ this.oidcAuthenticationService.onLogout.subscribe(
+ (value) => this.onLogout.next(value)
+ );
} else {
- return this.isLoggedIn();
+ this.basicAlfrescoAuthService.onLogout.subscribe(
+ (value) => this.onLogout.next(value)
+ );
}
}
- /**
- * Does the provider support OAuth?
- *
- * @returns True if supported, false otherwise
- */
- isOauth(): boolean {
- return this.alfrescoApi.getInstance().isOauthConfiguration();
+ get on(): ee.EmitterMethod {
+ return this.isOauth() ? this.oidcAuthenticationService.on : this.basicAlfrescoAuthService.on;
}
- /**
- * Logs the user in.
- *
- * @param username Username for the login
- * @param password Password for the login
- * @param rememberMe Stores the user's login details if true
- * @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 }> {
- return from(this.alfrescoApi.getInstance().login(username, password)).pipe(
- map((response: any) => {
- this.saveRememberMeCookie(rememberMe);
- this.onLogin.next(response);
- return {
- type: this.appConfig.get(AppConfigValues.PROVIDERS),
- ticket: response
- };
- }),
- catchError((err) => this.handleError(err))
- );
+ get off(): ee.EmitterMethod {
+ return this.isOauth() ? this.oidcAuthenticationService.off : this.basicAlfrescoAuthService.off;
}
- /**
- * Logs the user in with SSO
- */
- ssoImplicitLogin() {
- this.alfrescoApi.getInstance().implicitLogin();
+ get once(): ee.EmitterMethod {
+ return this.isOauth() ? this.oidcAuthenticationService.once : this.basicAlfrescoAuthService.once;
}
-
- /**
- * Logs the user out.
- *
- * @returns Response event called when logout is complete
- */
- logout() {
- return from(this.callApiLogout()).pipe(
- tap((response) => {
- this.onLogout.next(response);
- return response;
- }),
- catchError((err) => this.handleError(err))
- );
+ get emit(): (type: string, ...args: any[]) => void {
+ return this.isOauth() ? this.oidcAuthenticationService.emit : this.basicAlfrescoAuthService.emit;
}
- private callApiLogout(): Promise {
- if (this.alfrescoApi.getInstance()) {
- return this.alfrescoApi.getInstance().logout();
+ get onError(): Observable {
+ return this.isOauth() ? this.oidcAuthenticationService.onError : this.basicAlfrescoAuthService.onError;
+ }
+
+ addTokenToHeader(requestUrl: string, headersArg?: HttpHeaders): Observable {
+ return this.isOauth() ? this.oidcAuthenticationService.addTokenToHeader(requestUrl, headersArg) : this.basicAlfrescoAuthService.addTokenToHeader(requestUrl, headersArg);
+ }
+
+ isECMProvider(): boolean {
+ return this.isOauth() ? this.oidcAuthenticationService.isECMProvider() : this.basicAlfrescoAuthService.isECMProvider();
+ }
+
+ isBPMProvider(): boolean {
+ return this.isOauth() ? this.oidcAuthenticationService.isBPMProvider() : this.basicAlfrescoAuthService.isBPMProvider();
+ }
+
+ isALLProvider(): boolean {
+ return this.isOauth() ? this.oidcAuthenticationService.isALLProvider() : this.basicAlfrescoAuthService.isALLProvider();
+ }
+
+ private get oidcAuthenticationService(): OidcAuthenticationService {
+ return this.injector.get(OidcAuthenticationService);
+ }
+
+ private get basicAlfrescoAuthService(): BasicAlfrescoAuthService {
+ return this.injector.get(BasicAlfrescoAuthService);
+ }
+
+ getToken(): string {
+ if (this.isOauth()) {
+ return this.oidcAuthenticationService.getToken();
+ } else {
+ return this.basicAlfrescoAuthService.getToken();
+ }
+ }
+
+ isLoggedIn(): boolean {
+ if (this.isOauth()) {
+ return this.oidcAuthenticationService.isLoggedIn();
+ } else {
+ return this.basicAlfrescoAuthService.isLoggedIn();
+ }
+ }
+
+ logout(): Observable {
+ if (this.isOauth()) {
+ return this.oidcAuthenticationService.logout();
+ } else {
+ return from(this.basicAlfrescoAuthService.logout());
}
- return Promise.resolve();
}
- /**
- * Checks if the user is logged in on an ECM provider.
- *
- * @returns True if logged in, false otherwise
- */
isEcmLoggedIn(): boolean {
- if (this.isECMProvider() || this.isALLProvider()) {
- if (!this.isOauth() && this.cookie.isEnabled() && !this.isRememberMeSet()) {
- return false;
- }
- return this.alfrescoApi.getInstance().isEcmLoggedIn();
+ if (this.isOauth()) {
+ return this.oidcAuthenticationService.isEcmLoggedIn();
+ } else {
+ return this.basicAlfrescoAuthService.isEcmLoggedIn();
}
- return false;
}
- /**
- * Checks if the user is logged in on a BPM provider.
- *
- * @returns True if logged in, false otherwise
- */
isBpmLoggedIn(): boolean {
- if (this.isBPMProvider() || this.isALLProvider()) {
- if (!this.isOauth() && this.cookie.isEnabled() && !this.isRememberMeSet()) {
- return false;
- }
- return this.alfrescoApi.getInstance().isBpmLoggedIn();
+ if (this.isOauth()) {
+ return this.oidcAuthenticationService.isBpmLoggedIn();
+ } else {
+ return this.basicAlfrescoAuthService.isBpmLoggedIn();
+ }
+ }
+
+ reset(): void {
+ if (this.isOauth()) {
+ return this.oidcAuthenticationService.reset();
+ } else {
+ return this.basicAlfrescoAuthService.reset();
+ }
+ }
+
+ login(username: string, password: string, rememberMe?: boolean): Observable<{ type: string; ticket: any }> {
+ if (this.isOauth()) {
+ return this.oidcAuthenticationService.loginWithPassword(username, password);
+ } else {
+ return this.basicAlfrescoAuthService.login(username, password, rememberMe);
}
- return false;
}
/**
- * Gets the ECM username.
- *
- * @returns The ECM username
+ * @returns the username of the authenticated user
+ */
+ getUsername(): string {
+ if (this.isOauth()) {
+ return this.oidcAuthenticationService.getUsername();
+ } else {
+ return this.basicAlfrescoAuthService.getUsername();
+ }
+ }
+
+ /**
+ * @deprecated
+ * @returns the logged username
*/
getEcmUsername(): string {
- return this.alfrescoApi.getInstance().getEcmUsername();
+ if (this.isOauth()) {
+ return this.oidcAuthenticationService.getUsername();
+ } else {
+ return this.basicAlfrescoAuthService.getEcmUsername();
+ }
}
/**
- * Gets the BPM username
- *
- * @returns The BPM username
+ * @deprecated
+ * @returns the logged username
*/
getBpmUsername(): string {
- return this.alfrescoApi.getInstance().getBpmUsername();
+ if (this.isOauth()) {
+ return this.oidcAuthenticationService.getUsername();
+ } else {
+ return this.basicAlfrescoAuthService.getBpmUsername();
+ }
}
- isImplicitFlow(): boolean {
- return !!this.appConfig.oauth2?.implicitFlow;
+ getAuthHeaders(requestUrl: string, headers: HttpHeaders): HttpHeaders {
+ if (this.isOauth()) {
+ return this.oidcAuthenticationService.getAuthHeaders(requestUrl, headers);
+ } else {
+ return this.basicAlfrescoAuthService.getAuthHeaders(requestUrl, headers);
+ }
}
- isAuthCodeFlow(): boolean {
- return false;
+ isOauth(): boolean {
+ return this.basicAlfrescoAuthService.isOauth();
}
- /**
- * Gets the auth token.
- *
- * @returns Auth token string
- */
- getToken(): string {
- return this.storageService.getItem(JwtHelperService.USER_ACCESS_TOKEN);
+ isKerberosEnabled(): boolean {
+ return !this.isOauth() ? this.basicAlfrescoAuthService.isKerberosEnabled() : false;
}
- reset() { }
-
- once(event: string): Observable {
- const alfrescoApiEvent = event === 'token_received' ? 'token_issued' : event;
- return new Observable((subscriber) => {
- this.alfrescoApi.getInstance().oauth2Auth.once(alfrescoApiEvent, () => subscriber.next());
- });
- }
}
diff --git a/lib/core/src/lib/auth/services/base-authentication.service.ts b/lib/core/src/lib/auth/services/base-authentication.service.ts
index 3e6d618c02..eaa548b20c 100644
--- a/lib/core/src/lib/auth/services/base-authentication.service.ts
+++ b/lib/core/src/lib/auth/services/base-authentication.service.ts
@@ -15,76 +15,70 @@
* limitations under the License.
*/
-import { PeopleApi, UserProfileApi } from '@alfresco/js-api';
import { HttpHeaders } from '@angular/common/http';
import { RedirectionModel } from '../models/redirection.model';
import { Observable, Observer, ReplaySubject, throwError } from 'rxjs';
import { AppConfigService, AppConfigValues } from '../../app-config/app-config.service';
-import { AlfrescoApiService } from '../../services/alfresco-api.service';
import { CookieService } from '../../common/services/cookie.service';
import { LogService } from '../../common/services/log.service';
-import { inject } from '@angular/core';
+import { AuthenticationServiceInterface } from '../interfaces/authentication-service.interface';
+import ee from 'event-emitter';
-const REMEMBER_ME_COOKIE_KEY = 'ALFRESCO_REMEMBER_ME';
-const REMEMBER_ME_UNTIL = 1000 * 60 * 60 * 24 * 30;
+export abstract class BaseAuthenticationService implements AuthenticationServiceInterface, ee.Emitter {
-export abstract class BaseAuthenticationService {
- protected alfrescoApi = inject(AlfrescoApiService);
- protected appConfig = inject(AppConfigService);
- protected cookie = inject(CookieService);
- private logService = inject(LogService);
+ on: ee.EmitterMethod;
+ off: ee.EmitterMethod;
+ once: ee.EmitterMethod;
+ emit: (type: string, ...args: any[]) => void;
- protected bearerExcludedUrls: readonly string[] = ['resources/', 'assets/', 'auth/realms', 'idp/'];
protected redirectUrl: RedirectionModel = null;
+ onError = new ReplaySubject(1);
onLogin = new ReplaySubject(1);
onLogout = new ReplaySubject(1);
- private _peopleApi: PeopleApi;
- get peopleApi(): PeopleApi {
- this._peopleApi = this._peopleApi ?? new PeopleApi(this.alfrescoApi.getInstance());
- return this._peopleApi;
+ constructor(
+ protected appConfig: AppConfigService,
+ protected cookie: CookieService,
+ private logService: LogService
+ ) {
+ ee(this);
}
- private _profileApi: UserProfileApi;
- get profileApi(): UserProfileApi {
- this._profileApi = this._profileApi ?? new UserProfileApi(this.alfrescoApi.getInstance());
- return this._profileApi;
- }
+ abstract getAuthHeaders(requestUrl: string, header: HttpHeaders): HttpHeaders;
- abstract readonly supportCodeFlow: boolean;
abstract getToken(): string;
- abstract isLoggedIn(): boolean;
- abstract isLoggedInWith(provider: string): boolean;
- abstract isOauth(): boolean;
- abstract login(username: string, password: string, rememberMe?: boolean): Observable<{ type: string; ticket: any }>;
- abstract ssoImplicitLogin(): void;
- abstract logout(): Observable;
- abstract isEcmLoggedIn(): boolean;
- abstract isBpmLoggedIn(): boolean;
- abstract getEcmUsername(): string;
- abstract getBpmUsername(): string;
- abstract reset(): void;
- abstract once(event: string): Observable;
- getBearerExcludedUrls(): readonly string[] {
- return this.bearerExcludedUrls;
- }
+ abstract isLoggedIn(): boolean;
+
+ abstract logout(): any;
+
+ abstract isEcmLoggedIn(): boolean;
+
+ abstract isBpmLoggedIn(): boolean;
+
+ abstract reset(): void;
+
+ abstract getEcmUsername(): string;
+
+ abstract getBpmUsername(): string;
/**
* Adds the auth token to an HTTP header using the 'bearer' scheme.
*
+ * @param requestUrl the request url
* @param headersArg Header that will receive the token
* @returns The new header with the token added
*/
- addTokenToHeader(headersArg?: HttpHeaders): Observable {
+ addTokenToHeader(requestUrl: string, headersArg?: HttpHeaders): Observable {
return new Observable((observer: Observer) => {
let headers = headersArg;
if (!headers) {
headers = new HttpHeaders();
}
try {
- const header = this.getAuthHeaders(headers);
+
+ const header = this.getAuthHeaders(requestUrl, headers);
observer.next(header);
observer.complete();
@@ -94,50 +88,9 @@ export abstract class BaseAuthenticationService {
});
}
- private getAuthHeaders(header: HttpHeaders): HttpHeaders {
- const authType = this.appConfig.get(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);
- }
-
- 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();
+ const provider = this.appConfig.get('providers') as string;
+ return provider && provider.toUpperCase() === 'ECM';
}
/**
@@ -146,7 +99,12 @@ export abstract class BaseAuthenticationService {
* @returns True if supported, false otherwise
*/
isBPMProvider(): boolean {
- return this.alfrescoApi.getInstance().isBpmConfiguration();
+ const provider = this.appConfig.get('providers');
+ if (provider && (typeof provider === 'string' || provider instanceof String)) {
+ return provider.toUpperCase() === 'BPM';
+ } else {
+ return false;
+ }
}
/**
@@ -155,38 +113,13 @@ export abstract class BaseAuthenticationService {
* @returns True if both are supported, false otherwise
*/
isALLProvider(): boolean {
- return this.alfrescoApi.getInstance().isEcmBpmConfiguration();
+ const provider = this.appConfig.get('providers') as string;
+ return provider && provider.toUpperCase() === 'ALL';
}
- /**
- * 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;
+ isOauthConfiguration(): boolean {
+ const authType = this.appConfig.get('authType') as string;
+ return authType === 'OAUTH';
}
/**
@@ -196,64 +129,12 @@ export abstract class BaseAuthenticationService {
* @returns Object representing the error message
*/
handleError(error: any): Observable {
+ this.onError.next(error || 'Server error');
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(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(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');
+ isOauth(): boolean {
+ return this.appConfig.get(AppConfigValues.AUTHTYPE) === 'OAUTH';
}
}
diff --git a/lib/core/src/lib/auth/services/identity-user.service.spec.ts b/lib/core/src/lib/auth/services/identity-user.service.spec.ts
index 05407fef13..62dce42245 100644
--- a/lib/core/src/lib/auth/services/identity-user.service.spec.ts
+++ b/lib/core/src/lib/auth/services/identity-user.service.spec.ts
@@ -35,6 +35,7 @@ import { IdentityRoleModel } from '../models/identity-role.model';
import { CoreTestingModule } from '../../testing/core.testing.module';
import { TranslateModule } from '@ngx-translate/core';
import { AdfHttpClient } from '../../../../api/src';
+import { StorageService } from '../../common/services/storage.service';
describe('IdentityUserService', () => {
@@ -46,6 +47,7 @@ describe('IdentityUserService', () => {
{ id: 'id-5', name: 'MOCK-ROLE-2'}
];
+ let storageService: StorageService;
let service: IdentityUserService;
let adfHttpClient: AdfHttpClient;
let requestSpy: jasmine.Spy;
@@ -57,18 +59,14 @@ describe('IdentityUserService', () => {
CoreTestingModule
]
});
+ storageService = TestBed.inject(StorageService);
service = TestBed.inject(IdentityUserService);
adfHttpClient = TestBed.inject(AdfHttpClient);
requestSpy = spyOn(adfHttpClient, 'request');
-
- const store = {};
-
- spyOn(localStorage, 'getItem').and.callFake( (key: string): string => store[key] || null);
- spyOn(localStorage, 'setItem').and.callFake((key: string, value: string): string => store[key] = value);
});
it('should fetch identity user info from Jwt id token', () => {
- localStorage.setItem(JwtHelperService.USER_ID_TOKEN, mockToken);
+ storageService.setItem(JwtHelperService.USER_ID_TOKEN, mockToken);
const user = service.getCurrentUserInfo();
expect(user).toBeDefined();
expect(user.firstName).toEqual('John');
@@ -78,7 +76,7 @@ describe('IdentityUserService', () => {
});
it('should fallback on Jwt access token for identity user info', () => {
- localStorage.setItem(JwtHelperService.USER_ACCESS_TOKEN, mockToken);
+ storageService.setItem(JwtHelperService.USER_ACCESS_TOKEN, mockToken);
const user = service.getCurrentUserInfo();
expect(user).toBeDefined();
expect(user.firstName).toEqual('John');
diff --git a/lib/core/src/lib/auth/services/oidc-authentication.service.ts b/lib/core/src/lib/auth/services/oidc-authentication.service.ts
new file mode 100644
index 0000000000..508e1c3abe
--- /dev/null
+++ b/lib/core/src/lib/auth/services/oidc-authentication.service.ts
@@ -0,0 +1,194 @@
+/*!
+ * @license
+ * Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { Injectable } from '@angular/core';
+import { OAuthService, OAuthStorage } from 'angular-oauth2-oidc';
+import { EMPTY, Observable, defer } 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 { BaseAuthenticationService } from './base-authentication.service';
+import { CookieService } from '../../common/services/cookie.service';
+import { JwtHelperService } from './jwt-helper.service';
+import { LogService } from '../../common/services/log.service';
+import { AuthConfigService } from '../oidc/auth-config.service';
+import { AuthService } from '../oidc/auth.service';
+import { Minimatch } from 'minimatch';
+import { HttpHeaders } from '@angular/common/http';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class OidcAuthenticationService extends BaseAuthenticationService {
+
+ constructor(
+ appConfig: AppConfigService,
+ cookie: CookieService,
+ logService: LogService,
+ private jwtHelperService: JwtHelperService,
+ private authStorage: OAuthStorage,
+ private oauthService: OAuthService,
+ private readonly authConfig: AuthConfigService,
+ private readonly auth: AuthService
+ ) {
+ super(appConfig, cookie, logService);
+ }
+
+ isEcmLoggedIn(): boolean {
+ if (this.isECMProvider() || this.isALLProvider()) {
+ return this.isLoggedIn();
+ }
+ return false;
+
+ }
+
+ isBpmLoggedIn(): boolean {
+ if (this.isBPMProvider() || this.isALLProvider()) {
+ return this.isLoggedIn();
+ }
+ return false;
+ }
+
+ isLoggedIn(): boolean {
+ return this.oauthService.hasValidAccessToken() && this.oauthService.hasValidIdToken();
+ }
+
+ hasValidAccessToken(): boolean {
+ return this.oauthService.hasValidAccessToken();
+ }
+
+ hasValidIdToken(): boolean {
+ return this.oauthService.hasValidIdToken();
+ }
+
+ isImplicitFlow() {
+ const oauth2: OauthConfigModel = Object.assign({}, this.appConfig.get(AppConfigValues.OAUTHCONFIG, null));
+ return !!oauth2?.implicitFlow;
+ }
+
+ isAuthCodeFlow() {
+ const oauth2: OauthConfigModel = Object.assign({}, this.appConfig.get(AppConfigValues.OAUTHCONFIG, null));
+ return !!oauth2?.codeFlow;
+ }
+
+ login(username: string, password: string): Observable<{ type: string; ticket: any }> {
+ return this.auth.baseAuthLogin(username, password).pipe(
+ map((response) => {
+ this.onLogin.next(response);
+ return {
+ type: this.appConfig.get(AppConfigValues.PROVIDERS),
+ ticket: response
+ };
+ }),
+ catchError((err) => this.handleError(err))
+ );
+ }
+
+ loginWithPassword(username: string, password: string): Observable<{ type: string; ticket: any }> {
+ return defer(async () => {
+ try {
+ await this.authConfig.loadConfig();
+ await this.oauthService.loadDiscoveryDocument();
+ await this.oauthService.fetchTokenUsingPasswordFlowAndLoadUserProfile(username, password);
+ await this.oauthService.refreshToken();
+ const accessToken = this.oauthService.getAccessToken();
+ this.onLogin.next(accessToken);
+
+ return {
+ type: this.appConfig.get(AppConfigValues.PROVIDERS) as string,
+ ticket: accessToken
+ };
+ } catch (err) {
+ throw this.handleError(err);
+ }
+ });
+ }
+
+ getUsername(){
+ return this.jwtHelperService.getValueFromLocalToken(JwtHelperService.USER_PREFERRED_USERNAME);
+ }
+
+ /**
+ * @deprecated
+ * @returns the logged username
+ */
+ getEcmUsername(): string {
+ return this.getUsername();
+ }
+
+ /**
+ * @deprecated
+ * @returns the logged username
+ */
+ getBpmUsername(): string {
+ return this.getUsername();
+ }
+
+ ssoImplicitLogin() {
+ this.auth.login();
+ }
+
+ 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);
+ }
+
+ isPublicUrl(): boolean {
+ const oauth2 = this.appConfig.get(AppConfigValues.OAUTHCONFIG, null);
+
+ if (Array.isArray(oauth2.publicUrls)) {
+ return oauth2.publicUrls.length > 0 &&
+ oauth2.publicUrls.some((urlPattern: string) => {
+ const minimatch = new Minimatch(urlPattern);
+ return minimatch.match(window.location.href);
+ });
+ }
+ return false;
+ }
+
+ getAuthHeaders(_requestUrl: string, header: HttpHeaders): HttpHeaders {
+ return this.addBearerToken(header);
+ }
+
+ private addBearerToken(header: HttpHeaders): HttpHeaders {
+ const token: string = this.getToken();
+
+ if (!token) {
+ return header;
+ }
+
+ return header.set('Authorization', 'bearer ' + token);
+ }
+
+}
diff --git a/lib/core/src/lib/card-view/components/card-view-keyvaluepairsitem/card-view-keyvaluepairsitem.component.spec.ts b/lib/core/src/lib/card-view/components/card-view-keyvaluepairsitem/card-view-keyvaluepairsitem.component.spec.ts
index 433bff1d6f..639a236ecf 100644
--- a/lib/core/src/lib/card-view/components/card-view-keyvaluepairsitem/card-view-keyvaluepairsitem.component.spec.ts
+++ b/lib/core/src/lib/card-view/components/card-view-keyvaluepairsitem/card-view-keyvaluepairsitem.component.spec.ts
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
+import { ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { CardViewKeyValuePairsItemModel } from '../../models/card-view-keyvaluepairs.model';
import { CardViewKeyValuePairsItemComponent } from './card-view-keyvaluepairsitem.component';
@@ -122,57 +122,5 @@ describe('CardViewKeyValuePairsItemComponent', () => {
expect(cardViewUpdateService.update).toHaveBeenCalled();
expect(component.property.value.length).toBe(0);
});
-
- it('should update property on input blur', waitForAsync(() => {
- spyOn(cardViewUpdateService, 'update');
- component.ngOnChanges();
- fixture.detectChanges();
-
- const addButton = fixture.debugElement.query(By.css('.adf-card-view__key-value-pairs__add-btn'));
- addButton.triggerEventHandler('click', null);
- fixture.detectChanges();
-
- const nameInput = fixture.debugElement.query(By.css(`[data-automation-id="card-${component.property.key}-name-input-0"]`));
- const valueInput = fixture.debugElement.query(By.css(`[data-automation-id="card-${component.property.key}-value-input-0"]`));
-
- nameInput.nativeElement.value = mockData[0].name;
- nameInput.nativeElement.dispatchEvent(new Event('input'));
- valueInput.nativeElement.value = mockData[0].value;
- valueInput.nativeElement.dispatchEvent(new Event('input'));
-
- fixture.whenStable().then(() => {
- fixture.detectChanges();
-
- valueInput.triggerEventHandler('blur', null);
- fixture.detectChanges();
-
- expect(cardViewUpdateService.update).toHaveBeenCalled();
- expect(JSON.stringify(component.property.value)).toBe(JSON.stringify(mockData));
- });
- }));
-
- it('should not update property if at least one input is empty on blur', waitForAsync(() => {
- spyOn(cardViewUpdateService, 'update');
- component.ngOnChanges();
- fixture.detectChanges();
-
- const addButton = fixture.debugElement.query(By.css('.adf-card-view__key-value-pairs__add-btn'));
- addButton.triggerEventHandler('click', null);
- fixture.detectChanges();
-
- const valueInput = fixture.debugElement.query(By.css(`[data-automation-id="card-${component.property.key}-value-input-0"]`));
-
- valueInput.nativeElement.value = mockData[0].value;
- valueInput.nativeElement.dispatchEvent(new Event('input'));
-
- fixture.whenStable().then(() => {
- fixture.detectChanges();
-
- valueInput.triggerEventHandler('blur', null);
- fixture.detectChanges();
-
- expect(cardViewUpdateService.update).not.toHaveBeenCalled();
- });
- }));
});
});
diff --git a/lib/core/src/lib/common/mock/app-config.service.mock.ts b/lib/core/src/lib/common/mock/app-config.service.mock.ts
index e9a63d2bac..6dbb6c22de 100644
--- a/lib/core/src/lib/common/mock/app-config.service.mock.ts
+++ b/lib/core/src/lib/common/mock/app-config.service.mock.ts
@@ -19,12 +19,14 @@ import { Injectable } from '@angular/core';
import { AppConfigService, Status } from '../../app-config/app-config.service';
import { HttpClient } from '@angular/common/http';
import { ExtensionService } from '@alfresco/adf-extensions';
+
@Injectable()
export class AppConfigServiceMock extends AppConfigService {
config: any = {
application: {
- name: 'Alfresco ADF Application'
+ name: 'Alfresco ADF Application',
+ storagePrefix: 'ADF_APP'
},
ecmHost: 'http://{hostname}{:port}/ecm',
bpmHost: 'http://{hostname}{:port}/bpm',
@@ -35,11 +37,12 @@ export class AppConfigServiceMock extends AppConfigService {
super(http, extensionService);
}
- load(): Promise {
+ load(callback?: (...args: any[]) => any): Promise {
return new Promise((resolve) => {
this.status = Status.LOADED;
- this.onDataLoaded(this.config);
+ callback?.();
resolve(this.config);
+ this.onDataLoaded();
});
}
}
diff --git a/lib/core/src/lib/common/services/storage.service.spec.ts b/lib/core/src/lib/common/services/storage.service.spec.ts
index a20f29e6e0..755c8f1d56 100644
--- a/lib/core/src/lib/common/services/storage.service.spec.ts
+++ b/lib/core/src/lib/common/services/storage.service.spec.ts
@@ -21,7 +21,6 @@ import { StorageService } from '../../common/services/storage.service';
import { CoreTestingModule } from '../../testing/core.testing.module';
import { AppConfigServiceMock } from '../mock/app-config.service.mock';
import { TranslateModule } from '@ngx-translate/core';
-import { CoreModule } from '../../core.module';
describe('StorageService', () => {
@@ -34,8 +33,6 @@ describe('StorageService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
- TranslateModule.forRoot(),
- CoreModule.forRoot(),
CoreTestingModule
]
});
@@ -87,6 +84,7 @@ describe('StorageService', () => {
]
});
appConfig = TestBed.inject(AppConfigService);
+
appConfig.config = {
application: {
storagePrefix: ''
diff --git a/lib/core/src/lib/common/services/storage.service.ts b/lib/core/src/lib/common/services/storage.service.ts
index 71c45dd12e..e4daedd230 100644
--- a/lib/core/src/lib/common/services/storage.service.ts
+++ b/lib/core/src/lib/common/services/storage.service.ts
@@ -81,7 +81,7 @@ export class StorageService {
*/
removeItem(key: string) {
if (this.useLocalStorage) {
- localStorage.removeItem(this.prefix + key);
+ localStorage.removeItem(`${this.prefix}` + key);
} else {
delete this.memoryStore[this.prefix + key];
}
diff --git a/lib/core/src/lib/common/utils/object-utils.ts b/lib/core/src/lib/common/utils/object-utils.ts
index f8808473a9..cd63ca3b69 100644
--- a/lib/core/src/lib/common/utils/object-utils.ts
+++ b/lib/core/src/lib/common/utils/object-utils.ts
@@ -26,7 +26,7 @@ export class ObjectUtils {
*/
static getValue(target: any, key: string): any {
- if (!target) {
+ if (!target || !key) {
return undefined;
}
diff --git a/lib/core/src/lib/core.module.ts b/lib/core/src/lib/core.module.ts
index 61d77b0acb..60fc0a87ce 100644
--- a/lib/core/src/lib/core.module.ts
+++ b/lib/core/src/lib/core.module.ts
@@ -53,9 +53,8 @@ import { ExtensionsModule } from '@alfresco/adf-extensions';
import { directionalityConfigFactory } from './common/services/directionality-config-factory';
import { DirectionalityConfigService } from './common/services/directionality-config.service';
import { SearchTextModule } from './search-text/search-text-input.module';
-import { AdfHttpClient, AlfrescoJsClientsModule } from '@alfresco/adf-core/api';
+import { AdfHttpClient } from '@alfresco/adf-core/api';
import { AuthenticationInterceptor, Authentication } from '@alfresco/adf-core/auth';
-import { LegacyApiClientModule } from './api-factories/legacy-api-client.module';
import { HttpClientModule, HttpClientXsrfModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { AuthenticationService } from './auth/services/authentication.service';
import { MAT_SNACK_BAR_DEFAULT_OPTIONS } from '@angular/material/snack-bar';
@@ -101,8 +100,6 @@ import { AdfDateTimeFnsAdapter } from './common/utils/datetime-fns-adapter';
NotificationHistoryModule,
SearchTextModule,
BlankPageModule,
- LegacyApiClientModule,
- AlfrescoJsClientsModule,
HttpClientModule,
HttpClientXsrfModule.withOptions({
cookieName: 'CSRF-TOKEN',
diff --git a/lib/core/src/lib/directives/logout.directive.ts b/lib/core/src/lib/directives/logout.directive.ts
index b1e54587f4..e4d446abaf 100644
--- a/lib/core/src/lib/directives/logout.directive.ts
+++ b/lib/core/src/lib/directives/logout.directive.ts
@@ -37,7 +37,7 @@ export class LogoutDirective implements OnInit {
private renderer: Renderer2,
private router: Router,
private appConfig: AppConfigService,
- private auth: AuthenticationService) {
+ private authenticationService: AuthenticationService) {
}
ngOnInit() {
@@ -58,14 +58,14 @@ export class LogoutDirective implements OnInit {
}
logout() {
- this.auth.logout().subscribe(
+ this.authenticationService.logout().subscribe(
() => this.redirectToUri(),
() => this.redirectToUri()
);
}
redirectToUri() {
- if (this.enableRedirect && !this.auth.isOauth()) {
+ if (this.enableRedirect && !this.authenticationService.isOauth()) {
const redirectRoute = this.getRedirectUri();
this.router.navigate([redirectRoute]);
}
diff --git a/lib/core/src/lib/login/components/login-dialog-panel.component.spec.ts b/lib/core/src/lib/login/components/login-dialog-panel.component.spec.ts
index 8a59359409..8d6d12a338 100644
--- a/lib/core/src/lib/login/components/login-dialog-panel.component.spec.ts
+++ b/lib/core/src/lib/login/components/login-dialog-panel.component.spec.ts
@@ -16,11 +16,12 @@
*/
import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { AuthenticationService } from '../../auth/services/authentication.service';
import { LoginDialogPanelComponent } from './login-dialog-panel.component';
import { of } from 'rxjs';
import { CoreTestingModule } from '../../testing/core.testing.module';
import { TranslateModule } from '@ngx-translate/core';
+import { BasicAlfrescoAuthService } from '../../auth/basic-auth/basic-alfresco-auth.service';
+import { OidcAuthenticationService } from '../../auth/services/oidc-authentication.service';
describe('LoginDialogPanelComponent', () => {
let component: LoginDialogPanelComponent;
@@ -28,19 +29,23 @@ describe('LoginDialogPanelComponent', () => {
let element: HTMLElement;
let usernameInput: HTMLInputElement;
let passwordInput: HTMLInputElement;
- let authService: AuthenticationService;
+ let basicAlfrescoAuthService: BasicAlfrescoAuthService;
beforeEach(async () => {
TestBed.configureTestingModule({
imports: [
TranslateModule.forRoot(),
CoreTestingModule
+ ],
+ providers: [
+ { provide: OidcAuthenticationService, useValue: {}}
]
});
fixture = TestBed.createComponent(LoginDialogPanelComponent);
+ basicAlfrescoAuthService = TestBed.inject(BasicAlfrescoAuthService);
+
element = fixture.nativeElement;
component = fixture.componentInstance;
- authService = TestBed.inject(AuthenticationService);
fixture.detectChanges();
await fixture.whenStable();
@@ -76,7 +81,7 @@ describe('LoginDialogPanelComponent', () => {
expect(event.token.ticket).toBe('ticket');
done();
});
- spyOn(authService, 'login').and.returnValue(of({ type: 'type', ticket: 'ticket' }));
+ spyOn(basicAlfrescoAuthService, 'login').and.returnValue(of({ type: 'type', ticket: 'ticket' }));
loginWithCredentials('fake-username', 'fake-password');
});
diff --git a/lib/core/src/lib/login/components/login.component.spec.ts b/lib/core/src/lib/login/components/login.component.spec.ts
index 58bdc10759..40cdcd4bd1 100644
--- a/lib/core/src/lib/login/components/login.component.spec.ts
+++ b/lib/core/src/lib/login/components/login.component.spec.ts
@@ -25,10 +25,11 @@ import { AuthenticationService } from '../../auth/services/authentication.servic
import { LoginErrorEvent } from '../models/login-error.event';
import { LoginSuccessEvent } from '../models/login-success.event';
import { LoginComponent } from './login.component';
-import { of, throwError } from 'rxjs';
-import { AlfrescoApiService } from '../../services/alfresco-api.service';
+import { EMPTY, of, throwError } from 'rxjs';
import { CoreTestingModule } from '../../testing/core.testing.module';
import { LogService } from '../../common/services/log.service';
+import { BasicAlfrescoAuthService } from '../../auth/basic-auth/basic-alfresco-auth.service';
+import { OidcAuthenticationService } from '../../auth/services/oidc-authentication.service';
describe('LoginComponent', () => {
let component: LoginComponent;
@@ -38,7 +39,7 @@ describe('LoginComponent', () => {
let router: Router;
let userPreferences: UserPreferencesService;
let appConfigService: AppConfigService;
- let alfrescoApiService: AlfrescoApiService;
+ let basicAlfrescoAuthService: BasicAlfrescoAuthService;
let usernameInput;
let passwordInput;
@@ -60,6 +61,16 @@ describe('LoginComponent', () => {
TestBed.configureTestingModule({
imports: [
CoreTestingModule
+ ],
+ providers: [
+ {
+ provide: OidcAuthenticationService, useValue: {
+ ssoImplicitLogin: () => { },
+ isPublicUrl: () => false,
+ hasValidIdToken: () => false,
+ isLoggedIn: () => false
+ }
+ }
]
});
fixture = TestBed.createComponent(LoginComponent);
@@ -69,11 +80,11 @@ describe('LoginComponent', () => {
component.showRememberMe = true;
component.showLoginActions = true;
+ basicAlfrescoAuthService = TestBed.inject(BasicAlfrescoAuthService);
authService = TestBed.inject(AuthenticationService);
router = TestBed.inject(Router);
userPreferences = TestBed.inject(UserPreferencesService);
appConfigService = TestBed.inject(AppConfigService);
- alfrescoApiService = TestBed.inject(AlfrescoApiService);
const logService = TestBed.inject(LogService);
spyOn(logService, 'error');
@@ -111,7 +122,7 @@ describe('LoginComponent', () => {
});
it('should redirect to route on successful login', () => {
- spyOn(authService, 'login').and.returnValue(
+ spyOn(basicAlfrescoAuthService, 'login').and.returnValue(
of({ type: 'type', ticket: 'ticket' })
);
const redirect = '/home';
@@ -161,10 +172,10 @@ describe('LoginComponent', () => {
appConfigService.config = {};
appConfigService.config.providers = 'ECM';
- spyOn(authService, 'login').and.returnValue(of({ type: 'type', ticket: 'ticket' }));
+ spyOn(basicAlfrescoAuthService, 'login').and.returnValue(of({ type: 'type', ticket: 'ticket' }));
const redirect = '/home';
component.successRoute = redirect;
- authService.setRedirect({ provider: 'ECM', url: 'some-route' });
+ basicAlfrescoAuthService.setRedirect({ provider: 'ECM', url: 'some-route' });
spyOn(router, 'navigateByUrl');
@@ -174,8 +185,7 @@ describe('LoginComponent', () => {
it('should update user preferences upon login', async () => {
spyOn(userPreferences, 'setStoragePrefix').and.callThrough();
- spyOn(authService, 'login').and.returnValue(of({ type: 'type', ticket: 'ticket' }));
- spyOn(alfrescoApiService.getInstance(), 'login').and.returnValue(Promise.resolve());
+ spyOn(basicAlfrescoAuthService, 'login').and.returnValue(of({ type: 'type', ticket: 'ticket' }));
component.success.subscribe(() => {
expect(userPreferences.setStoragePrefix).toHaveBeenCalledWith('fake-username');
@@ -206,14 +216,14 @@ describe('LoginComponent', () => {
});
it('should be changed back to the default after a failed login attempt', () => {
- spyOn(authService, 'login').and.returnValue(throwError('Fake server error'));
+ spyOn(basicAlfrescoAuthService, 'login').and.returnValue(throwError('Fake server error'));
loginWithCredentials('fake-wrong-username', 'fake-wrong-password');
expect(getLoginButtonText()).toEqual('LOGIN.BUTTON.LOGIN');
});
it('should be changed to the "welcome key" after a successful login attempt', () => {
- spyOn(authService, 'login').and.returnValue(of({ type: 'type', ticket: 'ticket' }));
+ spyOn(basicAlfrescoAuthService, 'login').and.returnValue(of({ type: 'type', ticket: 'ticket' }));
loginWithCredentials('fake-username', 'fake-password');
expect(getLoginButtonText()).toEqual('LOGIN.BUTTON.WELCOME');
@@ -295,12 +305,12 @@ describe('LoginComponent', () => {
});
it('should be taken into consideration during login attempt', fakeAsync(() => {
- spyOn(authService, 'login').and.returnValue(of({ type: 'type', ticket: 'ticket' }));
+ spyOn(basicAlfrescoAuthService, 'login').and.returnValue(of({ type: 'type', ticket: 'ticket' }));
component.rememberMe = false;
loginWithCredentials('fake-username', 'fake-password');
- expect(authService.login).toHaveBeenCalledWith('fake-username', 'fake-password', false);
+ expect(basicAlfrescoAuthService.login).toHaveBeenCalledWith('fake-username', 'fake-password', false);
}));
});
@@ -469,7 +479,7 @@ describe('LoginComponent', () => {
});
it('should return error with a wrong username', (done) => {
- spyOn(alfrescoApiService.getInstance(), 'login').and.returnValue(Promise.reject(new Error('login error')));
+ spyOn(basicAlfrescoAuthService, 'login').and.returnValue(throwError(new Error()));
component.error.subscribe(() => {
fixture.detectChanges();
@@ -484,7 +494,7 @@ describe('LoginComponent', () => {
});
it('should return error with a wrong password', (done) => {
- spyOn(alfrescoApiService.getInstance(), 'login').and.returnValue(Promise.reject(new Error('login error')));
+ spyOn(basicAlfrescoAuthService, 'login').and.returnValue(throwError(new Error()));
component.error.subscribe(() => {
fixture.detectChanges();
@@ -500,7 +510,7 @@ describe('LoginComponent', () => {
});
it('should return error with a wrong username and password', (done) => {
- spyOn(alfrescoApiService.getInstance(), 'login').and.returnValue(Promise.reject(new Error('login error')));
+ spyOn(basicAlfrescoAuthService, 'login').and.returnValue(throwError(new Error()));
component.error.subscribe(() => {
fixture.detectChanges();
@@ -516,7 +526,7 @@ describe('LoginComponent', () => {
});
it('should return CORS error when server CORS error occurs', (done) => {
- spyOn(authService, 'login').and.returnValue(throwError({
+ spyOn(basicAlfrescoAuthService, 'login').and.returnValue(throwError({
error: {
crossDomain: true,
message: 'ERROR: the network is offline, Origin is not allowed by Access-Control-Allow-Origin'
@@ -537,7 +547,7 @@ describe('LoginComponent', () => {
});
it('should return CSRF error when server CSRF error occurs', fakeAsync(() => {
- spyOn(authService, 'login')
+ spyOn(basicAlfrescoAuthService, 'login')
.and.returnValue(throwError({ message: 'ERROR: Invalid CSRF-token', status: 403 }));
component.error.subscribe(() => {
@@ -552,7 +562,7 @@ describe('LoginComponent', () => {
}));
it('should return ECM read-only error when error occurs', fakeAsync(() => {
- spyOn(authService, 'login')
+ spyOn(basicAlfrescoAuthService, 'login')
.and.returnValue(
throwError(
{
@@ -600,7 +610,7 @@ describe('LoginComponent', () => {
});
it('should return success event after the login have succeeded', (done) => {
- spyOn(authService, 'login').and.returnValue(of({ type: 'type', ticket: 'ticket' }));
+ spyOn(basicAlfrescoAuthService, 'login').and.returnValue(of({ type: 'type', ticket: 'ticket' }));
expect(component.isError).toBe(false);
@@ -616,7 +626,7 @@ describe('LoginComponent', () => {
});
it('should emit success event after the login has succeeded and discard password', fakeAsync(() => {
- spyOn(authService, 'login').and.returnValue(of({ type: 'type', ticket: 'ticket' }));
+ spyOn(basicAlfrescoAuthService, 'login').and.returnValue(of({ type: 'type', ticket: 'ticket' }));
component.success.subscribe((event) => {
fixture.detectChanges();
@@ -631,7 +641,7 @@ describe('LoginComponent', () => {
}));
it('should emit error event after the login has failed', fakeAsync(() => {
- spyOn(authService, 'login').and.returnValue(throwError('Fake server error'));
+ spyOn(basicAlfrescoAuthService, 'login').and.returnValue(throwError('Fake server error'));
component.error.subscribe((error) => {
fixture.detectChanges();
@@ -668,7 +678,7 @@ describe('LoginComponent', () => {
});
it('should emit only the username and not the password as part of the executeSubmit', fakeAsync(() => {
- spyOn(alfrescoApiService.getInstance(), 'login').and.returnValue(Promise.resolve());
+ spyOn(basicAlfrescoAuthService, 'login').and.returnValue(EMPTY);
component.executeSubmit.subscribe((res) => {
fixture.detectChanges();
@@ -688,7 +698,6 @@ describe('LoginComponent', () => {
beforeEach(() => {
appConfigService.config.oauth2 = { implicitFlow: true, silentLogin: false };
appConfigService.load();
- alfrescoApiService.reset();
});
it('should not show login username and password if SSO implicit flow is active', fakeAsync(() => {
diff --git a/lib/core/src/lib/login/components/login.component.ts b/lib/core/src/lib/login/components/login.component.ts
index 5b3762e1c6..222d84bc86 100644
--- a/lib/core/src/lib/login/components/login.component.ts
+++ b/lib/core/src/lib/login/components/login.component.ts
@@ -29,6 +29,8 @@ import { AppConfigService, AppConfigValues } from '../../app-config/app-config.s
import { DomSanitizer, SafeStyle } from '@angular/platform-browser';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
+import { BasicAlfrescoAuthService } from '../../auth/basic-auth/basic-alfresco-auth.service';
+import { OidcAuthenticationService } from '../../auth/services/oidc-authentication.service';
// eslint-disable-next-line no-shadow
enum LoginSteps {
@@ -52,7 +54,7 @@ interface LoginFormValues {
templateUrl: './login.component.html',
styleUrls: ['./login.component.scss'],
encapsulation: ViewEncapsulation.None,
- host: { class: 'adf-login' }
+ host: {class: 'adf-login'}
})
export class LoginComponent implements OnInit, OnDestroy {
isPasswordShow: boolean = false;
@@ -129,6 +131,8 @@ export class LoginComponent implements OnInit, OnDestroy {
constructor(
private _fb: UntypedFormBuilder,
private authService: AuthenticationService,
+ private basicAlfrescoAuthService: BasicAlfrescoAuthService,
+ private oidcAuthenticationService: OidcAuthenticationService,
private translateService: TranslationService,
private router: Router,
private appConfig: AppConfigService,
@@ -160,7 +164,7 @@ export class LoginComponent implements OnInit, OnDestroy {
const url = params['redirectUrl'];
const provider = this.appConfig.get(AppConfigValues.PROVIDERS);
- this.authService.setRedirect({ provider, url });
+ this.basicAlfrescoAuthService.setRedirect({provider, url});
});
}
@@ -181,7 +185,7 @@ export class LoginComponent implements OnInit, OnDestroy {
}
redirectToImplicitLogin() {
- this.authService.ssoImplicitLogin();
+ this.oidcAuthenticationService.ssoImplicitLogin();
}
/**
@@ -193,12 +197,13 @@ export class LoginComponent implements OnInit, OnDestroy {
this.disableError();
const args = new LoginSubmitEvent({
- controls: { username: this.form.controls.username }
+ controls: {username: this.form.controls.username}
});
this.executeSubmit.emit(args);
if (!args.defaultPrevented) {
this.actualLoginStep = LoginSteps.Checking;
+
this.performLogin(values);
}
}
@@ -207,7 +212,7 @@ export class LoginComponent implements OnInit, OnDestroy {
if (this.authService.isLoggedIn()) {
this.router.navigate([this.successRoute]);
}
- this.authService.ssoImplicitLogin();
+ this.oidcAuthenticationService.ssoImplicitLogin();
}
/**
@@ -237,30 +242,31 @@ export class LoginComponent implements OnInit, OnDestroy {
}
}
- performLogin(values: LoginFormValues) {
- this.authService.login(values.username, values.password, this.rememberMe).subscribe(
- (token) => {
- const redirectUrl = this.authService.getRedirect();
+ performLogin(values: { username: string; password: string }) {
+ this.authService.login(values.username, values.password, this.rememberMe)
+ .subscribe(
+ async (token: any) => {
+ const redirectUrl = this.basicAlfrescoAuthService.getRedirect();
- this.actualLoginStep = LoginSteps.Welcome;
- this.userPreferences.setStoragePrefix(values.username);
- values.password = null;
- this.success.emit(new LoginSuccessEvent(token, values.username, null));
+ this.actualLoginStep = LoginSteps.Welcome;
+ this.userPreferences.setStoragePrefix(values.username);
+ values.password = null;
+ this.success.emit(new LoginSuccessEvent(token, values.username, null));
- if (redirectUrl) {
- this.authService.setRedirect(null);
- this.router.navigateByUrl(redirectUrl);
- } else if (this.successRoute) {
- this.router.navigate([this.successRoute]);
+ if (redirectUrl) {
+ this.basicAlfrescoAuthService.setRedirect(null);
+ await this.router.navigateByUrl(redirectUrl);
+ } else if (this.successRoute) {
+ await this.router.navigate([this.successRoute]);
+ }
+ },
+ (err: any) => {
+ this.actualLoginStep = LoginSteps.Landing;
+ this.displayErrorMessage(err);
+ this.isError = true;
+ this.error.emit(new LoginErrorEvent(err));
}
- },
- (err: any) => {
- this.actualLoginStep = LoginSteps.Landing;
- this.displayErrorMessage(err);
- this.isError = true;
- this.error.emit(new LoginErrorEvent(err));
- }
- );
+ );
}
/**
diff --git a/lib/core/src/lib/login/directives/login-footer.directive.spec.ts b/lib/core/src/lib/login/directives/login-footer.directive.spec.ts
index bce4351a80..bde2728e77 100644
--- a/lib/core/src/lib/login/directives/login-footer.directive.spec.ts
+++ b/lib/core/src/lib/login/directives/login-footer.directive.spec.ts
@@ -20,6 +20,7 @@ import { LoginComponent } from '../components/login.component';
import { LoginFooterDirective } from './login-footer.directive';
import { CoreTestingModule } from '../../testing/core.testing.module';
import { TranslateModule } from '@ngx-translate/core';
+import { OidcAuthenticationService } from '../../auth/services/oidc-authentication.service';
describe('LoginFooterDirective', () => {
let fixture: ComponentFixture;
@@ -31,6 +32,11 @@ describe('LoginFooterDirective', () => {
imports: [
TranslateModule.forRoot(),
CoreTestingModule
+ ],
+ providers: [
+ {
+ provide: OidcAuthenticationService, useValue: {}
+ }
]
});
fixture = TestBed.createComponent(LoginComponent);
diff --git a/lib/core/src/lib/login/directives/login-header.directive.spec.ts b/lib/core/src/lib/login/directives/login-header.directive.spec.ts
index 635eca7c57..c255e4c9ed 100644
--- a/lib/core/src/lib/login/directives/login-header.directive.spec.ts
+++ b/lib/core/src/lib/login/directives/login-header.directive.spec.ts
@@ -20,6 +20,7 @@ import { LoginComponent } from '../components/login.component';
import { LoginHeaderDirective } from './login-header.directive';
import { CoreTestingModule } from '../../testing/core.testing.module';
import { TranslateModule } from '@ngx-translate/core';
+import { OidcAuthenticationService } from '../../auth/services/oidc-authentication.service';
describe('LoginHeaderDirective', () => {
let fixture: ComponentFixture;
@@ -31,6 +32,9 @@ describe('LoginHeaderDirective', () => {
imports: [
TranslateModule.forRoot(),
CoreTestingModule
+ ],
+ providers: [
+ { provide: OidcAuthenticationService, useValue: {} }
]
});
fixture = TestBed.createComponent(LoginComponent);
diff --git a/lib/core/src/lib/snackbar-content/snackbar-content.component.spec.ts b/lib/core/src/lib/snackbar-content/snackbar-content.component.spec.ts
index cc8a2703c7..d9bff92a83 100644
--- a/lib/core/src/lib/snackbar-content/snackbar-content.component.spec.ts
+++ b/lib/core/src/lib/snackbar-content/snackbar-content.component.spec.ts
@@ -16,12 +16,12 @@
*/
import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { SnackbarContentComponent } from '@alfresco/adf-core';
import { MatIcon, MatIconModule } from '@angular/material/icon';
import { MAT_SNACK_BAR_DATA, MatSnackBarModule, MatSnackBarRef } from '@angular/material/snack-bar';
import { MatButtonModule } from '@angular/material/button';
import { By } from '@angular/platform-browser';
import { TranslateModule } from '@ngx-translate/core';
+import { SnackbarContentComponent } from './snackbar-content.component';
describe('SnackbarContentComponent', () => {
let component: SnackbarContentComponent;
diff --git a/lib/core/src/lib/testing/automation.service.ts b/lib/core/src/lib/testing/automation.service.ts
index dc8cf0dd0f..33acfba051 100644
--- a/lib/core/src/lib/testing/automation.service.ts
+++ b/lib/core/src/lib/testing/automation.service.ts
@@ -36,7 +36,8 @@ export class CoreAutomationService {
private userPreferencesService: UserPreferencesService,
private storageService: StorageService,
private auth: AuthenticationService
- ) {}
+ ) {
+ }
setup() {
const adfProxy = window['adf'] || {};
diff --git a/lib/core/src/lib/testing/core.story.module.ts b/lib/core/src/lib/testing/core.story.module.ts
index e368b8f409..516804b03f 100644
--- a/lib/core/src/lib/testing/core.story.module.ts
+++ b/lib/core/src/lib/testing/core.story.module.ts
@@ -20,9 +20,11 @@ import { CoreModule } from '../core.module';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { TranslateModule } from '@ngx-translate/core';
import { provideTranslations } from '../translation/translation.service';
+import { AuthModule } from '../../../src/lib/auth/oidc/auth.module';
@NgModule({
imports: [
+ AuthModule.forRoot(),
TranslateModule.forRoot(),
CoreModule.forRoot(),
BrowserAnimationsModule
diff --git a/lib/core/src/lib/testing/core.testing.module.ts b/lib/core/src/lib/testing/core.testing.module.ts
index a90762bedb..6226ae618a 100644
--- a/lib/core/src/lib/testing/core.testing.module.ts
+++ b/lib/core/src/lib/testing/core.testing.module.ts
@@ -32,9 +32,11 @@ import { CookieServiceMock } from '../mock/cookie.service.mock';
import { HttpClientModule } from '@angular/common/http';
import { directionalityConfigFactory } from '../common/services/directionality-config-factory';
import { DirectionalityConfigService } from '../common/services/directionality-config.service';
+import { AuthModule } from '../auth';
@NgModule({
imports: [
+ AuthModule.forRoot({ useHash: true }),
NoopAnimationsModule,
RouterTestingModule,
HttpClientModule,
diff --git a/lib/core/src/lib/translation/translate-loader.spec.ts b/lib/core/src/lib/translation/translate-loader.spec.ts
index a02da25253..8325d45692 100644
--- a/lib/core/src/lib/translation/translate-loader.spec.ts
+++ b/lib/core/src/lib/translation/translate-loader.spec.ts
@@ -20,6 +20,7 @@ import { TranslateLoaderService } from './translate-loader.service';
import { TranslationService } from './translation.service';
import { TranslateModule } from '@ngx-translate/core';
import { CoreModule } from '../core.module';
+import { AuthModule } from '../auth';
declare let jasmine: any;
@@ -30,6 +31,7 @@ describe('TranslateLoader', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
+ AuthModule.forRoot({ useHash: true }),
TranslateModule.forRoot(),
CoreModule.forRoot()
],
diff --git a/lib/core/src/lib/viewer/components/download-prompt-dialog/download-prompt-dialog.component.spec.ts b/lib/core/src/lib/viewer/components/download-prompt-dialog/download-prompt-dialog.component.spec.ts
index 3500c481a3..ab1e645ccc 100644
--- a/lib/core/src/lib/viewer/components/download-prompt-dialog/download-prompt-dialog.component.spec.ts
+++ b/lib/core/src/lib/viewer/components/download-prompt-dialog/download-prompt-dialog.component.spec.ts
@@ -17,10 +17,12 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { CoreTestingModule, DownloadPromptDialogComponent, DownloadPromptActions } from '@alfresco/adf-core';
import { By } from '@angular/platform-browser';
import { MatDialogRef } from '@angular/material/dialog';
import { TranslateModule } from '@ngx-translate/core';
+import { DownloadPromptDialogComponent } from './download-prompt-dialog.component';
+import { CoreTestingModule } from '../../../testing/core.testing.module';
+import { DownloadPromptActions } from '../../models/download-prompt.actions';
const mockDialog = {
close: jasmine.createSpy('close')
diff --git a/lib/insights/src/lib/testing/insights.testing.module.ts b/lib/insights/src/lib/testing/insights.testing.module.ts
index 573e5d6ed2..37cd940f62 100644
--- a/lib/insights/src/lib/testing/insights.testing.module.ts
+++ b/lib/insights/src/lib/testing/insights.testing.module.ts
@@ -26,11 +26,12 @@ import {
AppConfigServiceMock,
TranslationService,
TranslationMock,
- CoreModule
+ CoreModule, AuthModule
} from '@alfresco/adf-core';
@NgModule({
imports: [
+ AuthModule.forRoot({ useHash: true }),
NoopAnimationsModule,
TranslateModule,
CoreModule.forRoot(),
diff --git a/lib/process-services-cloud/src/lib/form/components/form-cloud.component.spec.ts b/lib/process-services-cloud/src/lib/form/components/form-cloud.component.spec.ts
index 50a2487ad0..4a970df4e3 100644
--- a/lib/process-services-cloud/src/lib/form/components/form-cloud.component.spec.ts
+++ b/lib/process-services-cloud/src/lib/form/components/form-cloud.component.spec.ts
@@ -25,7 +25,7 @@ import {
FormModel,
FormOutcomeEvent,
FormOutcomeModel, FormRenderingService, FormService,
- UploadWidgetContentLinkModel, WidgetVisibilityService, provideTranslations
+ UploadWidgetContentLinkModel, WidgetVisibilityService, provideTranslations, AuthModule
} from '@alfresco/adf-core';
import { Node } from '@alfresco/js-api';
import { ESCAPE } from '@angular/cdk/keycodes';
@@ -1151,6 +1151,7 @@ describe('Multilingual Form', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
+ AuthModule.forRoot({ useHash: true }),
NoopAnimationsModule,
TranslateModule.forRoot(),
CoreModule.forRoot(),
@@ -1226,6 +1227,7 @@ describe('retrieve metadata on submit', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
+ AuthModule.forRoot({ useHash: true }),
NoopAnimationsModule,
TranslateModule.forRoot(),
CoreModule.forRoot(),
diff --git a/lib/process-services-cloud/src/lib/people/services/identity-user.service.spec.ts b/lib/process-services-cloud/src/lib/people/services/identity-user.service.spec.ts
index 30e449eef3..fa9cdecad2 100644
--- a/lib/process-services-cloud/src/lib/people/services/identity-user.service.spec.ts
+++ b/lib/process-services-cloud/src/lib/people/services/identity-user.service.spec.ts
@@ -17,9 +17,7 @@
import { TestBed } from '@angular/core/testing';
import { TranslateModule } from '@ngx-translate/core';
-import { JwtHelperService } from '@alfresco/adf-core';
import { IdentityUserService } from './identity-user.service';
-import { mockToken } from '../mock/jwt-helper.service.spec';
import { ProcessServiceCloudTestingModule } from '../../testing/process-service-cloud.testing.module';
import {
mockSearchUserByApp,
@@ -52,37 +50,6 @@ describe('IdentityUserService', () => {
requestSpy = spyOn(adfHttpClient, 'request');
});
- describe('Current user info (JWT token)', () => {
-
- beforeEach(() => {
- const store = {};
-
- spyOn(localStorage, 'getItem').and.callFake((key: string): string => store[key] || null);
- spyOn(localStorage, 'setItem').and.callFake((key: string, value: string): string => store[key] = value);
- });
-
- it('should fetch identity user info from Jwt id token', () => {
- localStorage.setItem(JwtHelperService.USER_ID_TOKEN, mockToken);
- const user = service.getCurrentUserInfo();
- expect(user).toBeDefined();
- expect(user.firstName).toEqual('John');
- expect(user.lastName).toEqual('Doe');
- expect(user.email).toEqual('johnDoe@gmail.com');
- expect(user.username).toEqual('johnDoe1');
- });
-
- it('should fallback on Jwt access token for identity user info', () => {
- localStorage.setItem(JwtHelperService.USER_ACCESS_TOKEN, mockToken);
- const user = service.getCurrentUserInfo();
- expect(user).toBeDefined();
- expect(user.firstName).toEqual('John');
- expect(user.lastName).toEqual('Doe');
- expect(user.email).toEqual('johnDoe@gmail.com');
- expect(user.username).toEqual('johnDoe1');
- });
-
- });
-
describe('Search', () => {
it('should fetch users', (done) => {
diff --git a/lib/process-services-cloud/src/lib/task/task-filters/services/task-filter-cloud.service.spec.ts b/lib/process-services-cloud/src/lib/task/task-filters/services/task-filter-cloud.service.spec.ts
index 4ad3e3819d..8633dde78f 100644
--- a/lib/process-services-cloud/src/lib/task/task-filters/services/task-filter-cloud.service.spec.ts
+++ b/lib/process-services-cloud/src/lib/task/task-filters/services/task-filter-cloud.service.spec.ts
@@ -35,6 +35,7 @@ import { NotificationCloudService } from '../../../services/notification-cloud.s
import { ProcessServiceCloudTestingModule } from '../../../testing/process-service-cloud.testing.module';
import { IdentityUserService } from '../../../people/services/identity-user.service';
import { ApolloModule } from 'apollo-angular';
+import { StorageService } from '@alfresco/adf-core';
describe('TaskFilterCloudService', () => {
let service: TaskFilterCloudService;
@@ -242,12 +243,13 @@ describe('Inject [LocalPreferenceCloudService] into the TaskFilterCloudService',
let preferenceCloudService: PreferenceCloudServiceInterface;
let identityUserService: IdentityUserService;
let getPreferencesSpy: jasmine.Spy;
+ let storageService: StorageService;
const identityUserMock = { username: 'fakeusername', firstName: 'fake-identity-first-name', lastName: 'fake-identity-last-name', email: 'fakeIdentity@email.com' };
beforeEach(() => {
TestBed.configureTestingModule({
- imports: [HttpClientTestingModule, ApolloModule],
+ imports: [HttpClientTestingModule, ProcessServiceCloudTestingModule, ApolloModule],
providers: [
{ provide: TASK_FILTERS_SERVICE_TOKEN, useClass: LocalPreferenceCloudService }
]
@@ -255,6 +257,7 @@ describe('Inject [LocalPreferenceCloudService] into the TaskFilterCloudService',
service = TestBed.inject(TaskFilterCloudService);
preferenceCloudService = service.preferenceService;
identityUserService = TestBed.inject(IdentityUserService);
+ storageService = TestBed.inject(StorageService);
getPreferencesSpy = spyOn(preferenceCloudService, 'getPreferences').and.returnValue(of([]));
spyOn(identityUserService, 'getCurrentUserInfo').and.returnValue(identityUserMock);
});
@@ -285,7 +288,7 @@ describe('Inject [LocalPreferenceCloudService] into the TaskFilterCloudService',
expect(res[2].status).toEqual('COMPLETED');
expect(getPreferencesSpy).toHaveBeenCalled();
- const localData = JSON.parse(localStorage.getItem(`task-filters-${appName}-${identityUserMock.username}`));
+ const localData = JSON.parse(storageService.getItem(`task-filters-${appName}-${identityUserMock.username}`));
expect(localData.length).toEqual(3);
expect(localData[0].name).toEqual('ADF_CLOUD_TASK_FILTERS.MY_TASKS');
diff --git a/lib/process-services-cloud/src/lib/testing/process-service-cloud.testing.module.ts b/lib/process-services-cloud/src/lib/testing/process-service-cloud.testing.module.ts
index dbc4b35992..ff2ccedeb2 100644
--- a/lib/process-services-cloud/src/lib/testing/process-service-cloud.testing.module.ts
+++ b/lib/process-services-cloud/src/lib/testing/process-service-cloud.testing.module.ts
@@ -25,7 +25,7 @@ import {
AppConfigServiceMock,
TranslationService,
TranslationMock,
- CoreModule
+ CoreModule, AuthModule
} from '@alfresco/adf-core';
import { TranslateModule } from '@ngx-translate/core';
import { ProcessServicesCloudModule } from '../process-services-cloud.module';
@@ -33,6 +33,7 @@ import { RouterTestingModule } from '@angular/router/testing';
@NgModule({
imports: [
+ AuthModule.forRoot({ useHash: true }),
HttpClientModule,
NoopAnimationsModule,
RouterTestingModule,
diff --git a/lib/process-services-cloud/src/lib/testing/process-services-cloud-story.module.ts b/lib/process-services-cloud/src/lib/testing/process-services-cloud-story.module.ts
index b95dc72f36..92f7f07648 100644
--- a/lib/process-services-cloud/src/lib/testing/process-services-cloud-story.module.ts
+++ b/lib/process-services-cloud/src/lib/testing/process-services-cloud-story.module.ts
@@ -16,13 +16,14 @@
*/
import { NgModule } from '@angular/core';
-import { CoreModule, provideTranslations } from '@alfresco/adf-core';
+import { AuthModule, CoreModule, provideTranslations } from '@alfresco/adf-core';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { TranslateModule } from '@ngx-translate/core';
import { ProcessServicesCloudModule } from '../process-services-cloud.module';
@NgModule({
imports: [
+ AuthModule.forRoot(),
BrowserAnimationsModule,
TranslateModule.forRoot(),
CoreModule.forRoot(),
diff --git a/lib/process-services/src/lib/form/widgets/content-widget/attach-file-widget-dialog.component.spec.ts b/lib/process-services/src/lib/form/widgets/content-widget/attach-file-widget-dialog.component.spec.ts
index 664148ac19..d7a2270f11 100644
--- a/lib/process-services/src/lib/form/widgets/content-widget/attach-file-widget-dialog.component.spec.ts
+++ b/lib/process-services/src/lib/form/widgets/content-widget/attach-file-widget-dialog.component.spec.ts
@@ -21,12 +21,13 @@ import { ContentModule, ContentNodeSelectorPanelComponent, DocumentListService,
import { EventEmitter, NO_ERRORS_SCHEMA } from '@angular/core';
import { ProcessTestingModule } from '../../../testing/process.testing.module';
import { AttachFileWidgetDialogComponent } from './attach-file-widget-dialog.component';
-import { AuthenticationService, AlfrescoApiService } from '@alfresco/adf-core';
+import { AlfrescoApiService, BasicAlfrescoAuthService } from '@alfresco/adf-core';
import { AttachFileWidgetDialogComponentData } from './attach-file-widget-dialog-component.interface';
import { of, throwError } from 'rxjs';
import { By } from '@angular/platform-browser';
import { Node, SiteEntry, NodeEntry, SitePaging, SitePagingList } from '@alfresco/js-api';
import { TranslateModule } from '@ngx-translate/core';
+import { OidcAuthenticationService } from 'lib/core/src/lib/auth/services/oidc-authentication.service';
describe('AttachFileWidgetDialogComponent', () => {
@@ -40,7 +41,7 @@ describe('AttachFileWidgetDialogComponent', () => {
ecmHost: 'http://fakeUrl.com'
};
let element: HTMLInputElement;
- let authService: AuthenticationService;
+ let basicAlfrescoAuthService: BasicAlfrescoAuthService;
let siteService: SitesService;
let nodeService: NodesApiService;
let documentListService: DocumentListService;
@@ -58,6 +59,7 @@ describe('AttachFileWidgetDialogComponent', () => {
ProcessTestingModule
],
providers: [
+ { provide: OidcAuthenticationService, useValue: {} },
{ provide: MAT_DIALOG_DATA, useValue: data },
{ provide: MatDialogRef, useValue: { close: () => of() } }
],
@@ -66,7 +68,7 @@ describe('AttachFileWidgetDialogComponent', () => {
fixture = TestBed.createComponent(AttachFileWidgetDialogComponent);
widget = fixture.componentInstance;
element = fixture.nativeElement;
- authService = fixture.debugElement.injector.get(AuthenticationService);
+ basicAlfrescoAuthService = fixture.debugElement.injector.get(BasicAlfrescoAuthService);
siteService = fixture.debugElement.injector.get(SitesService);
nodeService = fixture.debugElement.injector.get(NodesApiService);
documentListService = fixture.debugElement.injector.get(DocumentListService);
@@ -106,7 +108,7 @@ describe('AttachFileWidgetDialogComponent', () => {
});
it('should be able to login', (done) => {
- spyOn(authService, 'login').and.returnValue(of({ type: 'type', ticket: 'ticket'}));
+ spyOn(basicAlfrescoAuthService, 'login').and.returnValue(of({ type: 'type', ticket: 'ticket'}));
isLogged = true;
let loginButton: HTMLButtonElement = element.querySelector('button[data-automation-id="attach-file-dialog-actions-login"]');
const usernameInput: HTMLInputElement = element.querySelector('#username');
@@ -173,7 +175,7 @@ describe('AttachFileWidgetDialogComponent', () => {
describe('login only', () => {
beforeEach(() => {
- spyOn(authService, 'login').and.returnValue(of({ type: 'type', ticket: 'ticket'}));
+ spyOn(basicAlfrescoAuthService, 'login').and.returnValue(of({ type: 'type', ticket: 'ticket'}));
spyOn(matDialogRef, 'close').and.callThrough();
fixture.detectChanges();
widget.data.loginOnly = true;
@@ -192,7 +194,8 @@ describe('AttachFileWidgetDialogComponent', () => {
usernameInput.dispatchEvent(new Event('input'));
passwordInput.dispatchEvent(new Event('input'));
loginButton.click();
- authService.onLogin.next('logged In');
+
+ basicAlfrescoAuthService.onLogin.next('logged-in');
fixture.detectChanges();
expect(matDialogRef.close).toHaveBeenCalled();
});
diff --git a/lib/process-services/src/lib/testing/process.testing.module.ts b/lib/process-services/src/lib/testing/process.testing.module.ts
index 4a44138268..cf1425926e 100644
--- a/lib/process-services/src/lib/testing/process.testing.module.ts
+++ b/lib/process-services/src/lib/testing/process.testing.module.ts
@@ -26,7 +26,7 @@ import {
TranslationService,
TranslationMock,
CoreModule,
- FormRenderingService
+ FormRenderingService, AuthModule
} from '@alfresco/adf-core';
import { TranslateModule } from '@ngx-translate/core';
import { ProcessFormRenderingService } from '../form/process-form-rendering.service';
@@ -34,6 +34,7 @@ import { RouterTestingModule } from '@angular/router/testing';
@NgModule({
imports: [
+ AuthModule.forRoot({ useHash: true }),
NoopAnimationsModule,
TranslateModule.forRoot(),
CoreModule.forRoot(),
diff --git a/lib/testing/src/lib/protractor/content-services/actions/upload.actions.ts b/lib/testing/src/lib/protractor/content-services/actions/upload.actions.ts
index 719cdac032..e896db3003 100644
--- a/lib/testing/src/lib/protractor/content-services/actions/upload.actions.ts
+++ b/lib/testing/src/lib/protractor/content-services/actions/upload.actions.ts
@@ -37,7 +37,7 @@ export class UploadActions {
async uploadFile(fileLocation: fs.PathLike, fileName: string, parentFolderId: string): Promise {
const file = fs.createReadStream(fileLocation);
- return this.uploadApi.uploadFile(
+ const uploadPromise = this.uploadApi.uploadFile(
file,
'',
parentFolderId,
@@ -48,6 +48,12 @@ export class UploadActions {
renditions: 'doclib'
}
);
+
+ await uploadPromise.then(() => {
+ Logger.info(`${fileName} uploaded in ${parentFolderId}`);
+ });
+
+ return uploadPromise;
}
async createEmptyFiles(emptyFileNames: string[], parentFolderId: string): Promise {
@@ -74,6 +80,7 @@ export class UploadActions {
async deleteFileOrFolder(nodeId: string) {
const apiCall = async () => {
try {
+ Logger.info(`Deleting ${nodeId}`);
return this.nodesApi.deleteNode(nodeId, { permanent: true });
} catch (error) {
Logger.error('Error delete file or folder');
@@ -91,8 +98,18 @@ export class UploadActions {
if (files && files.length > 0) {
for (const fileName of files) {
const pathFile = path.join(sourcePath, fileName);
- promises.push(this.uploadFile(pathFile, fileName, folder));
+
+ const uploadPromise = this.uploadFile(pathFile, fileName, folder);
+
+ await uploadPromise.then(() => {
+ Logger.info(`File ${fileName} uploaded successfully in ${folder}!`);
+ }).catch(() => {
+ Logger.error(`File ${fileName} error during the upload in ${folder}!`);
+ });
+
+ promises.push(uploadPromise);
}
+
uploadedFiles = await Promise.all(promises);
}
diff --git a/lib/testing/src/lib/protractor/core/actions/identity/identity.service.ts b/lib/testing/src/lib/protractor/core/actions/identity/identity.service.ts
index d07d49a87c..beebe29f2b 100644
--- a/lib/testing/src/lib/protractor/core/actions/identity/identity.service.ts
+++ b/lib/testing/src/lib/protractor/core/actions/identity/identity.service.ts
@@ -82,7 +82,14 @@ export class IdentityService {
const method = 'DELETE';
const queryParams = {};
const postBody = {};
- return this.api.performIdentityOperation(path, method, queryParams, postBody);
+
+ const deletePromise = this.api.performIdentityOperation(path, method, queryParams, postBody);
+
+ await deletePromise.then(() => {
+ Logger.info(`user ${userId} delete`);
+ });
+
+ return deletePromise;
}
async getUserInfoByUsername(username: string): Promise {
diff --git a/lib/testing/src/lib/protractor/core/pages/login.page.ts b/lib/testing/src/lib/protractor/core/pages/login.page.ts
index 598345bb0e..62bd09fc6b 100644
--- a/lib/testing/src/lib/protractor/core/pages/login.page.ts
+++ b/lib/testing/src/lib/protractor/core/pages/login.page.ts
@@ -66,6 +66,8 @@ export class LoginPage {
Logger.log('Login With ' + username);
const authType = await LocalStorageUtil.getConfigField('authType');
+ Logger.log(`AuthType ${authType}`);
+
if (!authType || authType === 'OAUTH') {
await this.loginSSOIdentityService(username, password, options);
} else {
@@ -83,7 +85,10 @@ export class LoginPage {
await BrowserActions.getUrl(loginURL);
if (oauth2 && oauth2.silentLogin === false) {
+ Logger.log(`Login SSO`);
await this.clickOnSSOButton();
+ }else{
+ Logger.log(`Login SSO silent login`);
}
await BrowserVisibility.waitUntilElementIsVisible(this.usernameField);
@@ -98,6 +103,8 @@ export class LoginPage {
}
async loginBasicAuth(username: string, password: string, options: LoginOptions = { waitForUserIcon: true }): Promise {
+ Logger.log(`Login Basic`);
+
await this.goToLoginPage();
await this.enterUsernameBasicAuth(username);
diff --git a/lib/testing/src/lib/protractor/core/utils/file-browser.util.ts b/lib/testing/src/lib/protractor/core/utils/file-browser.util.ts
index f02394ee0a..3617fdc607 100644
--- a/lib/testing/src/lib/protractor/core/utils/file-browser.util.ts
+++ b/lib/testing/src/lib/protractor/core/utils/file-browser.util.ts
@@ -23,7 +23,7 @@ export class FileBrowserUtil {
static async isFileDownloaded(fileName: string): Promise {
const DEFAULT_ROOT_PATH = browser.params.testConfig ? browser.params.testConfig.main.rootPath : __dirname;
- const file = await browser.driver.wait(() => fs.existsSync(path.join(DEFAULT_ROOT_PATH, 'downloads', fileName)), 30000);
+ const file = await browser.driver.wait(() => fs.existsSync(path.join(DEFAULT_ROOT_PATH, 'downloads', fileName)), 30000,`${fileName} not downloaded`);
await expect(file).toBe(true, `${fileName} not downloaded`);
diff --git a/lib/testing/src/lib/protractor/core/utils/local-storage.util.ts b/lib/testing/src/lib/protractor/core/utils/local-storage.util.ts
index 5f92a0e497..e962b6918c 100644
--- a/lib/testing/src/lib/protractor/core/utils/local-storage.util.ts
+++ b/lib/testing/src/lib/protractor/core/utils/local-storage.util.ts
@@ -17,6 +17,9 @@
import { browser } from 'protractor';
+/*
+Open the CoreAutomationService in ADF core to see where we augment the window
+*/
export class LocalStorageUtil {
static async getConfigField(field: string): Promise {
return browser.executeScript('return window.adf ? window.adf.getConfigField(`' + field + '`) : null;');