From f938d894b93028e1d1ece6bf2116e6822fb340a6 Mon Sep 17 00:00:00 2001 From: Marek Posolda Date: Mon, 5 Jan 2026 22:18:39 +0100 Subject: [PATCH] AdminEvent.getResourcePath() returns paths with duplicated slashes closes #45114 Signed-off-by: mposolda --- js/apps/admin-ui/src/events/AdminEvents.tsx | 4 +-- js/apps/admin-ui/test/events/list.spec.ts | 27 +++++++++++++++++++ js/apps/admin-ui/test/events/list.ts | 23 ++++++++++++++++ .../resources/admin/AdminEventBuilder.java | 4 ++- 4 files changed, 55 insertions(+), 3 deletions(-) diff --git a/js/apps/admin-ui/src/events/AdminEvents.tsx b/js/apps/admin-ui/src/events/AdminEvents.tsx index d2904df06c5..fa83b599472 100644 --- a/js/apps/admin-ui/src/events/AdminEvents.tsx +++ b/js/apps/admin-ui/src/events/AdminEvents.tsx @@ -336,7 +336,7 @@ export const AdminEvents = ({ resourcePath }: AdminEventsProps) => { collapsedText: t("showRemaining"), }} variant={SelectVariant.typeaheadMulti} - typeAheadAriaLabel="Select" + typeAheadAriaLabel="select-resourceTypes" onToggle={(isOpen) => setSelectResourceTypesOpen(isOpen) } @@ -403,7 +403,7 @@ export const AdminEvents = ({ resourcePath }: AdminEventsProps) => { collapsedText: t("showRemaining"), }} variant={SelectVariant.typeaheadMulti} - typeAheadAriaLabel="Select" + typeAheadAriaLabel="select-operationTypes" onToggle={(isOpen) => setSelectOperationTypesOpen(isOpen) } diff --git a/js/apps/admin-ui/test/events/list.spec.ts b/js/apps/admin-ui/test/events/list.spec.ts index 5714b2c7deb..ae08325ff91 100644 --- a/js/apps/admin-ui/test/events/list.spec.ts +++ b/js/apps/admin-ui/test/events/list.spec.ts @@ -16,6 +16,7 @@ import { clickSearchPanel, enableSaveEvents, fillSearchPanel, + fillAdminEventsSearchPanel, goToAdminEventsTab, goToEventsConfig, } from "./list.ts"; @@ -120,5 +121,31 @@ test.describe.serial("Events tests", () => { await goToAdminEventsTab(page); await assertAxeViolations(page); }); + + test("creating user", async ({ page }) => { + const userToCreate = { + username: `my-user`, + enabled: true, + credentials: [{ value: "events-test" }], + realm: realmName, + email: "some-other@email.com", + firstName: "My", + lastName: "User", + }; + + await adminClient.createUser(userToCreate); + + await goToAdminEventsTab(page); + + await clickSearchPanel(page); + await assertSearchButtonDisabled(page); + await fillAdminEventsSearchPanel(page, { + resourceType: "USER", + }); + await clickSearchButton(page); + + await assertRowExists(page, "users/"); + await assertRowExists(page, "users//", false); // Assert no trailing slash + }); }); }); diff --git a/js/apps/admin-ui/test/events/list.ts b/js/apps/admin-ui/test/events/list.ts index 30798f85a2e..8aec1848637 100644 --- a/js/apps/admin-ui/test/events/list.ts +++ b/js/apps/admin-ui/test/events/list.ts @@ -38,6 +38,11 @@ type SearchParam = { eventType?: string; }; +type AdminEventsSearchParam = { + resourceType?: string; + operationType?: string; +}; + export async function fillSearchPanel( page: Page, { userId, client, eventType }: SearchParam, @@ -47,6 +52,24 @@ export async function fillSearchPanel( if (eventType) await selectItem(page, page.getByLabel("Select"), eventType); } +export async function fillAdminEventsSearchPanel( + page: Page, + { resourceType, operationType }: AdminEventsSearchParam, +) { + if (resourceType) + await selectItem( + page, + page.getByLabel("select-resourceTypes"), + resourceType, + ); + if (operationType) + await selectItem( + page, + page.getByLabel("select-operationTypes"), + operationType, + ); +} + export async function assertSearchButtonDisabled(page: Page, disabled = true) { if (disabled) { await expect(page.getByTestId("search-events-btn")).toBeDisabled(); diff --git a/services/src/main/java/org/keycloak/services/resources/admin/AdminEventBuilder.java b/services/src/main/java/org/keycloak/services/resources/admin/AdminEventBuilder.java index 8089f1b33a8..4a96b98ee04 100644 --- a/services/src/main/java/org/keycloak/services/resources/admin/AdminEventBuilder.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/AdminEventBuilder.java @@ -233,7 +233,9 @@ public class AdminEventBuilder { public AdminEventBuilder resourcePath(UriInfo uriInfo, String id) { StringBuilder sb = new StringBuilder(); sb.append(getResourcePath(uriInfo)); - sb.append("/"); + if (!sb.toString().endsWith("/")) { + sb.append("/"); + } sb.append(id); adminEvent.setResourcePath(sb.toString()); return this;