diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f4e8d467ee..805240230b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -67,6 +67,25 @@ jobs: npx playwright test --reporter=list env: OPENIDM_URL: http://localhost:8080 + - name: Start OpenIDM with custom context path /myidm + if: runner.os == 'Linux' + run: | + openidm/shutdown.sh + timeout 1m bash -c 'while [ -f openidm/.openidm.pid ]; do sleep 2; done' || true + rm -rf openidm/logs/* + OPENIDM_OPTS="-Dlogback.configurationFile=conf/logging-config.groovy -Dopenidm.context.path=/myidm" openidm/startup.sh & + timeout 3m bash -c 'until grep -q "OpenIDM ready" openidm/logs/openidm0.log.0 ; do sleep 5; done' || cat openidm/logs/openidm0.log.0 + grep -q "OpenIDM ready" openidm/logs/openidm0.log.0 + ! grep "ERROR" openidm/logs/openidm0.log.0 + ! grep "SEVERE" openidm/logs/openidm0.log.0 + - name: UI Smoke Tests with /myidm context path (Playwright) + if: runner.os == 'Linux' + run: | + cd e2e + npx playwright test --reporter=list + env: + OPENIDM_URL: http://localhost:8080 + OPENIDM_CONTEXT_PATH: /myidm - name: Test on Windows if: runner.os == 'Windows' run: | diff --git a/Dockerfile b/Dockerfile index 39437c20c5..561d723b60 100644 --- a/Dockerfile +++ b/Dockerfile @@ -24,7 +24,8 @@ WORKDIR /opt #COPY openidm-zip/target/openidm-*.zip ./ -RUN apt-get update \ +RUN echo 'Acquire::Retries "3";' > /etc/apt/apt.conf.d/80-retries \ + && apt-get update \ && apt-get install -y --no-install-recommends curl unzip \ && bash -c 'if [ ! -z "$VERSION" ] ; then rm -rf ./*.zip ; curl -L https://github.com/OpenIdentityPlatform/OpenIDM/releases/download/$VERSION/openidm-$VERSION.zip --output openidm-$VERSION.zip ; fi' \ && unzip openidm-*.zip && rm -rf *.zip \ diff --git a/e2e/ui-smoke-test.spec.mjs b/e2e/ui-smoke-test.spec.mjs index 7d95d8689b..d5e58afef0 100644 --- a/e2e/ui-smoke-test.spec.mjs +++ b/e2e/ui-smoke-test.spec.mjs @@ -18,6 +18,7 @@ import { test, expect } from "@playwright/test"; const BASE_URL = process.env.OPENIDM_URL || "http://localhost:8080"; +const CONTEXT_PATH = process.env.OPENIDM_CONTEXT_PATH || "/openidm"; const ADMIN_USER = process.env.OPENIDM_ADMIN_USER || "openidm-admin"; const ADMIN_PASS = process.env.OPENIDM_ADMIN_PASS || "openidm-admin"; @@ -119,7 +120,7 @@ test.describe("OpenIDM UI Smoke Tests", () => { }); test("REST API ping is accessible", async ({ request }) => { - const response = await request.get(`${BASE_URL}/openidm/info/ping`, { + const response = await request.get(`${BASE_URL}${CONTEXT_PATH}/info/ping`, { headers: { "X-OpenIDM-Username": ADMIN_USER, "X-OpenIDM-Password": ADMIN_PASS, @@ -131,7 +132,7 @@ test.describe("OpenIDM UI Smoke Tests", () => { }); test("REST API config endpoint is accessible", async ({ request }) => { - const response = await request.get(`${BASE_URL}/openidm/config/ui/configuration`, { + const response = await request.get(`${BASE_URL}${CONTEXT_PATH}/config/ui/configuration`, { headers: { "X-OpenIDM-Username": ADMIN_USER, "X-OpenIDM-Password": ADMIN_PASS, diff --git a/openidm-api-servlet/src/main/java/org/forgerock/openidm/servlet/internal/ServletComponent.java b/openidm-api-servlet/src/main/java/org/forgerock/openidm/servlet/internal/ServletComponent.java index 85c59358ec..7fab4807ac 100644 --- a/openidm-api-servlet/src/main/java/org/forgerock/openidm/servlet/internal/ServletComponent.java +++ b/openidm-api-servlet/src/main/java/org/forgerock/openidm/servlet/internal/ServletComponent.java @@ -12,7 +12,7 @@ * information: "Portions copyright [year] [name of copyright owner]". * * Copyright 2013-2016 ForgeRock AS. - * Portions Copyrighted 2024-2025 3A Systems LLC. + * Portions Copyrighted 2024-2026 3A Systems LLC. */ package org.forgerock.openidm.servlet.internal; @@ -74,7 +74,7 @@ /** * A component to create and register the "API" Servlet; that is, the CHF Servlet that * - * 1) listens on /openidm, + * 1) listens on /openidm (or the path configured via openidm.context.path system property), * 2) dispatches to the HttpApplication, that is composed of * a) the auth filter * b) the JSON resource HTTP Handler, that @@ -93,7 +93,11 @@ public class ServletComponent implements EventHandler { static final String PID = "org.forgerock.openidm.api-servlet"; - private static final String SERVLET_ALIAS = "/openidm"; + /** System property name for the configurable REST context path. */ + static final String OPENIDM_CONTEXT_PATH_PROPERTY = ServerConstants.OPENIDM_CONTEXT_PATH_PROPERTY; + + /** Default REST context path. */ + static final String OPENIDM_CONTEXT_PATH_DEFAULT = ServerConstants.OPENIDM_CONTEXT_PATH_DEFAULT; private static final String API_ID = "frapi:openidm"; @@ -155,9 +159,19 @@ protected synchronized void unbindRegistrator(ServletFilterRegistrator registrat private HttpServlet servlet; + /** + * Returns the servlet alias (REST context path) from the system property + * {@code openidm.context.path}, defaulting to {@code /openidm}. + */ + static String getServletAlias() { + return ServerConstants.normalizeContextPath( + System.getProperty(OPENIDM_CONTEXT_PATH_PROPERTY, OPENIDM_CONTEXT_PATH_DEFAULT)); + } + @Activate protected void activate(ComponentContext context) throws ServletException, NamespaceException { - logger.debug("Registering servlet at {}", SERVLET_ALIAS); + final String servletAlias = getServletAlias(); + logger.debug("Registering servlet at {}", servletAlias); final Handler handler = CrestHttp.newHttpHandler( new CrestApplication() { @@ -201,8 +215,8 @@ public void stop() { @SuppressWarnings("rawtypes") final Dictionary params = new Hashtable(); - servletRegistration.registerServlet(SERVLET_ALIAS, servlet, params); - logger.info("Registered servlet at {}", SERVLET_ALIAS); + servletRegistration.registerServlet(servletAlias, servlet, params); + logger.info("Registered servlet at {}", servletAlias); } @Deactivate diff --git a/openidm-api-servlet/src/test/java/org/forgerock/openidm/servlet/internal/ServletComponentTest.java b/openidm-api-servlet/src/test/java/org/forgerock/openidm/servlet/internal/ServletComponentTest.java new file mode 100644 index 0000000000..c44266a5b7 --- /dev/null +++ b/openidm-api-servlet/src/test/java/org/forgerock/openidm/servlet/internal/ServletComponentTest.java @@ -0,0 +1,67 @@ +/* + * The contents of this file are subject to the terms of the Common Development and + * Distribution License (the License). You may not use this file except in compliance with the + * License. + * + * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + * specific language governing permission and limitations under the License. + * + * When distributing Covered Software, include this CDDL Header Notice in each file and include + * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + * Header, with the fields enclosed by brackets [] replaced by your own identifying + * information: "Portions copyright [year] [name of copyright owner]". + * + * Copyright 2025-2026 3A Systems LLC. + */ + +package org.forgerock.openidm.servlet.internal; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.testng.annotations.AfterMethod; +import org.testng.annotations.Test; + +/** + * Unit tests for {@link ServletComponent} context path configuration. + */ +public class ServletComponentTest { + + @AfterMethod + public void clearSystemProperty() { + System.clearProperty(ServletComponent.OPENIDM_CONTEXT_PATH_PROPERTY); + } + + @Test + public void testDefaultServletAlias() { + // When no system property is set, should return the default /openidm + System.clearProperty(ServletComponent.OPENIDM_CONTEXT_PATH_PROPERTY); + assertThat(ServletComponent.getServletAlias()).isEqualTo("/openidm"); + } + + @Test + public void testCustomServletAlias() { + // When system property is set to /myidm, should return /myidm + System.setProperty(ServletComponent.OPENIDM_CONTEXT_PATH_PROPERTY, "/myidm"); + assertThat(ServletComponent.getServletAlias()).isEqualTo("/myidm"); + } + + @Test + public void testServletAliasWithoutLeadingSlash() { + // Should add leading slash if missing + System.setProperty(ServletComponent.OPENIDM_CONTEXT_PATH_PROPERTY, "myidm"); + assertThat(ServletComponent.getServletAlias()).isEqualTo("/myidm"); + } + + @Test + public void testServletAliasWithTrailingSlash() { + // Should remove trailing slash + System.setProperty(ServletComponent.OPENIDM_CONTEXT_PATH_PROPERTY, "/myidm/"); + assertThat(ServletComponent.getServletAlias()).isEqualTo("/myidm"); + } + + @Test + public void testServletAliasConstants() { + assertThat(ServletComponent.OPENIDM_CONTEXT_PATH_PROPERTY).isEqualTo("openidm.context.path"); + assertThat(ServletComponent.OPENIDM_CONTEXT_PATH_DEFAULT).isEqualTo("/openidm"); + } +} diff --git a/openidm-doc/src/main/asciidoc/integrators-guide/appendix-rest.adoc b/openidm-doc/src/main/asciidoc/integrators-guide/appendix-rest.adoc index 9a4eaf7aa3..7d1e3f0748 100644 --- a/openidm-doc/src/main/asciidoc/integrators-guide/appendix-rest.adoc +++ b/openidm-doc/src/main/asciidoc/integrators-guide/appendix-rest.adoc @@ -12,7 +12,7 @@ information: "Portions copyright [year] [name of copyright owner]". Copyright 2017 ForgeRock AS. - Portions Copyright 2024-2025 3A Systems LLC. + Portions Copyright 2024-2026 3A Systems LLC. //// :figure-caption!: @@ -82,6 +82,11 @@ Note that for LDAP resources, you should not map the LDAP `dn` to the OpenIDM `u ... ---- +[NOTE] +==== +The `/openidm` context path shown in all URI examples throughout this guide is the default value. You can change it by setting the `openidm.context.path` system property in `conf/system.properties` or as a JVM argument (for example, `-Dopenidm.context.path=/myidm`). For more information, see xref:chap-configuration.adoc#configuring-rest-context-path["Configuring the REST Context Path"] in the __Integrator's Guide__. +==== + [#rest-object-identifier] === Object Identifiers diff --git a/openidm-doc/src/main/asciidoc/integrators-guide/chap-configuration.adoc b/openidm-doc/src/main/asciidoc/integrators-guide/chap-configuration.adoc index 8ded4b15ec..9de8a2d7d2 100644 --- a/openidm-doc/src/main/asciidoc/integrators-guide/chap-configuration.adoc +++ b/openidm-doc/src/main/asciidoc/integrators-guide/chap-configuration.adoc @@ -12,7 +12,7 @@ information: "Portions copyright [year] [name of copyright owner]". Copyright 2017 ForgeRock AS. - Portions Copyright 2024-2025 3A Systems LLC. + Portions Copyright 2024-2026 3A Systems LLC. //// :figure-caption!: @@ -152,6 +152,37 @@ felix.fileinstall.enableConfigSave=false ---- +[#configuring-rest-context-path] +==== Configuring the REST Context Path + +By default, the OpenIDM REST API is available under the `/openidm` context path (for example, `\https://localhost:8443/openidm/`). You can change this base path by setting the `openidm.context.path` system property. + +To set a custom REST context path, edit the `conf/system.properties` file and uncomment or add the following line, replacing `/openidm` with your preferred path: + +[source] +---- +openidm.context.path=/openidm +---- + +Alternatively, you can pass the property as a JVM argument when starting OpenIDM: + +[source, console] +---- +$ OPENIDM_OPTS="-Dopenidm.context.path=/myidm" ./startup.sh +---- + +The path must begin with a `/` and must not end with `/`. If the value provided does not start with a `/`, one is added automatically. + +After changing this property, the REST API will be accessible under the new path, for example `\https://localhost:8443/myidm/config`. When the Admin UI or Self-Service UI is served by OpenIDM, `ResourceServlet` injects the effective context path into `index.html` at runtime as `window.__openidm_context_path`. The UI reads that injected value for its REST calls, so in this deployment model the UIs automatically follow a custom `openidm.context.path` without requiring a reverse proxy or a rebuild. + +If you host the UI separately instead of serving it through OpenIDM, this runtime injection does not occur. In that case, you must provide equivalent runtime configuration for the UI, or use another deployment mechanism such as a reverse proxy or a custom UI build that targets the desired context path. + +[NOTE] +==== +Changing the context path affects all REST API endpoints. If you expose the Admin UI or Self-Service UI under a custom path, ensure that any external integrations, load balancer rules, or documentation referring to the `/openidm` path are updated accordingly. +==== + + [#configuring-proxy] ==== Communicating Through a Proxy Server diff --git a/openidm-servlet-registrator/pom.xml b/openidm-servlet-registrator/pom.xml index f2089487b2..e6a77a8924 100644 --- a/openidm-servlet-registrator/pom.xml +++ b/openidm-servlet-registrator/pom.xml @@ -22,7 +22,7 @@ ~ your own identifying information: ~ "Portions Copyrighted [year] [name of copyright owner]" ~ - ~ Portions Copyrighted 2024-2025 3A Systems LLC. + ~ Portions Copyrighted 2024-2026 3A Systems LLC. --> 4.0.0 @@ -37,6 +37,11 @@ This bundle is duplicates of OpenIDM HTTP context bundle + + org.openidentityplatform.openidm + openidm-system + ${project.version} + org.openidentityplatform.openidm openidm-enhanced-config diff --git a/openidm-servlet-registrator/src/main/java/org/forgerock/openidm/servletregistration/impl/ServletRegistrationSingleton.java b/openidm-servlet-registrator/src/main/java/org/forgerock/openidm/servletregistration/impl/ServletRegistrationSingleton.java index 345ed61151..6fcdecc6f0 100644 --- a/openidm-servlet-registrator/src/main/java/org/forgerock/openidm/servletregistration/impl/ServletRegistrationSingleton.java +++ b/openidm-servlet-registrator/src/main/java/org/forgerock/openidm/servletregistration/impl/ServletRegistrationSingleton.java @@ -21,7 +21,7 @@ * your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * - * Portions Copyrighted 2024-2025 3A Systems LLC. + * Portions Copyrighted 2024-2026 3A Systems LLC. */ package org.forgerock.openidm.servletregistration.impl; @@ -55,6 +55,7 @@ import org.forgerock.openidm.servletregistration.RegisteredFilter; import org.forgerock.openidm.servletregistration.ServletRegistration; import org.forgerock.openidm.servletregistration.ServletFilterRegistrator; +import org.forgerock.openidm.core.ServerConstants; import org.forgerock.util.Function; import org.ops4j.pax.web.service.WebContainer; import org.osgi.framework.BundleContext; @@ -90,7 +91,21 @@ public class ServletRegistrationSingleton implements ServletRegistration { private static final String[] DEFAULT_SERVLET_NAME = new String[] { "OpenIDM REST" }; - private static final String[] DEFAULT_SERVLET_URL_PATTERNS = new String[] { "/openidm/*", "/selfservice/*" }; + /** System property name for the configurable REST context path. */ + private static final String OPENIDM_CONTEXT_PATH_PROPERTY = ServerConstants.OPENIDM_CONTEXT_PATH_PROPERTY; + + /** Default REST context path. */ + private static final String OPENIDM_CONTEXT_PATH_DEFAULT = ServerConstants.OPENIDM_CONTEXT_PATH_DEFAULT; + + /** + * Returns the default servlet URL patterns, using the configured context path + * from the {@code openidm.context.path} system property (default: {@code /openidm}). + */ + private static String[] getDefaultServletUrlPatterns() { + String contextPath = ServerConstants.normalizeContextPath( + System.getProperty(OPENIDM_CONTEXT_PATH_PROPERTY, OPENIDM_CONTEXT_PATH_DEFAULT)); + return new String[] { contextPath + "/*", "/selfservice/*" }; + } // Context of this scr component private BundleContext bundleContext; @@ -212,7 +227,7 @@ public URL apply(JsonValue jsonValue) throws JsonValueException { // URL patterns to apply the filter to, e.g. one could also add "/openidmui/*"); List urlPatterns = config.get(SERVLET_FILTER_URL_PATTERNS) - .defaultTo(Arrays.asList(DEFAULT_SERVLET_URL_PATTERNS)) + .defaultTo(Arrays.asList(getDefaultServletUrlPatterns())) .asList(String.class); // Filter init params, a string to string map 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..0b1bf0763d 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,9 +25,11 @@ 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; +import java.util.regex.Matcher; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServlet; @@ -38,6 +40,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 +132,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 +161,55 @@ 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"); + + // Normalize: leading '/', no trailing '/', then strip leading '/' for the JS value. + String contextPath = ServerConstants.normalizeContextPath(System.getProperty( + ServerConstants.OPENIDM_CONTEXT_PATH_PROPERTY, + ServerConstants.OPENIDM_CONTEXT_PATH_DEFAULT)); + // Strip leading slash — the UI Constants.context value does not include it + String contextValue = contextPath.substring(1); + + 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. + // Escape characters that could break out of the JS string or the script tag. + // Use proper JS-string escaping only (no HTML entities inside a JS string). + // Backslash must be escaped first to avoid double-escaping subsequent sequences. + String safeContextValue = contextValue + .replace("\\", "\\\\") + .replace("\"", "\\\"") + .replace("<", "\\u003c") + .replace(">", "\\u003e"); + String injection = "\n"; + // Use Matcher.quoteReplacement to avoid treating '$' or '\' in injection specially. + html = html.replaceFirst("", Matcher.quoteReplacement(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-shell/src/main/java/org/forgerock/openidm/shell/impl/RemoteCommandScope.java b/openidm-shell/src/main/java/org/forgerock/openidm/shell/impl/RemoteCommandScope.java index 3baf5aa6b2..dd484947bd 100644 --- a/openidm-shell/src/main/java/org/forgerock/openidm/shell/impl/RemoteCommandScope.java +++ b/openidm-shell/src/main/java/org/forgerock/openidm/shell/impl/RemoteCommandScope.java @@ -12,6 +12,7 @@ * information: "Portions copyright [year] [name of copyright owner]". * * Copyright 2011-2016 ForgeRock AS. + * Portions copyright 2026 3A Systems LLC. */ package org.forgerock.openidm.shell.impl; @@ -43,6 +44,7 @@ import org.forgerock.json.resource.UpdateRequest; import org.forgerock.openidm.config.persistence.ConfigBootstrapHelper; import org.forgerock.openidm.core.IdentityServer; +import org.forgerock.openidm.core.ServerConstants; import org.forgerock.openidm.shell.CustomCommandScope; import org.forgerock.openidm.shell.felixgogo.MetaVar; import org.forgerock.services.context.RootContext; @@ -57,10 +59,27 @@ public class RemoteCommandScope extends CustomCommandScope { private static final String IDM_PORT_DESC = "Port of OpenIDM REST service. This will override any port in --url."; private static final String IDM_PORT_METAVAR = "PORT"; + /** Compile-time default URL used in CLI parameter annotations. */ private static final String IDM_URL_DEFAULT = "http://localhost:8080/openidm/"; - private static final String IDM_URL_DESC = "URL of OpenIDM REST service. Default " + IDM_URL_DEFAULT; + + private static final String IDM_URL_DESC = + "URL of OpenIDM REST service. Default: http://localhost:8080 plus the effective " + + "context path (derived from -Dopenidm.context.path; default context path: " + + "/openidm)."; private static final String IDM_URL_METAVAR = "URL"; + /** + * Returns the effective default IDM URL, reading the {@code openidm.context.path} system + * property (default: {@code /openidm}) to construct the URL. + * This is only called when no {@code --url} argument was explicitly provided. + */ + private static String getEffectiveIdmUrl() { + String contextPath = ServerConstants.normalizeContextPath( + System.getProperty(ServerConstants.OPENIDM_CONTEXT_PATH_PROPERTY, + ServerConstants.OPENIDM_CONTEXT_PATH_DEFAULT)); + return "http://localhost:8080" + contextPath + "/"; + } + private static final String USER_PASS_DESC = "Server user and password"; private static final String USER_PASS_METAVAR = "USER[:PASSWORD]"; private static final String USER_PASS_DEFAULT = ""; @@ -139,14 +158,17 @@ private static String getPassword(final String userPass) { /** * Gets OpenIDM URL configuration parameter. * - * @param url OpenIDM URL + * @param url OpenIDM URL explicitly provided by the user, or blank if not provided * @return URL */ private static String getUrl(final String url) { if (isNotBlank(url)) { + // User explicitly provided --url; use it as-is (just normalize trailing slash). + // The openidm.context.path system property does NOT override an explicit --url. return url.endsWith("/") ? url : url + "/"; } - throw new IllegalArgumentException("URL required"); + // --url was not provided; derive the default URL from the openidm.context.path system property + return getEffectiveIdmUrl(); } /** @@ -185,7 +207,7 @@ public void update(CommandSession session, @Descriptor(IDM_URL_DESC) @MetaVar(IDM_URL_METAVAR) - @Parameter(names = {"--url"}, absentValue = IDM_URL_DEFAULT) + @Parameter(names = {"--url"}, absentValue = "") final String idmUrl, @Descriptor(IDM_PORT_DESC) @@ -271,7 +293,7 @@ public void configimport( @Descriptor(IDM_URL_DESC) @MetaVar(IDM_URL_METAVAR) - @Parameter(names = { "--url" }, absentValue = IDM_URL_DEFAULT) + @Parameter(names = { "--url" }, absentValue = "") final String idmUrl, @Descriptor(IDM_PORT_DESC) @@ -314,7 +336,7 @@ public void configimport( @Descriptor(IDM_URL_DESC) @MetaVar(IDM_URL_METAVAR) - @Parameter(names = { "--url" }, absentValue = IDM_URL_DEFAULT) + @Parameter(names = { "--url" }, absentValue = "") final String idmUrl, @Descriptor(IDM_PORT_DESC) @@ -488,7 +510,7 @@ public void configexport( @Descriptor(IDM_URL_DESC) @MetaVar(IDM_URL_METAVAR) - @Parameter(names = { "--url" }, absentValue = IDM_URL_DEFAULT) + @Parameter(names = { "--url" }, absentValue = "") final String idmUrl, @Descriptor(IDM_PORT_DESC) @@ -519,7 +541,7 @@ public void configexport( @Descriptor(IDM_URL_DESC) @MetaVar(IDM_URL_METAVAR) - @Parameter(names = { "--url" }, absentValue = IDM_URL_DEFAULT) + @Parameter(names = { "--url" }, absentValue = "") final String idmUrl, @Descriptor(IDM_PORT_DESC) @@ -592,7 +614,7 @@ public void configureconnector( @Descriptor(IDM_URL_DESC) @MetaVar(IDM_URL_METAVAR) - @Parameter(names = { "--url" }, absentValue = IDM_URL_DEFAULT) + @Parameter(names = { "--url" }, absentValue = "") final String idmUrl, @Descriptor(IDM_PORT_DESC) diff --git a/openidm-system/src/main/java/org/forgerock/openidm/core/ServerConstants.java b/openidm-system/src/main/java/org/forgerock/openidm/core/ServerConstants.java index f03d65169e..e5a1904b26 100644 --- a/openidm-system/src/main/java/org/forgerock/openidm/core/ServerConstants.java +++ b/openidm-system/src/main/java/org/forgerock/openidm/core/ServerConstants.java @@ -24,6 +24,7 @@ * * Copyright 2006-2010 Sun Microsystems, Inc. * Portions Copyright 2010-2016 ForgeRock AS + * Portions copyright 2026 3A Systems LLC. */ package org.forgerock.openidm.core; @@ -274,4 +275,36 @@ public static String getDisplayVersion() { public static final String LAUNCHER_WORKING_URL = "launcher.working.url"; public static final String LAUNCHER_PROJECT_LOCATION = "launcher.project.location"; public static final String LAUNCHER_PROJECT_URL = "launcher.project.url"; + + /** + * The name of the system property that specifies the REST context path. + * For example, setting {@code -Dopenidm.context.path=/myidm} will make the REST API + * available under {@code /myidm} instead of the default {@code /openidm}. + */ + public static final String OPENIDM_CONTEXT_PATH_PROPERTY = "openidm.context.path"; + + /** + * The default REST context path used when {@link #OPENIDM_CONTEXT_PATH_PROPERTY} is not set. + */ + public static final String OPENIDM_CONTEXT_PATH_DEFAULT = "/openidm"; + + /** + * Normalizes a REST context path value so that it always starts with {@code /} and never + * ends with {@code /} (unless the path is exactly {@code /}). + *

+ * For example: {@code "openidm"} → {@code "/openidm"}, + * {@code "/myidm/"} → {@code "/myidm"}. + * + * @param path the raw context path value (from a system property, config, etc.) + * @return the normalized path + */ + public static String normalizeContextPath(String path) { + if (!path.startsWith("/")) { + path = "/" + path; + } + if (path.endsWith("/") && path.length() > 1) { + path = path.substring(0, path.length() - 1); + } + return path; + } } diff --git a/openidm-ui/openidm-ui-admin/src/main/js/config/validators/AdminValidators.js b/openidm-ui/openidm-ui-admin/src/main/js/config/validators/AdminValidators.js index 23ef4bc269..7e775b1b01 100644 --- a/openidm-ui/openidm-ui-admin/src/main/js/config/validators/AdminValidators.js +++ b/openidm-ui/openidm-ui-admin/src/main/js/config/validators/AdminValidators.js @@ -12,12 +12,14 @@ * information: "Portions copyright [year] [name of copyright owner]". * * Copyright 2015-2016 ForgeRock AS. + * Portions copyright 2026 3A Systems LLC. */ define([ "jquery", - "underscore" -], function ($, _) { + "underscore", + "org/forgerock/commons/ui/common/util/Constants" +], function ($, _, constants) { var obj = { "changed": { "name": "Changed field", @@ -39,8 +41,8 @@ define([ return; } - if (v === "/openidm" || v === "/admin" || v === "/system") { - callback(["The URL cannot be one of the following reserved names: \"openidm\", \"admin\" or \"system\"."]); + if (v === "/" + constants.context || v === "/admin" || v === "/system") { + callback(["The URL cannot be one of the following reserved names: \"" + constants.context + "\", \"admin\" or \"system\"."]); return; } diff --git a/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/delegates/AuditDelegate.js b/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/delegates/AuditDelegate.js index 854fdfc5d4..1f7fb2f75f 100644 --- a/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/delegates/AuditDelegate.js +++ b/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/delegates/AuditDelegate.js @@ -12,6 +12,7 @@ * information: "Portions copyright [year] [name of copyright owner]". * * Copyright 2015-2016 ForgeRock AS. + * Portions copyright 2026 3A Systems LLC. */ define([ @@ -20,7 +21,7 @@ define([ "org/forgerock/commons/ui/common/main/AbstractDelegate" ], function($, constants, AbstractDelegate) { - var obj = new AbstractDelegate(constants.host + "/openidm/audit/"); + var obj = new AbstractDelegate(constants.host + "/" + constants.context + "/audit/"); obj.availableHandlers = function() { return obj.serviceCall({ diff --git a/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/delegates/ClusterDelegate.js b/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/delegates/ClusterDelegate.js index 4add69f408..bdb00a9b9e 100644 --- a/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/delegates/ClusterDelegate.js +++ b/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/delegates/ClusterDelegate.js @@ -12,6 +12,7 @@ * information: "Portions copyright [year] [name of copyright owner]". * * Copyright 2015-2016 ForgeRock AS. + * Portions copyright 2026 3A Systems LLC. */ define([ @@ -20,7 +21,7 @@ define([ "org/forgerock/commons/ui/common/main/AbstractDelegate" ], function($, constants, AbstractDelegate) { - var obj = new AbstractDelegate(constants.host + "/openidm"); + var obj = new AbstractDelegate(constants.host + "/" + constants.context); obj.getNodes = function() { return obj.serviceCall({ diff --git a/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/delegates/ConnectorDelegate.js b/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/delegates/ConnectorDelegate.js index 74de9cc5e9..6c81086f09 100644 --- a/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/delegates/ConnectorDelegate.js +++ b/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/delegates/ConnectorDelegate.js @@ -12,6 +12,7 @@ * information: "Portions copyright [year] [name of copyright owner]". * * Copyright 2014-2016 ForgeRock AS. + * Portions copyright 2026 3A Systems LLC. */ define([ @@ -22,7 +23,7 @@ define([ "org/forgerock/commons/ui/common/main/EventManager" ], function($, _, constants, AbstractDelegate, eventManager) { - var obj = new AbstractDelegate(constants.host + "/openidm/system"); + var obj = new AbstractDelegate(constants.host + "/" + constants.context + "/system"); obj.connectorDelegateCache = {}; diff --git a/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/delegates/ExternalAccessDelegate.js b/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/delegates/ExternalAccessDelegate.js index ef5dca25e2..7aa65671fb 100644 --- a/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/delegates/ExternalAccessDelegate.js +++ b/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/delegates/ExternalAccessDelegate.js @@ -12,6 +12,7 @@ * information: "Portions copyright [year] [name of copyright owner]". * * Copyright 2014-2016 ForgeRock AS. + * Portions copyright 2026 3A Systems LLC. */ define([ @@ -19,7 +20,7 @@ define([ "org/forgerock/commons/ui/common/main/AbstractDelegate" ], function(constants, AbstractDelegate) { - var obj = new AbstractDelegate(constants.host + "/openidm/endpoint/oauthproxy"); + var obj = new AbstractDelegate(constants.host + "/" + constants.context + "/endpoint/oauthproxy"); obj.getToken = function(id, authCode, redirectUri, tokenUrl, connectorLocation) { var googleDetails = "grant_type=authorization_code&code=" +authCode +"&client_id=" +id +"&redirect_uri=" +redirectUri, @@ -48,7 +49,7 @@ define([ obj.externalRestRequest = (url, method, body, headers) => { method = method || "GET"; return obj.serviceCall({ - serviceUrl: constants.host + "/openidm/external/rest", + serviceUrl: constants.host + "/" + constants.context + "/external/rest", url: "?_action=call", type: "POST", data: JSON.stringify({ diff --git a/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/delegates/MaintenanceDelegate.js b/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/delegates/MaintenanceDelegate.js index 09e542c215..715f6812cf 100644 --- a/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/delegates/MaintenanceDelegate.js +++ b/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/delegates/MaintenanceDelegate.js @@ -12,6 +12,7 @@ * information: "Portions copyright [year] [name of copyright owner]". * * Copyright 2015-2016 ForgeRock AS. + * Portions copyright 2026 3A Systems LLC. */ define([ @@ -21,7 +22,7 @@ define([ "org/forgerock/commons/ui/common/main/AbstractDelegate" ], function($, _, constants, AbstractDelegate) { - var obj = new AbstractDelegate(constants.host + "/openidm/maintenance"); + var obj = new AbstractDelegate(constants.host + "/" + constants.context + "/maintenance"); obj.getStatus = function () { return obj.serviceCall({ diff --git a/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/delegates/ReconDelegate.js b/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/delegates/ReconDelegate.js index fcc08fa8b9..1e883e5a6d 100644 --- a/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/delegates/ReconDelegate.js +++ b/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/delegates/ReconDelegate.js @@ -12,6 +12,7 @@ * information: "Portions copyright [year] [name of copyright owner]". * * Copyright 2011-2016 ForgeRock AS. + * Portions copyright 2026 3A Systems LLC. */ define([ @@ -25,7 +26,7 @@ define([ "org/forgerock/commons/ui/common/main/Router" ], function($, _, constants, AbstractDelegate, configuration, eventManager, spinner, router) { - var obj = new AbstractDelegate(constants.host + "/openidm/recon"); + var obj = new AbstractDelegate(constants.host + "/" + constants.context + "/recon"); obj.waitForAll = function (reconIds, suppressSpinner, progressCallback, delayTime) { var resultPromise = $.Deferred(), @@ -149,7 +150,7 @@ define([ obj.stopRecon = function (id, suppressSpinner) { return obj.serviceCall({ "suppressSpinner": suppressSpinner, - "serviceUrl": "/openidm/recon/" + id, + "serviceUrl": "/" + constants.context + "/recon/" + id, "url": "?_action=cancel", "type": "POST" }); @@ -163,7 +164,7 @@ define([ getTargetObj = _.bind(function(link){ return this.serviceCall({ "type": "GET", - "serviceUrl": "/openidm/" + link.targetObjectId, + "serviceUrl": "/" + constants.context + "/" + link.targetObjectId, "url": "" }).then(function(targetObject){ newLinks.push({ sourceObjectId: link.sourceObjectId , targetObject: targetObject }); @@ -181,7 +182,7 @@ define([ } else { this.serviceCall({ "type": "GET", - "serviceUrl": "/openidm/audit/recon", + "serviceUrl": "/" + constants.context + "/audit/recon", "url": "?_queryFilter=" + encodeURIComponent(queryFilter) }).then(function(qry){ if(qry.result.length){ @@ -205,7 +206,7 @@ define([ var queryFilter = 'reconId eq "' + reconId + '" and ' + objectIdType + ' eq "' + objectId + '"'; return obj.serviceCall({ "type": "GET", - "serviceUrl": "/openidm/audit/recon", + "serviceUrl": "/" + constants.context + "/audit/recon", "url": "?_queryFilter=" + encodeURIComponent(queryFilter) }); }; diff --git a/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/delegates/SchedulerDelegate.js b/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/delegates/SchedulerDelegate.js index c3198ec872..9cdd06c654 100644 --- a/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/delegates/SchedulerDelegate.js +++ b/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/delegates/SchedulerDelegate.js @@ -12,6 +12,7 @@ * information: "Portions copyright [year] [name of copyright owner]". * * Copyright 2014-2016 ForgeRock AS. + * Portions copyright 2026 3A Systems LLC. */ define([ @@ -21,7 +22,7 @@ define([ "org/forgerock/commons/ui/common/main/AbstractDelegate" ], function(_, $, constants, AbstractDelegate) { - var obj = new AbstractDelegate(constants.host + "/openidm/scheduler/job"); + var obj = new AbstractDelegate(constants.host + "/" + constants.context + "/scheduler/job"); obj.availableSchedules = function() { return obj.serviceCall({ diff --git a/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/delegates/ScriptDelegate.js b/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/delegates/ScriptDelegate.js index a41be33b44..7a8b71e952 100644 --- a/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/delegates/ScriptDelegate.js +++ b/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/delegates/ScriptDelegate.js @@ -12,6 +12,7 @@ * information: "Portions copyright [year] [name of copyright owner]". * * Copyright 2015-2016 ForgeRock AS. + * Portions copyright 2026 3A Systems LLC. */ define([ @@ -20,7 +21,7 @@ define([ "org/forgerock/commons/ui/common/main/AbstractDelegate" ], function(_, constants, AbstractDelegate) { - var obj = new AbstractDelegate(constants.host + "/openidm/script"); + var obj = new AbstractDelegate(constants.host + "/" + constants.context + "/script"); obj.evalScript = function(script, additionalGlobals) { var scriptDetails = _.cloneDeep(script); diff --git a/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/delegates/SecurityDelegate.js b/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/delegates/SecurityDelegate.js index 0a663f0fc1..ca622288cf 100644 --- a/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/delegates/SecurityDelegate.js +++ b/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/delegates/SecurityDelegate.js @@ -12,6 +12,7 @@ * information: "Portions copyright [year] [name of copyright owner]". * * Copyright 2014-2016 ForgeRock AS. + * Portions copyright 2026 3A Systems LLC. */ define([ @@ -20,7 +21,7 @@ define([ "org/forgerock/commons/ui/common/main/AbstractDelegate" ], function($, constants, AbstractDelegate) { - var obj = new AbstractDelegate(constants.host + "/openidm/security"); + var obj = new AbstractDelegate(constants.host + "/" + constants.context + "/security"); obj.getPublicKeyCert = function (storeType, alias) { var promise = $.Deferred(); diff --git a/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/delegates/SyncDelegate.js b/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/delegates/SyncDelegate.js index 41a2016b67..ceef5fabdd 100644 --- a/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/delegates/SyncDelegate.js +++ b/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/delegates/SyncDelegate.js @@ -12,6 +12,7 @@ * information: "Portions copyright [year] [name of copyright owner]". * * Copyright 2014-2016 ForgeRock AS. + * Portions copyright 2026 3A Systems LLC. */ /* eslint no-eval: 0 */ @@ -26,7 +27,7 @@ define([ "org/forgerock/openidm/ui/common/delegates/ConfigDelegate" ], function($, _, constants, AbstractDelegate, configuration, eventManager, configDelegate) { - var obj = new AbstractDelegate(constants.host + "/openidm/sync"); + var obj = new AbstractDelegate(constants.host + "/" + constants.context + "/sync"); obj.performAction = function (reconId, mapping, action, sourceId, targetId, linkType) { var params = { @@ -69,13 +70,13 @@ define([ } else { return obj.serviceCall({ - "serviceUrl": constants.host + "/openidm/repo/link", + "serviceUrl": constants.host + "/" + constants.context + "/repo/link", "url": "?_queryId=links-for-" + ordinal + "&linkType=" + linkType + "&" + ordinal + "=" + encodeURIComponent(id) }).then(function (qry) { var i, deletePromises = []; for (i=0;i { return resourceDelegate.serviceCall({ "type": "POST", - "serviceUrl": "/openidm/repo/links", + "serviceUrl": "/" + constants.context + "/repo/links", "url": "?_action=command&commandId=delete-mapping-links&mapping=" + mappingName }); }); diff --git a/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/user/EditUserView.js b/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/user/EditUserView.js index 45becb35d8..9261afffa2 100644 --- a/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/user/EditUserView.js +++ b/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/user/EditUserView.js @@ -12,6 +12,7 @@ * information: "Portions copyright [year] [name of copyright owner]". * * Copyright 2015-2016 ForgeRock AS. + * Portions copyright 2026 3A Systems LLC. */ define([ @@ -69,7 +70,7 @@ function ($, _, if ($(e.currentTarget).attr("disabled") !== "disabled") { ResourceDelegate.serviceCall({ - serviceUrl: "/openidm/managed/user", + serviceUrl: "/" + constants.context + "/managed/user", url: "/" + this.objectId + "?_action=resetPassword", type: "POST", success: (e) => { diff --git a/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/workflow/ActiveProcessesView.js b/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/workflow/ActiveProcessesView.js index b5121dc42c..e75a7811eb 100644 --- a/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/workflow/ActiveProcessesView.js +++ b/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/workflow/ActiveProcessesView.js @@ -12,6 +12,7 @@ * information: "Portions copyright [year] [name of copyright owner]". * * Copyright 2015-2016 ForgeRock AS. + * Portions copyright 2026 3A Systems LLC. */ define([ @@ -53,7 +54,7 @@ define([ this.parentRender(_.bind(function() { var processGrid, - ProcessModel = AbstractModel.extend({ "url": "/openidm/workflow/processinstance" }), + ProcessModel = AbstractModel.extend({ "url": "/" + constants.context + "/workflow/processinstance" }), Process = AbstractCollection.extend({ model: ProcessModel }); this.model.processes = new Process(); @@ -69,7 +70,7 @@ define([ }); }); - this.model.processes.url = "/openidm/workflow/processinstance?_queryId=filtered-query"; + this.model.processes.url = "/" + constants.context + "/workflow/processinstance?_queryId=filtered-query"; this.model.processes.state.pageSize = null; this.model.processes.state.sortKey = "-startTime"; @@ -228,9 +229,9 @@ define([ } if(filterString.length > 0) { - this.model.processes.url = "/openidm/workflow/processinstance?" + filterString; + this.model.processes.url = "/" + constants.context + "/workflow/processinstance?" + filterString; } else { - this.model.processes.url = "/openidm/workflow/processinstance?_queryId=query-all-ids"; + this.model.processes.url = "/" + constants.context + "/workflow/processinstance?_queryId=query-all-ids"; } this.model.processes.getFirstPage(); diff --git a/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/workflow/ProcessDefinitionView.js b/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/workflow/ProcessDefinitionView.js index 5ff664d3ff..a8168d4927 100644 --- a/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/workflow/ProcessDefinitionView.js +++ b/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/workflow/ProcessDefinitionView.js @@ -12,6 +12,7 @@ * information: "Portions copyright [year] [name of copyright owner]". * * Copyright 2011-2016 ForgeRock AS. + * Portions copyright 2026 3A Systems LLC. */ define([ @@ -23,7 +24,7 @@ define([ "org/forgerock/commons/ui/common/main/AbstractModel", "org/forgerock/openidm/ui/admin/util/WorkflowUtils" ], function(_, AbstractView, eventManager, constants, UIUtils, AbstractModel, WorkflowUtils) { - var ProcessModel = AbstractModel.extend({ url: "/openidm/workflow/processdefinition" }), + var ProcessModel = AbstractModel.extend({ url: "/" + constants.context + "/workflow/processdefinition" }), ProcessDefinitionView = AbstractView.extend({ template: "templates/admin/workflow/ProcessDefinitionViewTemplate.html", @@ -39,7 +40,7 @@ define([ this.data.processDefinition = this.model.toJSON(); - this.data.diagramUrl = "/openidm/workflow/processdefinition/" + this.model.id + "?_fields=/diagram&_mimeType=image/png"; + this.data.diagramUrl = "/" + constants.context + "/workflow/processdefinition/" + this.model.id + "?_fields=/diagram&_mimeType=image/png"; this.parentRender(_.bind(function(){ diff --git a/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/workflow/ProcessDefinitionsView.js b/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/workflow/ProcessDefinitionsView.js index e53fc45a28..1f87978b86 100644 --- a/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/workflow/ProcessDefinitionsView.js +++ b/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/workflow/ProcessDefinitionsView.js @@ -12,18 +12,21 @@ * information: "Portions copyright [year] [name of copyright owner]". * * Copyright 2015-2016 ForgeRock AS. + * Portions copyright 2026 3A Systems LLC. */ define([ "jquery", "underscore", "org/forgerock/openidm/ui/admin/util/AdminAbstractView", + "org/forgerock/commons/ui/common/util/Constants", "org/forgerock/commons/ui/common/main/AbstractModel", "org/forgerock/commons/ui/common/main/AbstractCollection", "backgrid", "org/forgerock/openidm/ui/admin/util/BackgridUtils" ], function($, _, AdminAbstractView, + constants, AbstractModel, AbstractCollection, Backgrid, @@ -40,11 +43,11 @@ define([ this.parentRender(_.bind(function(){ this.parentRender(_.bind(function() { var processDefinitionGrid, - ProcessDefinitionModel = AbstractModel.extend({ "url": "/openidm/workflow/processdefinition" }), + ProcessDefinitionModel = AbstractModel.extend({ "url": "/" + constants.context + "/workflow/processdefinition" }), Process = AbstractCollection.extend({ model: ProcessDefinitionModel }); this.model.processes = new Process(); - this.model.processes.url = "/openidm/workflow/processdefinition?_queryId=filtered-query"; + this.model.processes.url = "/" + constants.context + "/workflow/processdefinition?_queryId=filtered-query"; this.model.processes.state.pageSize = null; processDefinitionGrid = new Backgrid.Grid({ diff --git a/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/workflow/ProcessHistoryView.js b/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/workflow/ProcessHistoryView.js index 4e9ef0aa91..6805dd40cd 100644 --- a/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/workflow/ProcessHistoryView.js +++ b/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/workflow/ProcessHistoryView.js @@ -12,6 +12,7 @@ * information: "Portions copyright [year] [name of copyright owner]". * * Copyright 2015-2016 ForgeRock AS. + * Portions copyright 2026 3A Systems LLC. */ define([ @@ -53,7 +54,7 @@ define([ this.parentRender(_.bind(function() { var processGrid, - ProcessModel = AbstractModel.extend({ "url": "/openidm/workflow/processinstance/history" }), + ProcessModel = AbstractModel.extend({ "url": "/" + constants.context + "/workflow/processinstance/history" }), Process = AbstractCollection.extend({ model: ProcessModel }); this.model.processes = new Process(); @@ -69,7 +70,7 @@ define([ }); }); - this.model.processes.url = "/openidm/workflow/processinstance/history?_queryId=filtered-query&finished=true"; + this.model.processes.url = "/" + constants.context + "/workflow/processinstance/history?_queryId=filtered-query&finished=true"; this.model.processes.state.pageSize = null; this.model.processes.state.sortKey = "-startTime"; @@ -221,7 +222,7 @@ define([ filterString = filterString + "&processDefinitionKey=" + this.model.processTypeFilter; } - this.model.processes.url = "/openidm/workflow/processinstance/history?" + filterString; + this.model.processes.url = "/" + constants.context + "/workflow/processinstance/history?" + filterString; this.model.processes.getFirstPage(); } diff --git a/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/workflow/ProcessInstanceView.js b/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/workflow/ProcessInstanceView.js index d96c715935..deb6ecc8a2 100644 --- a/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/workflow/ProcessInstanceView.js +++ b/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/workflow/ProcessInstanceView.js @@ -12,6 +12,7 @@ * information: "Portions copyright [year] [name of copyright owner]". * * Copyright 2011-2016 ForgeRock AS. + * Portions copyright 2026 3A Systems LLC. */ define([ @@ -39,9 +40,9 @@ define([ BackgridUtils, UIUtils) { - var ProcessInstanceModel = AbstractModel.extend({ url: "/openidm/workflow/processinstance" }), - ProcessDefinitionModel = AbstractModel.extend({ url: "/openidm/workflow/processdefinition" }), - UserModel = AbstractModel.extend({ url: "/openidm/managed/user" }), + var ProcessInstanceModel = AbstractModel.extend({ url: "/" + constants.context + "/workflow/processinstance" }), + ProcessDefinitionModel = AbstractModel.extend({ url: "/" + constants.context + "/workflow/processdefinition" }), + UserModel = AbstractModel.extend({ url: "/" + constants.context + "/managed/user" }), TaskInstanceCollection = AbstractCollection.extend({ mode: "client" }), @@ -81,7 +82,7 @@ define([ if (this.data.processInstance.startUserId) { startedBy.id = this.data.processInstance.startUserId; if (startedBy.id === 'openidm-admin') { - startedBy.url = '/openidm/repo/internal/user'; + startedBy.url = '/' + constants.context + '/repo/internal/user'; } fetchArr.push(startedBy.fetch()); } @@ -97,9 +98,9 @@ define([ if (this.data.processDefinition.processDiagramResourceName) { this.data.showDiagram = true; if (!this.model.get("endTime")) { - this.data.diagramUrl = "/openidm/workflow/processinstance/" + this.model.id + "?_fields=/diagram&_mimeType=image/png"; + this.data.diagramUrl = "/" + constants.context + "/workflow/processinstance/" + this.model.id + "?_fields=/diagram&_mimeType=image/png"; } else { - this.data.diagramUrl = "/openidm/workflow/processdefinition/" + this.data.processDefinition._id + "?_fields=/diagram&_mimeType=image/png"; + this.data.diagramUrl = "/" + constants.context + "/workflow/processdefinition/" + this.data.processDefinition._id + "?_fields=/diagram&_mimeType=image/png"; } } diff --git a/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/workflow/ProcessListView.js b/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/workflow/ProcessListView.js index 7050b1888d..1e8fdd62f3 100644 --- a/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/workflow/ProcessListView.js +++ b/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/workflow/ProcessListView.js @@ -12,17 +12,20 @@ * information: "Portions copyright [year] [name of copyright owner]". * * Copyright 2015-2016 ForgeRock AS. + * Portions copyright 2026 3A Systems LLC. */ define([ "underscore", "org/forgerock/openidm/ui/admin/util/AdminAbstractView", + "org/forgerock/commons/ui/common/util/Constants", "org/forgerock/openidm/ui/admin/workflow/ActiveProcessesView", "org/forgerock/openidm/ui/admin/workflow/ProcessDefinitionsView", "org/forgerock/openidm/ui/admin/workflow/ProcessHistoryView", "org/forgerock/commons/ui/common/main/AbstractCollection" ], function(_, AdminAbstractView, + constants, ActiveProcessesView, ProcessDefinitionsView, ProcessHistoryView, @@ -40,7 +43,7 @@ define([ this.parentRender(_.bind(function(){ this.model.processDefinitions = new AbstractCollection(); - this.model.processDefinitions.url = "/openidm/workflow/processdefinition?_queryId=filtered-query"; + this.model.processDefinitions.url = "/" + constants.context + "/workflow/processdefinition?_queryId=filtered-query"; this.model.processDefinitions.getFirstPage().then(function(processDefinitions){ processDefinition = _.chain(processDefinitions.result) diff --git a/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/workflow/TaskInstanceView.js b/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/workflow/TaskInstanceView.js index 20b1ddcf78..8dcbda2932 100644 --- a/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/workflow/TaskInstanceView.js +++ b/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/workflow/TaskInstanceView.js @@ -12,6 +12,7 @@ * information: "Portions copyright [year] [name of copyright owner]". * * Copyright 2011-2016 ForgeRock AS. + * Portions copyright 2026 3A Systems LLC. */ define([ @@ -25,9 +26,9 @@ define([ "org/forgerock/commons/ui/common/main/AbstractModel", "org/forgerock/openidm/ui/admin/util/WorkflowUtils" ], function($, _, Handlebars, AbstractView, eventManager, constants, UIUtils, AbstractModel, WorkflowUtils) { - var TaskModel = AbstractModel.extend({ url: "/openidm/workflow/taskinstance" }), - ProcessModel = AbstractModel.extend({ url: "/openidm/workflow/processdefinition" }), - UserModel = AbstractModel.extend({ url: "/openidm/managed/user" }), + var TaskModel = AbstractModel.extend({ url: "/" + constants.context + "/workflow/taskinstance" }), + ProcessModel = AbstractModel.extend({ url: "/" + constants.context + "/workflow/processdefinition" }), + UserModel = AbstractModel.extend({ url: "/" + constants.context + "/managed/user" }), TaskInstanceView = AbstractView.extend({ template: "templates/admin/workflow/TaskInstanceViewTemplate.html", diff --git a/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/workflow/TaskListView.js b/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/workflow/TaskListView.js index 3ef0b10d46..d710d5111a 100644 --- a/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/workflow/TaskListView.js +++ b/openidm-ui/openidm-ui-admin/src/main/js/org/forgerock/openidm/ui/admin/workflow/TaskListView.js @@ -12,6 +12,7 @@ * information: "Portions copyright [year] [name of copyright owner]". * * Copyright 2015-2016 ForgeRock AS. + * Portions copyright 2026 3A Systems LLC. */ define([ @@ -50,16 +51,16 @@ define([ render: function(args, callback) { this.parentRender(_.bind(function() { var tasksGrid, - TaskInstanceModel = AbstractModel.extend({ url: "/openidm/workflow/taskinstance" }), + TaskInstanceModel = AbstractModel.extend({ url: "/" + constants.context + "/workflow/taskinstance" }), TaskModel = AbstractCollection.extend({ model: TaskInstanceModel, - url: "/openidm/workflow/taskinstance?_queryId=filtered-query" + url: "/" + constants.context + "/workflow/taskinstance?_queryId=filtered-query" }), Tasks = new TaskModel(); this.model = new TaskInstanceModel(); - Tasks.url = "/openidm/workflow/taskinstance?_queryId=filtered-query"; + Tasks.url = "/" + constants.context + "/workflow/taskinstance?_queryId=filtered-query"; Tasks.setSorting("-createTime"); Tasks.state.pageSize = null; @@ -146,11 +147,11 @@ define([ preload: true, onChange: _.bind(function(value) { if(value === "anyone") { - Tasks.url = "/openidm/workflow/taskinstance?_queryId=filtered-query"; + Tasks.url = "/" + constants.context + "/workflow/taskinstance?_queryId=filtered-query"; } else if(value === "unassigned") { - Tasks.url = "/openidm/workflow/taskinstance?_queryId=filtered-query&unassigned=true"; + Tasks.url = "/" + constants.context + "/workflow/taskinstance?_queryId=filtered-query&unassigned=true"; } else { - Tasks.url = "/openidm/workflow/taskinstance?_queryId=filtered-query&assignee=" + value; + Tasks.url = "/" + constants.context + "/workflow/taskinstance?_queryId=filtered-query&assignee=" + value; } Tasks.getFirstPage(); 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..d8702819f1 100755 --- a/openidm-ui/openidm-ui-api/src/main/resources/index.html +++ b/openidm-ui/openidm-ui-api/src/main/resources/index.html @@ -13,6 +13,7 @@ information: "Portions Copyrighted [year] [name of copyright owner]". Copyright 2016 ForgeRock AS + Portions Copyrighted 2026 3A Systems LLC. --> @@ -51,7 +52,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/delegates/ConfigDelegate.js b/openidm-ui/openidm-ui-common/src/main/js/org/forgerock/openidm/ui/common/delegates/ConfigDelegate.js index 57e89eccbb..b0665ea0c8 100644 --- a/openidm-ui/openidm-ui-common/src/main/js/org/forgerock/openidm/ui/common/delegates/ConfigDelegate.js +++ b/openidm-ui/openidm-ui-common/src/main/js/org/forgerock/openidm/ui/common/delegates/ConfigDelegate.js @@ -12,6 +12,7 @@ * information: "Portions copyright [year] [name of copyright owner]". * * Copyright 2016 ForgeRock AS. + * Portions copyright 2026 3A Systems LLC. */ define([ @@ -23,7 +24,7 @@ define([ "org/forgerock/commons/ui/common/main/EventManager" ], function($, _, constants, AbstractDelegate, conf, eventManager) { - var obj = new AbstractDelegate(constants.host + "/openidm/config"); + var obj = new AbstractDelegate(constants.host + "/" + constants.context + "/config"); obj.serviceCall = function (callParams) { diff --git a/openidm-ui/openidm-ui-common/src/main/js/org/forgerock/openidm/ui/common/delegates/InfoDelegate.js b/openidm-ui/openidm-ui-common/src/main/js/org/forgerock/openidm/ui/common/delegates/InfoDelegate.js index 841c3bcc67..fca3f0d987 100644 --- a/openidm-ui/openidm-ui-common/src/main/js/org/forgerock/openidm/ui/common/delegates/InfoDelegate.js +++ b/openidm-ui/openidm-ui-common/src/main/js/org/forgerock/openidm/ui/common/delegates/InfoDelegate.js @@ -12,6 +12,7 @@ * information: "Portions copyright [year] [name of copyright owner]". * * Copyright 2015-2016 ForgeRock AS. + * Portions copyright 2026 3A Systems LLC. */ define([ @@ -19,7 +20,7 @@ define([ "org/forgerock/commons/ui/common/main/AbstractDelegate" ], function(constants, AbstractDelegate) { - var obj = new AbstractDelegate(constants.host + "/openidm/info/"); + var obj = new AbstractDelegate(constants.host + "/" + constants.context + "/info/"); obj.getVersion = function() { return obj.serviceCall({ diff --git a/openidm-ui/openidm-ui-common/src/main/js/org/forgerock/openidm/ui/common/delegates/InternalUserDelegate.js b/openidm-ui/openidm-ui-common/src/main/js/org/forgerock/openidm/ui/common/delegates/InternalUserDelegate.js index 79ccf2e8a5..23cdc7f347 100644 --- a/openidm-ui/openidm-ui-common/src/main/js/org/forgerock/openidm/ui/common/delegates/InternalUserDelegate.js +++ b/openidm-ui/openidm-ui-common/src/main/js/org/forgerock/openidm/ui/common/delegates/InternalUserDelegate.js @@ -12,6 +12,7 @@ * information: "Portions copyright [year] [name of copyright owner]". * * Copyright 2011-2016 ForgeRock AS. + * Portions copyright 2026 3A Systems LLC. */ define([ @@ -21,7 +22,7 @@ define([ "org/forgerock/commons/ui/common/main/EventManager" ], function(constants, AbstractDelegate, configuration, eventManager) { - var obj = new AbstractDelegate(constants.host + "/openidm/repo/internal/user"); + var obj = new AbstractDelegate(constants.host + "/" + constants.context + "/repo/internal/user"); obj.patchSelectedUserAttributes = function(id, rev, patchDefinitionObject, successCallback, errorCallback, noChangesCallback) { //PATCH for repo is unsupported diff --git a/openidm-ui/openidm-ui-common/src/main/js/org/forgerock/openidm/ui/common/delegates/PolicyDelegate.js b/openidm-ui/openidm-ui-common/src/main/js/org/forgerock/openidm/ui/common/delegates/PolicyDelegate.js index 26c6e983f6..b9237576f7 100644 --- a/openidm-ui/openidm-ui-common/src/main/js/org/forgerock/openidm/ui/common/delegates/PolicyDelegate.js +++ b/openidm-ui/openidm-ui-common/src/main/js/org/forgerock/openidm/ui/common/delegates/PolicyDelegate.js @@ -12,6 +12,7 @@ * information: "Portions copyright [year] [name of copyright owner]". * * Copyright 2011-2016 ForgeRock AS. + * Portions copyright 2026 3A Systems LLC. */ define([ @@ -22,7 +23,7 @@ define([ "org/forgerock/commons/ui/common/main/EventManager" ], function(_, constants, AbstractDelegate, configuration, eventManager) { - var obj = new AbstractDelegate(constants.host + "/openidm/policy"); + var obj = new AbstractDelegate(constants.host + "/" + constants.context + "/policy"); obj.readEntity = function (baseEntity) { if (baseEntity === "selfservice/registration") { diff --git a/openidm-ui/openidm-ui-common/src/main/js/org/forgerock/openidm/ui/common/delegates/ResourceDelegate.js b/openidm-ui/openidm-ui-common/src/main/js/org/forgerock/openidm/ui/common/delegates/ResourceDelegate.js index 7b1ca5cff7..e7843b277a 100644 --- a/openidm-ui/openidm-ui-common/src/main/js/org/forgerock/openidm/ui/common/delegates/ResourceDelegate.js +++ b/openidm-ui/openidm-ui-common/src/main/js/org/forgerock/openidm/ui/common/delegates/ResourceDelegate.js @@ -12,6 +12,7 @@ * information: "Portions copyright [year] [name of copyright owner]". * * Copyright 2011-2016 ForgeRock AS. + * Portions copyright 2026 3A Systems LLC. */ define([ @@ -24,7 +25,7 @@ define([ "org/forgerock/commons/ui/common/util/ObjectUtil" ], function($, _, constants, AbstractDelegate, configDelegate, messagesManager, ObjectUtil) { - var obj = new AbstractDelegate(constants.host + "/openidm/"); + var obj = new AbstractDelegate(constants.host + "/" + constants.context + "/"); obj.getSchema = function(args){ var objectType = args[0], @@ -145,7 +146,7 @@ define([ obj.linkedView = function(id, resourcePath) { return obj.serviceCall({ - serviceUrl: constants.host + "/openidm/endpoint/linkedView/" + resourcePath, + serviceUrl: constants.host + "/" + constants.context + "/endpoint/linkedView/" + resourcePath, url: id, type: "GET" }); diff --git a/openidm-ui/openidm-ui-common/src/main/js/org/forgerock/openidm/ui/common/delegates/SearchDelegate.js b/openidm-ui/openidm-ui-common/src/main/js/org/forgerock/openidm/ui/common/delegates/SearchDelegate.js index c25b469901..7f6a59c626 100644 --- a/openidm-ui/openidm-ui-common/src/main/js/org/forgerock/openidm/ui/common/delegates/SearchDelegate.js +++ b/openidm-ui/openidm-ui-common/src/main/js/org/forgerock/openidm/ui/common/delegates/SearchDelegate.js @@ -12,6 +12,7 @@ * information: "Portions copyright [year] [name of copyright owner]". * * Copyright 2015-2016 ForgeRock AS. + * Portions copyright 2026 3A Systems LLC. */ define([ @@ -20,7 +21,7 @@ define([ "org/forgerock/commons/ui/common/main/AbstractDelegate" ], function(_, constants, AbstractDelegate) { - var obj = new AbstractDelegate(constants.host + "/openidm"); + var obj = new AbstractDelegate(constants.host + "/" + constants.context); obj.searchResults = function (resource, props, searchString, comparisonOperator, additionalQuery) { var maxPageSize = 10; diff --git a/openidm-ui/openidm-ui-common/src/main/js/org/forgerock/openidm/ui/common/delegates/SocialDelegate.js b/openidm-ui/openidm-ui-common/src/main/js/org/forgerock/openidm/ui/common/delegates/SocialDelegate.js index e78e1d6549..9d27d64a32 100644 --- a/openidm-ui/openidm-ui-common/src/main/js/org/forgerock/openidm/ui/common/delegates/SocialDelegate.js +++ b/openidm-ui/openidm-ui-common/src/main/js/org/forgerock/openidm/ui/common/delegates/SocialDelegate.js @@ -12,6 +12,7 @@ * information: "Portions copyright [year] [name of copyright owner]". * * Copyright 2014-2016 ForgeRock AS. + * Portions copyright 2026 3A Systems LLC. */ define([ @@ -27,7 +28,7 @@ define([ Constants, OAuth) { - var obj = new AbstractDelegate(Constants.host + "/openidm/identityProviders"); + var obj = new AbstractDelegate(Constants.host + "/" + Constants.context + "/identityProviders"); obj.loginProviders = function () { var headers = {}, @@ -38,7 +39,7 @@ define([ return obj.serviceCall({ url: "", - serviceUrl: "/openidm/authentication", + serviceUrl: "/" + Constants.context + "/authentication", type: "get", headers: headers }).then((results) => { @@ -82,7 +83,7 @@ define([ obj.getAuthToken = function (provider, code, redirect_uri) { return this.serviceCall({ "type": "POST", - "serviceUrl": "/openidm/authentication", + "serviceUrl": "/" + Constants.context + "/authentication", "url": "?_action=getAuthToken", "data": JSON.stringify({ provider: provider, diff --git a/openidm-ui/openidm-ui-common/src/main/js/org/forgerock/openidm/ui/common/delegates/SystemHealthDelegate.js b/openidm-ui/openidm-ui-common/src/main/js/org/forgerock/openidm/ui/common/delegates/SystemHealthDelegate.js index 64a9b3ea9d..b5fa08283e 100644 --- a/openidm-ui/openidm-ui-common/src/main/js/org/forgerock/openidm/ui/common/delegates/SystemHealthDelegate.js +++ b/openidm-ui/openidm-ui-common/src/main/js/org/forgerock/openidm/ui/common/delegates/SystemHealthDelegate.js @@ -12,6 +12,7 @@ * information: "Portions copyright [year] [name of copyright owner]". * * Copyright 2015-2016 ForgeRock AS. + * Portions copyright 2026 3A Systems LLC. */ define([ @@ -20,7 +21,7 @@ define([ "org/forgerock/commons/ui/common/main/EventManager" ], function(constants, AbstractDelegate, eventManager) { - var obj = new AbstractDelegate(constants.host + "/openidm/health"); + var obj = new AbstractDelegate(constants.host + "/" + constants.context + "/health"); obj.connectorDelegateCache = {}; diff --git a/openidm-ui/openidm-ui-common/src/main/js/org/forgerock/openidm/ui/common/notifications/NotificationDelegate.js b/openidm-ui/openidm-ui-common/src/main/js/org/forgerock/openidm/ui/common/notifications/NotificationDelegate.js index 7994d3734f..843d20154d 100644 --- a/openidm-ui/openidm-ui-common/src/main/js/org/forgerock/openidm/ui/common/notifications/NotificationDelegate.js +++ b/openidm-ui/openidm-ui-common/src/main/js/org/forgerock/openidm/ui/common/notifications/NotificationDelegate.js @@ -12,6 +12,7 @@ * information: "Portions copyright [year] [name of copyright owner]". * * Copyright 2011-2016 ForgeRock AS. + * Portions copyright 2026 3A Systems LLC. */ define([ @@ -21,7 +22,7 @@ define([ "org/forgerock/commons/ui/common/main/EventManager" ], function(constants, AbstractDelegate, configuration, eventManager) { - var obj = new AbstractDelegate(constants.host + "/openidm/endpoint/usernotifications"); + var obj = new AbstractDelegate(constants.host + "/" + constants.context + "/endpoint/usernotifications"); obj.getNotificationsForUser = function(successCallback, errorCallback) { obj.serviceCall({ 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/main/js/org/forgerock/openidm/ui/common/workflow/WorkflowDelegate.js b/openidm-ui/openidm-ui-common/src/main/js/org/forgerock/openidm/ui/common/workflow/WorkflowDelegate.js index 0db80b9660..408c17bf84 100644 --- a/openidm-ui/openidm-ui-common/src/main/js/org/forgerock/openidm/ui/common/workflow/WorkflowDelegate.js +++ b/openidm-ui/openidm-ui-common/src/main/js/org/forgerock/openidm/ui/common/workflow/WorkflowDelegate.js @@ -12,6 +12,7 @@ * information: "Portions copyright [year] [name of copyright owner]". * * Copyright 2011-2016 ForgeRock AS. + * Portions copyright 2026 3A Systems LLC. */ define([ @@ -22,11 +23,11 @@ define([ var obj = {}, taskManagementUrl, processManagementUrl, taskDefinitionUrl, processDefinitionUrl, endpointUrl, processDefinitionsEndpointUrl; - taskManagementUrl = "/openidm/workflow/taskinstance"; - processManagementUrl = "/openidm/workflow/processinstance"; - processDefinitionUrl = "/openidm/workflow/processdefinition"; - endpointUrl = "/openidm/endpoint/gettasksview"; - processDefinitionsEndpointUrl = "/openidm/endpoint/getprocessesforuser"; + taskManagementUrl = "/" + constants.context + "/workflow/taskinstance"; + processManagementUrl = "/" + constants.context + "/workflow/processinstance"; + processDefinitionUrl = "/" + constants.context + "/workflow/processdefinition"; + endpointUrl = "/" + constants.context + "/endpoint/gettasksview"; + processDefinitionsEndpointUrl = "/" + constants.context + "/endpoint/getprocessesforuser"; obj.startProccess = function(proccessNameKey, params, successCallback, errorCallback) { 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 dba1f757a1..b8041e0c97 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 @@ -1,5 +1,66 @@ +/** + * The contents of this file are subject to the terms of the Common Development and + * Distribution License (the License). You may not use this file except in compliance with the + * License. + * + * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + * specific language governing permission and limitations under the License. + * + * When distributing Covered Software, include this CDDL Header Notice in each file and include + * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + * Header, with the fields enclosed by brackets [] replaced by your own identifying + * information: "Portions copyright [year] [name of copyright owner]". + * + * Portions copyright 2026 3A Systems LLC. + */ + define([ "org/forgerock/openidm/ui/common/util/Constants" ], function (Constants) { QUnit.module('Constants Tests'); -}); \ No newline at end of file + + QUnit.test("Constants.context defaults to 'openidm'", function (assert) { + assert.equal(Constants.context, "openidm", + "The default context should be 'openidm'"); + }); + + QUnit.test("Constants.context produces correct REST API path prefix", function (assert) { + var contextPath = "/" + Constants.context; + assert.equal(contextPath, "/openidm", + "The default REST API path prefix should be '/openidm'"); + }); + + QUnit.test("Constants.context can be overridden for custom REST path", function (assert) { + var originalContext = Constants.context; + Constants.context = "myidm"; + assert.equal(Constants.context, "myidm", + "Constants.context should be overridable to a custom value"); + assert.equal("/" + Constants.context, "/myidm", + "Custom context should produce the correct REST API path prefix"); + Constants.context = originalContext; + }); + + 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; + } + }); +}); diff --git a/openidm-zip/src/main/resources/conf/system.properties b/openidm-zip/src/main/resources/conf/system.properties index 22e3e08bc7..dc53fa0293 100644 --- a/openidm-zip/src/main/resources/conf/system.properties +++ b/openidm-zip/src/main/resources/conf/system.properties @@ -9,6 +9,11 @@ launcher.project.url=&{launcher.project.url} # Example bootstrap setting to set on command line or in this file +# REST context path for the OpenIDM API. Default is /openidm. +# Change this to serve the REST API under a different path (e.g., /myidm). +# This can also be set via the JVM argument: -Dopenidm.context.path=/myidm +# openidm.context.path=/openidm + # To disable the JSON file monitoring you need to uncomment this line # openidm.fileinstall.enabled=false