From 77fb3c4dd472b1b1b284dc5b6597d7b1e64879d1 Mon Sep 17 00:00:00 2001 From: Jon Koops Date: Wed, 19 Jun 2024 15:21:53 +0200 Subject: [PATCH] Use correct host URL for Admin Console requests (#30535) Closes #30432 Signed-off-by: Jon Koops --- .../topics/changes/changes-25_0_0.adoc | 19 ++---- js/apps/account-ui/README.md | 2 +- .../theme/keycloak.v3/account/index.ftl | 1 + js/apps/account-ui/src/api.ts | 14 ++-- js/apps/account-ui/src/api/methods.ts | 2 +- js/apps/account-ui/src/api/request.ts | 2 +- js/apps/account-ui/src/environment.ts | 32 +--------- js/apps/account-ui/src/i18n.ts | 2 +- .../theme/keycloak.v2/admin/index.ftl | 2 + js/apps/admin-ui/src/admin-client.ts | 2 +- js/apps/admin-ui/src/environment.ts | 29 +++------ js/apps/admin-ui/src/i18n/i18n.ts | 2 +- .../add/SamlConnectSettings.tsx | 2 +- .../add/SamlGeneralSettings.tsx | 2 +- .../ui-shared/src/context/KeycloakContext.tsx | 2 +- js/libs/ui-shared/src/context/environment.ts | 24 +++++-- .../resources/account/AccountConsole.java | 34 ++++++---- .../resources/admin/AdminConsole.java | 64 ++++++++----------- 18 files changed, 105 insertions(+), 132 deletions(-) diff --git a/docs/documentation/upgrading/topics/changes/changes-25_0_0.adoc b/docs/documentation/upgrading/topics/changes/changes-25_0_0.adoc index 0dfa4ed838..1e2dbd8898 100644 --- a/docs/documentation/upgrading/topics/changes/changes-25_0_0.adoc +++ b/docs/documentation/upgrading/topics/changes/changes-25_0_0.adoc @@ -445,22 +445,15 @@ If you wish to use Oracle DB, you must manually install a version of the Oracle = Deprecated theme variables -The following variables were deprecated in the Account theme: +The following variables were deprecated in the Admin theme and will be removed in a future version: -* `authUrl`. Use `authServerUrl` instead. +* `authServerUrl`. Use `serverBaseUrl` instead. +* `authUrl`. Use `adminBaseUrl` instead. -The following variables from the environment script injected into the page of the Account theme are deprecated: +The following variables were deprecated in the Account theme and will be removed in a future version: -* `authUrl`. Use `authServerUrl` instead. -* `features.isInternationalizationEnabled`. Do not use this variable. - -The following variables were deprecated in the Admin theme: - -* `authUrl`. Do not use this variable. - -The following variables from the environment script injected into the page of the Admin theme are deprecated: - -* `authUrl`. Do not use this variable. +* `authServerUrl`. Use `serverBaseUrl` instead, note `serverBaseUrl` does not include trailing slash. +* `authUrl`. Use `serverBaseUrl` instead, note `serverBaseUrl` does not include trailing slash. = Methods to get and set current refresh token in client session are now deprecated diff --git a/js/apps/account-ui/README.md b/js/apps/account-ui/README.md index 650b4c9114..87c8745b7d 100644 --- a/js/apps/account-ui/README.md +++ b/js/apps/account-ui/README.md @@ -22,7 +22,7 @@ import { KeycloakProvider } from "@keycloak/keycloak-account-ui"; //... diff --git a/js/apps/account-ui/maven-resources/theme/keycloak.v3/account/index.ftl b/js/apps/account-ui/maven-resources/theme/keycloak.v3/account/index.ftl index 1cc5a4d4e9..47332e2b58 100644 --- a/js/apps/account-ui/maven-resources/theme/keycloak.v3/account/index.ftl +++ b/js/apps/account-ui/maven-resources/theme/keycloak.v3/account/index.ftl @@ -110,6 +110,7 @@ + * ``` */ -export function getInjectedEnvironment(defaults: T): T { +export function getInjectedEnvironment(): T { const element = document.getElementById("environment"); const contents = element?.textContent; @@ -33,7 +43,7 @@ export function getInjectedEnvironment(defaults: T): T { } try { - return { ...defaults, ...JSON.parse(contents) }; + return JSON.parse(contents); } catch (error) { throw new Error("Unable to parse environment variables as JSON."); } diff --git a/services/src/main/java/org/keycloak/services/resources/account/AccountConsole.java b/services/src/main/java/org/keycloak/services/resources/account/AccountConsole.java index f7ffc3af69..cebfe97f64 100644 --- a/services/src/main/java/org/keycloak/services/resources/account/AccountConsole.java +++ b/services/src/main/java/org/keycloak/services/resources/account/AccountConsole.java @@ -96,22 +96,34 @@ public class AccountConsole implements AccountResourceProvider { @NoCache @Path("{any:.*}") public Response getMainPage() throws IOException, FreeMarkerException { - UriInfo uriInfo = session.getContext().getUri(UrlType.FRONTEND); - URI accountBaseUrl = uriInfo.getBaseUriBuilder().path(RealmsResource.class).path(realm.getName()) - .path(Constants.ACCOUNT_MANAGEMENT_CLIENT_ID).path("/").build(realm); + // Get the URI info of the server and admin console. + final var serverUriInfo = session.getContext().getUri(UrlType.FRONTEND); + final var adminUriInfo = session.getContext().getUri(UrlType.ADMIN); - Map map = new HashMap<>(); + // Get the base URLs of the server and admin console. + final var serverBaseUri = serverUriInfo.getBaseUri(); + final var adminBaseUri = adminUriInfo.getBaseUri(); - URI adminBaseUri = session.getContext().getUri(UrlType.ADMIN).getBaseUri(); - URI authUrl = uriInfo.getBaseUri(); - var authServerUrl = authUrl.getPath().endsWith("/") ? authUrl : authUrl + "/"; - // TODO: The 'authUrl' variable is deprecated and only exists to provide backwards compatibility for older themes, it should be removed in a future version. - map.put("authUrl", authServerUrl); - map.put("authServerUrl", authServerUrl); + // Strip any trailing slashes from the URLs. + final var serverBaseUrl = serverBaseUri.toString().replaceFirst("/+$", ""); + + final var map = new HashMap(); + final var accountBaseUrl = serverUriInfo.getBaseUriBuilder() + .path(RealmsResource.class) + .path(realm.getName()) + .path(Constants.ACCOUNT_MANAGEMENT_CLIENT_ID) + .path("/") + .build(realm); + + map.put("serverBaseUrl", serverBaseUrl); + // TODO: Some variables are deprecated and only exist to provide backwards compatibility for older themes, they should be removed in a future version. + // Note that these should be removed from the template of the Account Console as well. + map.put("authUrl", serverBaseUrl + "/"); // Superseded by 'serverBaseUrl', remove in the future. + map.put("authServerUrl", serverBaseUrl + "/"); // Superseded by 'serverBaseUrl', remove in the future. map.put("baseUrl", accountBaseUrl.getPath().endsWith("/") ? accountBaseUrl : accountBaseUrl + "/"); map.put("realm", realm); map.put("clientId", Constants.ACCOUNT_CONSOLE_CLIENT_ID); - map.put("resourceUrl", Urls.themeRoot(authUrl).getPath() + "/" + Constants.ACCOUNT_MANAGEMENT_CLIENT_ID + "/" + theme.getName()); + map.put("resourceUrl", Urls.themeRoot(serverBaseUri).getPath() + "/" + Constants.ACCOUNT_MANAGEMENT_CLIENT_ID + "/" + theme.getName()); map.put("resourceCommonUrl", Urls.themeRoot(adminBaseUri).getPath() + "/common/keycloak"); map.put("resourceVersion", Version.RESOURCES_VERSION); diff --git a/services/src/main/java/org/keycloak/services/resources/admin/AdminConsole.java b/services/src/main/java/org/keycloak/services/resources/admin/AdminConsole.java index 3c010360f8..9c24070d23 100644 --- a/services/src/main/java/org/keycloak/services/resources/admin/AdminConsole.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/AdminConsole.java @@ -313,47 +313,39 @@ public class AdminConsole { } /** - * Main page of this realm's admin console - * - * @return - * @throws URISyntaxException + * Main page of this realm's admin console. */ @GET @NoCache public Response getMainPage() throws IOException, FreeMarkerException { - if (!session.getContext().getUri(UrlType.ADMIN).getRequestUri().getPath().endsWith("/")) { - return Response.status(302).location(session.getContext().getUri(UrlType.ADMIN).getRequestUriBuilder().path("/").build()).build(); + final var baseUriInfo = session.getContext().getUri(UrlType.FRONTEND); + final var adminUriInfo = session.getContext().getUri(UrlType.ADMIN); + + // Redirect to a URL with a trailing slash if the current URL doesn't have one. + if (!adminUriInfo.getRequestUri().getPath().endsWith("/")) { + return Response.status(302).location(adminUriInfo.getRequestUriBuilder().path("/").build()).build(); } else { - Theme theme = AdminRoot.getTheme(session, realm); + // Get the base URLs of the server and admin console. + final var serverBaseUri = baseUriInfo.getBaseUri(); + final var adminBaseUri = adminUriInfo.getBaseUri(); - Map map = new HashMap<>(); + // Strip any trailing slashes from the URLs. + final var serverBaseUrl = serverBaseUri.toString().replaceFirst("/+$", ""); + final var adminBaseUrl = adminBaseUri.toString().replaceFirst("/+$", ""); - URI adminBaseUri = session.getContext().getUri(UrlType.ADMIN).getBaseUri(); - String adminBaseUrl = adminBaseUri.toString(); - if (adminBaseUrl.endsWith("/")) { - adminBaseUrl = adminBaseUrl.substring(0, adminBaseUrl.length() - 1); - } + final var map = new HashMap(); + final var theme = AdminRoot.getTheme(session, realm); - String kcJsRelativeBasePath = adminBaseUri.getPath(); - - if(!kcJsRelativeBasePath.endsWith("/")) { - kcJsRelativeBasePath = kcJsRelativeBasePath + "/"; - } - - URI authServerBaseUri = session.getContext().getUri(UrlType.FRONTEND).getBaseUri(); - - String authServerBaseUrl = authServerBaseUri.toString(); - if (authServerBaseUrl.endsWith("/")) { - authServerBaseUrl = authServerBaseUrl.substring(0, authServerBaseUrl.length() - 1); - } - - map.put("authServerUrl", authServerBaseUrl); - // TODO: The 'authUrl' variable is deprecated and only exists to provide backwards compatibility for older themes, it should be removed in a future version. - map.put("authUrl", adminBaseUrl); + map.put("serverBaseUrl", serverBaseUrl); + map.put("adminBaseUrl", adminBaseUrl); + // TODO: Some variables are deprecated and only exist to provide backwards compatibility for older themes, they should be removed in a future version. + // Note that these should be removed from the template of the Administration Console as well. + map.put("authServerUrl", serverBaseUrl); // Superseded by 'serverBaseUrl', remove in the future. + map.put("authUrl", adminBaseUrl); // Superseded by 'adminBaseUrl', remove in the future. map.put("consoleBaseUrl", Urls.adminConsoleRoot(adminBaseUri, realm.getName()).getPath()); map.put("resourceUrl", Urls.themeRoot(adminBaseUri).getPath() + "/admin/" + theme.getName()); map.put("resourceCommonUrl", Urls.themeRoot(adminBaseUri).getPath() + "/common/keycloak"); - map.put("keycloakJsUrl", kcJsRelativeBasePath + "js/keycloak.js?version=" + Version.RESOURCES_VERSION); + map.put("keycloakJsUrl", adminBaseUrl + "/js/keycloak.js?version=" + Version.RESOURCES_VERSION); map.put("masterRealm", Config.getAdminRealm()); map.put("resourceVersion", Version.RESOURCES_VERSION); map.put("loginRealm", realm.getName()); @@ -380,13 +372,13 @@ public class AdminConsole { map.put("entryImports", entryImports); } - FreeMarkerProvider freeMarkerUtil = session.getProvider(FreeMarkerProvider.class); - String result = freeMarkerUtil.processTemplate(map, "index.ftl", theme); - Response.ResponseBuilder builder = Response.status(Response.Status.OK).type(MediaType.TEXT_HTML_UTF_8).language(Locale.ENGLISH).entity(result); + final var freeMarkerUtil = session.getProvider(FreeMarkerProvider.class); + final var result = freeMarkerUtil.processTemplate(map, "index.ftl", theme); + final var builder = Response.status(Response.Status.OK).type(MediaType.TEXT_HTML_UTF_8).language(Locale.ENGLISH).entity(result); - // Replace CSP if admin is hosted on different URL - if (!adminBaseUri.equals(authServerBaseUri)) { - session.getProvider(SecurityHeadersProvider.class).options().allowFrameSrc(UriUtils.getOrigin(authServerBaseUri)); + // Allow iframes to be embedded from the server if the admin console is running on a different URL. + if (!adminBaseUri.equals(serverBaseUri)) { + session.getProvider(SecurityHeadersProvider.class).options().allowFrameSrc(UriUtils.getOrigin(serverBaseUri)); } return builder.build();