From b0d40f919c795837dba0120bced202c6bc520f75 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 9 Apr 2026 14:45:10 +0000 Subject: [PATCH 1/3] Initial plan From ff9d97bb06a1c09546c38c6857702361ee340f35 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 9 Apr 2026 15:12:03 +0000 Subject: [PATCH 2/3] Fix UI context path: inject window.__openidm_context_path from ResourceServlet into index.html Agent-Logs-Url: https://github.com/OpenIdentityPlatform/OpenIDM/sessions/855cbe4a-33a7-49bc-88c4-11af25bed6b1 Co-authored-by: vharseko <6818498+vharseko@users.noreply.github.com> --- .../ui/internal/service/ResourceServlet.java | 38 ++++++++++++++++++- .../src/main/resources/index.html | 3 +- .../openidm/ui/common/util/Constants.js | 5 ++- .../openidm/ui/common/util/ConstantsTest.js | 26 ++++++++++++- 4 files changed, 68 insertions(+), 4 deletions(-) diff --git a/openidm-servlet/src/main/java/org/forgerock/openidm/ui/internal/service/ResourceServlet.java b/openidm-servlet/src/main/java/org/forgerock/openidm/ui/internal/service/ResourceServlet.java index c9702b57ca..7b2037aa58 100644 --- a/openidm-servlet/src/main/java/org/forgerock/openidm/ui/internal/service/ResourceServlet.java +++ b/openidm-servlet/src/main/java/org/forgerock/openidm/ui/internal/service/ResourceServlet.java @@ -15,7 +15,7 @@ * limitations under the License. * * Portions copyright 2013-2015 ForgeRock AS. - * Portions Copyrighted 2024-2025 3A Systems LLC. + * Portions Copyrighted 2024-2026 3A Systems LLC. */ package org.forgerock.openidm.ui.internal.service; @@ -25,6 +25,7 @@ import java.io.OutputStream; import java.net.URL; import java.net.URLConnection; +import java.nio.charset.StandardCharsets; import java.util.Dictionary; import java.util.Hashtable; import java.util.Map; @@ -38,6 +39,7 @@ import org.forgerock.openidm.config.enhanced.EnhancedConfig; import org.forgerock.openidm.core.IdentityServer; import org.forgerock.openidm.core.PropertyUtil; +import org.forgerock.openidm.core.ServerConstants; import org.forgerock.openidm.servletregistration.ServletRegistration; import org.ops4j.pax.web.service.WebContainer; import org.osgi.service.component.ComponentContext; @@ -129,6 +131,7 @@ protected void doGet(HttpServletRequest req, HttpServletResponse res) } // Never cache index.html to ensure we always have the current product version for asset requests + // (Cache-Control is also set in handleIndexHtml for the index.html special case) if (target.equals("/index.html")) { res.setHeader("Cache-Control", "no-cache"); } @@ -157,12 +160,45 @@ protected void doGet(HttpServletRequest req, HttpServletResponse res) if (url == null) { res.sendError(HttpServletResponse.SC_NOT_FOUND); + } else if (target.equals("/index.html")) { + handleIndexHtml(res, url); } else { handle(req, res, url, target); } } } + /** + * Serves index.html with the openidm.context.path injected as a global JS variable. + * Replaces {@code } with a small inline script that sets + * {@code window.__openidm_context_path} before RequireJS boots. + */ + private void handleIndexHtml(HttpServletResponse res, URL url) throws IOException { + res.setContentType("text/html"); + res.setHeader("Cache-Control", "no-cache"); + + String contextPath = System.getProperty( + ServerConstants.OPENIDM_CONTEXT_PATH_PROPERTY, + ServerConstants.OPENIDM_CONTEXT_PATH_DEFAULT); + // Strip leading slash — the UI Constants.context value does not use it + String contextValue = contextPath.startsWith("/") ? contextPath.substring(1) : contextPath; + + byte[] raw; + try (InputStream is = url.openStream()) { + raw = is.readAllBytes(); + } + String html = new String(raw, StandardCharsets.UTF_8); + + // Inject a tiny script right before so it is available before RequireJS loads + String injection = "\n"; + html = html.replace("", injection); + + byte[] out = html.getBytes(StandardCharsets.UTF_8); + res.setContentLength(out.length); + res.getOutputStream().write(out); + } + /** * Initializes the servlet and registers it with the WebContainer. * diff --git a/openidm-ui/openidm-ui-api/src/main/resources/index.html b/openidm-ui/openidm-ui-api/src/main/resources/index.html index bfa7a784c8..58e3f76f6d 100755 --- a/openidm-ui/openidm-ui-api/src/main/resources/index.html +++ b/openidm-ui/openidm-ui-api/src/main/resources/index.html @@ -51,7 +51,8 @@ url = decodeURIComponent(url[1]); } else { // default Swagger JSON URL - url = "/openidm/?_api"; + var ctx = (window.__openidm_context_path || "openidm"); + url = "/" + ctx + "/?_api"; } if (window.SwaggerTranslator) { diff --git a/openidm-ui/openidm-ui-common/src/main/js/org/forgerock/openidm/ui/common/util/Constants.js b/openidm-ui/openidm-ui-common/src/main/js/org/forgerock/openidm/ui/common/util/Constants.js index db45bdb12a..6f8c3585f9 100644 --- a/openidm-ui/openidm-ui-common/src/main/js/org/forgerock/openidm/ui/common/util/Constants.js +++ b/openidm-ui/openidm-ui-common/src/main/js/org/forgerock/openidm/ui/common/util/Constants.js @@ -12,12 +12,15 @@ * information: "Portions copyright [year] [name of copyright owner]". * * Copyright 2016 ForgeRock AS. + * Portions Copyright 2026 3A Systems, LLC. */ define([ "org/forgerock/commons/ui/common/util/Constants" ], function (commonConstants) { - commonConstants.context = "openidm"; + commonConstants.context = (typeof window !== "undefined" && window.__openidm_context_path) + ? window.__openidm_context_path + : "openidm"; commonConstants.HEADER_PARAM_PASSWORD = "X-OpenIDM-Password"; commonConstants.HEADER_PARAM_USERNAME = "X-OpenIDM-Username"; diff --git a/openidm-ui/openidm-ui-common/src/test/qunit/tests/org/forgerock/openidm/ui/common/util/ConstantsTest.js b/openidm-ui/openidm-ui-common/src/test/qunit/tests/org/forgerock/openidm/ui/common/util/ConstantsTest.js index 3e9e7c24f0..33963938d4 100644 --- a/openidm-ui/openidm-ui-common/src/test/qunit/tests/org/forgerock/openidm/ui/common/util/ConstantsTest.js +++ b/openidm-ui/openidm-ui-common/src/test/qunit/tests/org/forgerock/openidm/ui/common/util/ConstantsTest.js @@ -23,4 +23,28 @@ define([ "Custom context should produce the correct REST API path prefix"); Constants.context = originalContext; }); -}); \ No newline at end of file + + QUnit.test("window.__openidm_context_path overrides the default context", function (assert) { + var originalValue = window.__openidm_context_path; + window.__openidm_context_path = "myidm"; + var contextValue = (typeof window !== "undefined" && window.__openidm_context_path) + ? window.__openidm_context_path + : "openidm"; + assert.equal(contextValue, "myidm", + "window.__openidm_context_path should override the default 'openidm' context"); + window.__openidm_context_path = originalValue; + }); + + QUnit.test("window.__openidm_context_path falls back to 'openidm' when unset", function (assert) { + var originalValue = window.__openidm_context_path; + delete window.__openidm_context_path; + var contextValue = (typeof window !== "undefined" && window.__openidm_context_path) + ? window.__openidm_context_path + : "openidm"; + assert.equal(contextValue, "openidm", + "Should fall back to 'openidm' when window.__openidm_context_path is not set"); + if (originalValue !== undefined) { + window.__openidm_context_path = originalValue; + } + }); +}); From f34a6212ab1ee729975afcbb13f5c8f4ff1dbfc4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 9 Apr 2026 15:16:15 +0000 Subject: [PATCH 3/3] Improve context path escaping and use replaceFirst for injection Agent-Logs-Url: https://github.com/OpenIdentityPlatform/OpenIDM/sessions/855cbe4a-33a7-49bc-88c4-11af25bed6b1 Co-authored-by: vharseko <6818498+vharseko@users.noreply.github.com> --- .../ui/internal/service/ResourceServlet.java | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/openidm-servlet/src/main/java/org/forgerock/openidm/ui/internal/service/ResourceServlet.java b/openidm-servlet/src/main/java/org/forgerock/openidm/ui/internal/service/ResourceServlet.java index 7b2037aa58..49ce07eb5d 100644 --- a/openidm-servlet/src/main/java/org/forgerock/openidm/ui/internal/service/ResourceServlet.java +++ b/openidm-servlet/src/main/java/org/forgerock/openidm/ui/internal/service/ResourceServlet.java @@ -189,10 +189,18 @@ private void handleIndexHtml(HttpServletResponse res, URL url) throws IOExceptio } String html = new String(raw, StandardCharsets.UTF_8); - // Inject a tiny script right before so it is available before RequireJS loads + // Inject a tiny script right before so it is available before RequireJS loads. + // Escape characters that could break out of the JS string or the script tag. + String safeContextValue = contextValue + .replace("&", "&") + .replace("<", "\\u003c") + .replace(">", "\\u003e") + .replace("\"", "\\\"") + .replace("'", "\\'"); String injection = "\n"; - html = html.replace("", injection); + + safeContextValue + "\";\n"; + // replaceFirst to guard against malformed HTML with multiple tags + html = html.replaceFirst("", injection); byte[] out = html.getBytes(StandardCharsets.UTF_8); res.setContentLength(out.length);