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);