diff --git a/apps/metadata-editor-e2e/src/e2e/dashboard.cy.ts b/apps/metadata-editor-e2e/src/e2e/dashboard.cy.ts
index 5cee3c2b69..47132f26e7 100644
--- a/apps/metadata-editor-e2e/src/e2e/dashboard.cy.ts
+++ b/apps/metadata-editor-e2e/src/e2e/dashboard.cy.ts
@@ -344,11 +344,25 @@ describe('dashboard (authenticated)', () => {
})
})
-describe('when the user is not logged in', () => {
- beforeEach(() => {
- cy.visit('/catalog/search')
+describe('Logging in and out', () => {
+ describe('when the user is not logged in', () => {
+ beforeEach(() => {
+ cy.visit('/catalog/search')
+ })
+ it('redirects to the login page', () => {
+ cy.url().should('include', '/catalog.signin?redirect=')
+ })
})
- it('redirects to the login page', () => {
- cy.url().should('include', '/catalog.signin?redirect=')
+ describe('Logging out', () => {
+ beforeEach(() => {
+ cy.login('admin', 'admin', false)
+ cy.visit('/catalog/search')
+ })
+ it('logs out the user', () => {
+ cy.get('gn-ui-avatar').should('be.visible')
+ cy.get('md-editor-sidebar').find('gn-ui-button').eq(1).click()
+ cy.url().should('include', '/catalog.signin?redirect=')
+ cy.get('gn-ui-avatar').should('not.exist')
+ })
})
})
diff --git a/apps/metadata-editor/src/app/app.module.ts b/apps/metadata-editor/src/app/app.module.ts
index 87354808bc..c2a3784725 100644
--- a/apps/metadata-editor/src/app/app.module.ts
+++ b/apps/metadata-editor/src/app/app.module.ts
@@ -26,6 +26,7 @@ import { DashboardPageComponent } from './dashboard/dashboard-page.component'
import { EditorRouterService } from './router.service'
import {
LOGIN_URL,
+ LOGOUT_URL,
provideGn4,
provideRepositoryUrl,
} from '@geonetwork-ui/api/repository'
@@ -70,6 +71,10 @@ import { FeatureEditorModule } from '@geonetwork-ui/feature/editor'
provide: LOGIN_URL,
useFactory: () => getGlobalConfig().LOGIN_URL,
},
+ {
+ provide: LOGOUT_URL,
+ useFactory: () => getGlobalConfig().LOGOUT_URL,
+ },
],
bootstrap: [AppComponent],
})
diff --git a/apps/metadata-editor/src/app/dashboard/sidebar/sidebar.component.html b/apps/metadata-editor/src/app/dashboard/sidebar/sidebar.component.html
index 0c81c789b8..fc99769cb1 100644
--- a/apps/metadata-editor/src/app/dashboard/sidebar/sidebar.component.html
+++ b/apps/metadata-editor/src/app/dashboard/sidebar/sidebar.component.html
@@ -31,8 +31,14 @@
-
+
diff --git a/apps/metadata-editor/src/app/dashboard/sidebar/sidebar.component.spec.ts b/apps/metadata-editor/src/app/dashboard/sidebar/sidebar.component.spec.ts
index 50982f3777..8743381d52 100644
--- a/apps/metadata-editor/src/app/dashboard/sidebar/sidebar.component.spec.ts
+++ b/apps/metadata-editor/src/app/dashboard/sidebar/sidebar.component.spec.ts
@@ -1,19 +1,27 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'
-import { AvatarServiceInterface } from '@geonetwork-ui/api/repository'
+import {
+ AuthService,
+ AvatarServiceInterface,
+} from '@geonetwork-ui/api/repository'
import { OrganizationsServiceInterface } from '@geonetwork-ui/common/domain/organizations.service.interface'
import { PlatformServiceInterface } from '@geonetwork-ui/common/domain/platform.service.interface'
import { TranslateModule } from '@ngx-translate/core'
-import { MockBuilder, MockProviders } from 'ng-mocks'
+import { MockBuilder, MockProvider, MockProviders } from 'ng-mocks'
import { SidebarComponent } from './sidebar.component'
describe('SidebarComponent', () => {
let component: SidebarComponent
let fixture: ComponentFixture
+ let service: AuthService
beforeEach(() => {
return MockBuilder(SidebarComponent)
})
+ afterEach(() => {
+ jest.resetAllMocks()
+ })
+
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [SidebarComponent, TranslateModule.forRoot()],
@@ -23,9 +31,13 @@ describe('SidebarComponent', () => {
AvatarServiceInterface,
OrganizationsServiceInterface
),
+ MockProvider(AuthService, {
+ logoutUrl: 'http://logout.com/bla?',
+ }),
],
}).compileComponents()
+ service = TestBed.inject(AuthService)
fixture = TestBed.createComponent(SidebarComponent)
component = fixture.componentInstance
fixture.detectChanges()
@@ -34,4 +46,21 @@ describe('SidebarComponent', () => {
it('should create', () => {
expect(component).toBeTruthy()
})
+
+ describe('logOut', () => {
+ it('should log out', async () => {
+ jest.spyOn(window, 'fetch').mockResolvedValue({
+ ok: true,
+ } as Response)
+
+ const originalUrl = window.origin
+
+ await component.logOut()
+
+ expect(window.fetch).toHaveBeenCalledWith(service.logoutUrl, {
+ method: 'GET',
+ })
+ expect(window.location.href.slice(0, -1)).toBe(originalUrl)
+ })
+ })
})
diff --git a/apps/metadata-editor/src/app/dashboard/sidebar/sidebar.component.ts b/apps/metadata-editor/src/app/dashboard/sidebar/sidebar.component.ts
index 0aeb4afca0..8b88be4411 100644
--- a/apps/metadata-editor/src/app/dashboard/sidebar/sidebar.component.ts
+++ b/apps/metadata-editor/src/app/dashboard/sidebar/sidebar.component.ts
@@ -3,7 +3,10 @@ import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'
import { TranslateModule } from '@ngx-translate/core'
import { DashboardMenuComponent } from '../dashboard-menu/dashboard-menu.component'
import { PlatformServiceInterface } from '@geonetwork-ui/common/domain/platform.service.interface'
-import { AvatarServiceInterface } from '@geonetwork-ui/api/repository'
+import {
+ AuthService,
+ AvatarServiceInterface,
+} from '@geonetwork-ui/api/repository'
import { LetDirective } from '@ngrx/component'
import { UiElementsModule } from '@geonetwork-ui/ui/elements'
import { OrganizationsServiceInterface } from '@geonetwork-ui/common/domain/organizations.service.interface'
@@ -31,7 +34,8 @@ export class SidebarComponent implements OnInit {
constructor(
public platformService: PlatformServiceInterface,
private avatarService: AvatarServiceInterface,
- public organisationsService: OrganizationsServiceInterface
+ public organisationsService: OrganizationsServiceInterface,
+ private authService: AuthService
) {}
ngOnInit(): void {
@@ -41,4 +45,21 @@ export class SidebarComponent implements OnInit {
(orgs, me) => orgs.filter((org) => org.name === me?.organisation)
)
}
+
+ logOut() {
+ const current_url = window.origin.toString()
+ fetch(this.authService.logoutUrl, {
+ method: 'GET',
+ })
+ .then((response) => {
+ if (response.ok) {
+ window.location.href = current_url
+ } else {
+ console.error('Logout failed')
+ }
+ })
+ .catch((error) => {
+ console.error('Error during logout request:', error)
+ })
+ }
}
diff --git a/conf/default.toml b/conf/default.toml
index 0cd7d309d6..bae70b32dc 100644
--- a/conf/default.toml
+++ b/conf/default.toml
@@ -23,6 +23,7 @@ proxy_path = ""
# - ${lang2}, ${lang3}: indicates if and where the current language should be part of the login URL in language 2 or 3 letter code
# Example to use the georchestra login page:
# login_url = "/cas/login?service=${current_url}"
+# logout_url = "/geonetwork/signout"
# This optional URL should point to the static html page wc-embedder.html which allows to display a web component (like chart and table) via a permalink.
# URLs can be indicated from the root of the same server starting with a "/" or as an external URL. Be conscious of potential CORS issues when using an external URL.
# The default location in the dockerized datahub app for example is "/datahub/wc-embedder.html".
diff --git a/libs/api/repository/src/lib/gn4/auth/auth.service.spec.ts b/libs/api/repository/src/lib/gn4/auth/auth.service.spec.ts
index f604788788..dabc434e34 100644
--- a/libs/api/repository/src/lib/gn4/auth/auth.service.spec.ts
+++ b/libs/api/repository/src/lib/gn4/auth/auth.service.spec.ts
@@ -82,4 +82,13 @@ describe('AuthService', () => {
)
})
})
+
+ describe('Logout', () => {
+ beforeEach(() => {
+ service = TestBed.inject(AuthService)
+ })
+ it('should return the logout url', () => {
+ expect(service.logoutUrl).toEqual('/geonetwork/signout')
+ })
+ })
})
diff --git a/libs/api/repository/src/lib/gn4/auth/auth.service.ts b/libs/api/repository/src/lib/gn4/auth/auth.service.ts
index fae24500ba..667e817aa3 100644
--- a/libs/api/repository/src/lib/gn4/auth/auth.service.ts
+++ b/libs/api/repository/src/lib/gn4/auth/auth.service.ts
@@ -5,11 +5,15 @@ import { TranslateService } from '@ngx-translate/core'
export const DEFAULT_GN4_LOGIN_URL = `/geonetwork/srv/\${lang3}/catalog.signin?redirect=\${current_url}`
export const LOGIN_URL = new InjectionToken('loginUrl')
+export const DEFAULT_GN4_LOGOUT_URL = `/geonetwork/signout`
+export const LOGOUT_URL = new InjectionToken('logoutUrl')
+
@Injectable({
providedIn: 'root',
})
export class AuthService {
baseLoginUrl = this.baseLoginUrlToken || DEFAULT_GN4_LOGIN_URL
+ baseLogoutUrl = this.baseLogoutUrlToken || DEFAULT_GN4_LOGOUT_URL
get loginUrl() {
let baseUrl = this.baseLoginUrl
const locationHasQueryParams = !!window.location.search
@@ -25,10 +29,14 @@ export class AuthService {
LANG_2_TO_3_MAPPER[this.translateService.currentLang]
)
}
+
+ get logoutUrl() {
+ return this.baseLogoutUrl
+ }
+
constructor(
- @Optional()
- @Inject(LOGIN_URL)
- private baseLoginUrlToken: string,
+ @Optional() @Inject(LOGIN_URL) private baseLoginUrlToken: string,
+ @Optional() @Inject(LOGOUT_URL) private baseLogoutUrlToken: string,
private translateService: TranslateService
) {}
}
diff --git a/libs/util/app-config/src/lib/app-config.spec.ts b/libs/util/app-config/src/lib/app-config.spec.ts
index e590d8a9ad..53b05d6bfa 100644
--- a/libs/util/app-config/src/lib/app-config.spec.ts
+++ b/libs/util/app-config/src/lib/app-config.spec.ts
@@ -133,6 +133,7 @@ describe('app config utils', () => {
PROXY_PATH: '/proxy/?url=',
METADATA_LANGUAGE: 'fre',
LOGIN_URL: '/cas/login?service=',
+ LOGOUT_URL: '/geonetwork/signout',
WEB_COMPONENT_EMBEDDER_URL: '/datahub/wc-embedder.html',
})
})
diff --git a/libs/util/app-config/src/lib/app-config.ts b/libs/util/app-config/src/lib/app-config.ts
index 04ca6b87fd..540073903c 100644
--- a/libs/util/app-config/src/lib/app-config.ts
+++ b/libs/util/app-config/src/lib/app-config.ts
@@ -98,6 +98,7 @@ export function loadAppConfig() {
'proxy_path',
'metadata_language',
'login_url',
+ 'logout_url',
'web_component_embedder_url',
'languages',
'contact_email',
@@ -124,6 +125,7 @@ export function loadAppConfig() {
).toLowerCase()
: undefined,
LOGIN_URL: parsedGlobalSection.login_url,
+ LOGOUT_URL: parsedGlobalSection.logout_url,
WEB_COMPONENT_EMBEDDER_URL:
parsedGlobalSection.web_component_embedder_url,
LANGUAGES: parsedGlobalSection.languages,
diff --git a/libs/util/app-config/src/lib/fixtures.ts b/libs/util/app-config/src/lib/fixtures.ts
index 2a7cec35d4..3126cb0492 100644
--- a/libs/util/app-config/src/lib/fixtures.ts
+++ b/libs/util/app-config/src/lib/fixtures.ts
@@ -6,6 +6,7 @@ geonetwork4_api_url = "/geonetwork/srv/api"
proxy_path = "/proxy/?url="
metadata_language = "fre"
login_url = "/cas/login?service="
+logout_url = "/geonetwork/signout"
web_component_embedder_url = "/datahub/wc-embedder.html"
[map]
diff --git a/libs/util/app-config/src/lib/model.ts b/libs/util/app-config/src/lib/model.ts
index 84682b6eee..742aa7fc4b 100644
--- a/libs/util/app-config/src/lib/model.ts
+++ b/libs/util/app-config/src/lib/model.ts
@@ -6,6 +6,7 @@ export interface GlobalConfig {
PROXY_PATH?: string
METADATA_LANGUAGE?: string
LOGIN_URL?: string
+ LOGOUT_URL?: string
WEB_COMPONENT_EMBEDDER_URL?: string
LANGUAGES?: string[]
CONTACT_EMAIL?: string
diff --git a/translations/de.json b/translations/de.json
index 63d5ec7762..00ae56f652 100644
--- a/translations/de.json
+++ b/translations/de.json
@@ -296,6 +296,7 @@
"editor.record.undo.tooltip.disabled": "",
"editor.record.undo.tooltip.enabled": "",
"editor.record.upToDate": "Dieser Datensatz ist auf dem neuesten Stand",
+ "editor.sidebar.logout": "",
"editor.sidebar.menu.editor": "",
"editor.temporary.disabled": "",
"externalviewer.dataset.unnamed": "Datensatz aus dem Datahub",
diff --git a/translations/en.json b/translations/en.json
index 3f15cfe258..f4c7e19558 100644
--- a/translations/en.json
+++ b/translations/en.json
@@ -296,6 +296,7 @@
"editor.record.undo.tooltip.disabled": "There are no pending changes on this record",
"editor.record.undo.tooltip.enabled": "Clicking this button will cancel the pending changes on this record.",
"editor.record.upToDate": "This record is up to date",
+ "editor.sidebar.logout": "Log out",
"editor.sidebar.menu.editor": "Editor",
"editor.temporary.disabled": "Not implemented yet",
"externalviewer.dataset.unnamed": "Datahub layer",
diff --git a/translations/es.json b/translations/es.json
index 10e1c8cb55..12e2cba9ce 100644
--- a/translations/es.json
+++ b/translations/es.json
@@ -296,6 +296,7 @@
"editor.record.undo.tooltip.disabled": "",
"editor.record.undo.tooltip.enabled": "",
"editor.record.upToDate": "",
+ "editor.sidebar.logout": "",
"editor.sidebar.menu.editor": "",
"editor.temporary.disabled": "",
"externalviewer.dataset.unnamed": "",
diff --git a/translations/fr.json b/translations/fr.json
index a12538009c..ba4da4eef8 100644
--- a/translations/fr.json
+++ b/translations/fr.json
@@ -296,6 +296,7 @@
"editor.record.undo.tooltip.disabled": "Il n'y a pas de modifications en cours sur cette fiche",
"editor.record.undo.tooltip.enabled": "Cliquez sur ce bouton pour annuler les modifications apportées à cette fiche",
"editor.record.upToDate": "",
+ "editor.sidebar.logout": "Se déconnecter",
"editor.sidebar.menu.editor": "",
"editor.temporary.disabled": "Pas encore implémenté",
"externalviewer.dataset.unnamed": "Couche du datahub",
diff --git a/translations/it.json b/translations/it.json
index ed95d6c683..272d8d2da0 100644
--- a/translations/it.json
+++ b/translations/it.json
@@ -296,6 +296,7 @@
"editor.record.undo.tooltip.disabled": "",
"editor.record.undo.tooltip.enabled": "",
"editor.record.upToDate": "",
+ "editor.sidebar.logout": "",
"editor.sidebar.menu.editor": "",
"editor.temporary.disabled": "",
"externalviewer.dataset.unnamed": "Layer del datahub",
diff --git a/translations/nl.json b/translations/nl.json
index 1c50db16cd..fc38f38858 100644
--- a/translations/nl.json
+++ b/translations/nl.json
@@ -296,6 +296,7 @@
"editor.record.undo.tooltip.disabled": "",
"editor.record.undo.tooltip.enabled": "",
"editor.record.upToDate": "",
+ "editor.sidebar.logout": "",
"editor.sidebar.menu.editor": "",
"editor.temporary.disabled": "",
"externalviewer.dataset.unnamed": "",
diff --git a/translations/pt.json b/translations/pt.json
index 3d48db46d9..4a412c2691 100644
--- a/translations/pt.json
+++ b/translations/pt.json
@@ -296,6 +296,7 @@
"editor.record.undo.tooltip.disabled": "",
"editor.record.undo.tooltip.enabled": "",
"editor.record.upToDate": "",
+ "editor.sidebar.logout": "",
"editor.sidebar.menu.editor": "",
"editor.temporary.disabled": "",
"externalviewer.dataset.unnamed": "",
diff --git a/translations/sk.json b/translations/sk.json
index cb11870799..bef5dc3a42 100644
--- a/translations/sk.json
+++ b/translations/sk.json
@@ -296,6 +296,7 @@
"editor.record.undo.tooltip.disabled": "",
"editor.record.undo.tooltip.enabled": "",
"editor.record.upToDate": "",
+ "editor.sidebar.logout": "",
"editor.sidebar.menu.editor": "",
"editor.temporary.disabled": "",
"externalviewer.dataset.unnamed": "",