Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 24 additions & 7 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,7 @@ jobs:
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
! grep -E "ERROR|SEVERE|Exception|Throwable" openidm/logs/openidm0.log.0
- name: Test on Windows
if: runner.os == 'Windows'
run: |
Expand All @@ -66,8 +65,11 @@ jobs:
Start-Sleep -s 180
type logs\openidm0.log.0
findstr "OpenIDM ready" logs\openidm0.log.0
type logs\openidm0.log.0 | find /c '"ERROR"' | findstr "0"
type logs\openidm0.log.0 | find /c '"SEVERE"' | findstr "0"
if (Select-String -Path logs\openidm0.log.0 -Pattern 'ERROR|SEVERE|Exception|Throwable' -CaseSensitive -Quiet) {
Write-Host "Errors or exceptions detected in openidm0.log.0"
Select-String -Path logs\openidm0.log.0 -Pattern 'ERROR|SEVERE|Exception|Throwable' -CaseSensitive
exit 1
}
- name: Upload failure artifacts
uses: actions/upload-artifact@v7
if: ${{ failure() }}
Expand Down Expand Up @@ -127,7 +129,7 @@ jobs:
run: |
OPTS=""
if [ -n "${{ matrix.context_path }}" ]; then
OPTS="-Dlogback.configurationFile=conf/logging-config.groovy -Dopenidm.context.path=${{ matrix.context_path }}"
OPTS="-Dopenidm.context.path=${{ matrix.context_path }}"
fi
ARGS=""
if [ -n "${{ matrix.samples }}" ]; then
Expand All @@ -136,8 +138,7 @@ jobs:
OPENIDM_OPTS="$OPTS" openidm/startup.sh $ARGS &
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
! grep -E "ERROR|SEVERE|Exception|Throwable" openidm/logs/openidm0.log.0
- name: UI Smoke Tests (Playwright)
run: |
cd e2e
Expand Down Expand Up @@ -170,7 +171,23 @@ jobs:
done
else
echo "openidm/logs directory not found"
exit 0
fi
echo "----- Checking logs for errors/exceptions -----"
status=0
while IFS= read -r f; do
if grep -E -n "ERROR|SEVERE|Exception|Throwable" "$f" > /tmp/log_errors.$$ 2>/dev/null; then
echo "Found errors/exceptions in $f:"
cat /tmp/log_errors.$$
status=1
fi
rm -f /tmp/log_errors.$$
done < <(find openidm/logs -type f)
if [ "$status" -ne 0 ]; then
echo "Errors or exceptions detected in openidm logs"
exit 1
fi
echo "No errors or exceptions detected in openidm logs"
build-docker:
runs-on: 'ubuntu-latest'
services:
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ FROM eclipse-temurin:25-jre-jammy
LABEL org.opencontainers.image.authors="Open Identity Platform Community"

ENV USER="openidm"
ENV OPENIDM_OPTS="-server -XX:+UseContainerSupport --add-exports java.base/com.sun.jndi.ldap=ALL-UNNAMED -Dlogback.configurationFile=conf/logging-config.groovy"
ENV OPENIDM_OPTS="-server -XX:+UseContainerSupport --add-exports java.base/com.sun.jndi.ldap=ALL-UNNAMED"

ARG VERSION

Expand Down
2 changes: 1 addition & 1 deletion Dockerfile-alpine
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ FROM alpine:latest
LABEL org.opencontainers.image.authors="Open Identity Platform Community"

ENV USER="openidm"
ENV OPENIDM_OPTS="-server -XX:+UseContainerSupport -Dlogback.configurationFile=conf/logging-config.groovy"
ENV OPENIDM_OPTS="-server -XX:+UseContainerSupport"

ARG VERSION

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
* information: "Portions copyright [year] [name of copyright owner]".
*
* Copyright 2012-2016 ForgeRock AS.
* Portions Copyrighted 2024 3A Systems LLC.
* Portions Copyrighted 2024-2026 3A Systems LLC.
*/
package org.forgerock.openidm.workflow.activiti.impl;

Expand All @@ -34,7 +34,6 @@
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import javax.script.ScriptEngine;
import javax.sql.DataSource;
import javax.transaction.TransactionManager;
import org.activiti.engine.ProcessEngine;
Expand All @@ -46,8 +45,6 @@
import org.activiti.engine.impl.scripting.ResolverFactory;
import org.activiti.engine.impl.scripting.ScriptBindingsFactory;
import org.activiti.engine.impl.scripting.ScriptingEngines;
import org.activiti.osgi.Extender;
import org.activiti.osgi.OsgiScriptingEngines;
import org.activiti.osgi.blueprint.ProcessEngineFactory;
import org.forgerock.openidm.datasource.DataSourceService;
import org.forgerock.openidm.router.IDMConnectionFactory;
Expand Down Expand Up @@ -77,10 +74,7 @@
import org.forgerock.openidm.workflow.activiti.impl.session.OpenIDMSessionFactory;
import org.forgerock.util.promise.Promise;
import org.h2.jdbcx.JdbcDataSource;
import org.osgi.framework.Bundle;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceFactory;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.cm.Configuration;
import org.osgi.service.cm.ConfigurationAdmin;
import org.osgi.service.component.ComponentContext;
Expand Down Expand Up @@ -320,12 +314,54 @@ void activate(ComponentContext compContext) {
processEngineFactory.setBundle(compContext.getBundleContext().getBundle());
processEngineFactory.init();

// Post-init wiring: variableTypes / resolverFactories / scriptingEngines
// are populated by buildProcessEngine() inside init(), so they must be
// mutated AFTER init(). ScriptTaskActivityBehavior reads
// configuration.getScriptingEngines() on every script execution, so
// replacing it here is effective.
//ScriptResolverFactory
List<ResolverFactory> resolverFactories = configuration.getResolverFactories();
if (resolverFactories == null) {
resolverFactories = new ArrayList<ResolverFactory>();
}
resolverFactories.add(new OpenIDMResolverFactory());
configuration.setResolverFactories(resolverFactories);
configuration.getVariableTypes().addType(new JsonValueType());
configuration.setScriptingEngines(new OsgiScriptingEngines(new ScriptBindingsFactory(resolverFactories)));
// Use the stock Activiti ScriptingEngines (which delegates to javax.script
// ScriptEngineManager) instead of OsgiScriptingEngines. The latter routes
// resolution through org.activiti.osgi.Extender, whose
// BundleScriptEngineResolver naively parses
// META-INF/services/javax.script.ScriptEngineFactory line by line and
// attempts to Class.forName each '#'-comment line, producing a noisy
// ClassNotFoundException WARNING for every script-task execution
// (see activiti-osgi 5.15 Extender.java). The Groovy ScriptEngineFactory
// is registered explicitly below because OSGi class loaders prevent the
// JDK ServiceLoader inside ScriptEngineManager from discovering it in
// the groovy-all bundle.
ScriptingEngines scriptingEngines =
new ScriptingEngines(new ScriptBindingsFactory(resolverFactories));
// GroovyScriptEngineFactory.getEngineName() returns "Groovy" but
// BPMN scriptFormat="groovy" looks up by lowercase language name.
// ScriptingEngines.addScriptEngineFactory only registers under
// getEngineName(); we additionally register all language/short names
// (and mime types/extensions) directly on a pre-built ScriptEngineManager.
javax.script.ScriptEngineFactory groovyFactory =
new org.codehaus.groovy.jsr223.GroovyScriptEngineFactory();
javax.script.ScriptEngineManager mgr = new javax.script.ScriptEngineManager();
mgr.registerEngineName(groovyFactory.getEngineName(), groovyFactory);
for (String name : groovyFactory.getNames()) {
mgr.registerEngineName(name, groovyFactory);
}
for (String mime : groovyFactory.getMimeTypes()) {
mgr.registerEngineMimeType(mime, groovyFactory);
}
for (String ext : groovyFactory.getExtensions()) {
mgr.registerEngineExtension(ext, groovyFactory);
}
scriptingEngines = new ScriptingEngines(mgr);
scriptingEngines.setScriptBindingsFactory(new ScriptBindingsFactory(resolverFactories));
configuration.setScriptingEngines(scriptingEngines);


//We are done!!
processEngine = processEngineFactory.getObject();
Expand Down Expand Up @@ -477,29 +513,11 @@ protected void unbindProcessEngine(ProcessEngine processEngine) {
target = "(service.pid=org.forgerock.openidm.script)")
protected void bindScriptRegistry(ScriptRegistry scriptRegistry) {
this.idmSessionFactory.setScriptRegistry(scriptRegistry);
if (Extender.getBundleContext()!=null) {
Extender.getBundleContext().registerService(Extender.ScriptEngineResolver.class, new ServiceFactory<Extender.ScriptEngineResolver>() {
@Override
public Extender.ScriptEngineResolver getService(Bundle bundle, ServiceRegistration<Extender.ScriptEngineResolver> serviceRegistration) {
return new Extender.ScriptEngineResolver() {
@Override
public ScriptEngine resolveScriptEngine(String s) {
if (!"groovy".equalsIgnoreCase(s)) {
throw new RuntimeException("unknown resolveScriptEngine " + s);
}
return new org.codehaus.groovy.jsr223.GroovyScriptEngineImpl();
}
};
}

;

@Override
public void ungetService(Bundle bundle, ServiceRegistration<Extender.ScriptEngineResolver> serviceRegistration, Extender.ScriptEngineResolver scriptEngineResolver) {

}
}, null);
}
// The previous registration of an Extender.ScriptEngineResolver OSGi service was
// tied to OsgiScriptingEngines (now replaced by stock Activiti ScriptingEngines).
// Without OsgiScriptingEngines nothing invokes Extender.resolveScriptEngine, so the
// resolver service is no longer needed and the noisy
// BundleScriptEngineResolver code path is no longer reached at script execution time.
}

protected void unbindScriptRegistry(ScriptRegistry scriptRegistry) {
Expand Down
7 changes: 5 additions & 2 deletions openidm-zip/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
~ your own identifying information:
~ "Portions Copyrighted [year] [name of copyright owner]"
~
~ Portions Copyrighted 2019-2025 3A Systems LLC.
~ Portions Copyrighted 2019-2026 3A Systems LLC.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
Expand Down Expand Up @@ -272,6 +272,10 @@
<groupId>org.apache.felix</groupId>
<artifactId>org.apache.felix.webconsole.plugins.packageadmin</artifactId>
</dependency>
<dependency>
<groupId>org.apache.felix</groupId>
<artifactId>org.apache.felix.prefs</artifactId>
</dependency>
<dependency>
<groupId>org.apache.geronimo.bundles</groupId>
<artifactId>json</artifactId>
Expand Down Expand Up @@ -897,7 +901,6 @@
</activation>
<properties>
<openidm.options>
-Dlogback.configurationFile=conf/logging-config.groovy
</openidm.options>
</properties>
<dependencies>
Expand Down
2 changes: 1 addition & 1 deletion openidm-zip/src/main/resources/bin/install-service.bat
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ set OPENIDM_OPTS_SERVICE=%OPENIDM_OPTS: =;%
rem set SERVER_START_PARAMS="-c;bin/launcher.json"
set CP=bin/launcher.jar;bin/felix.jar
rem JAVA_OPTS_SERVICE will be fed to the prunmgr.exe which requires all semi-colon delimiters
set JAVA_OPTS_SERVICE=%OPENIDM_OPTS_SERVICE%;-Djava.util.logging.config.file=conf\logging.properties;-Dlogback.configurationFile=conf\logging-config.xml;
set JAVA_OPTS_SERVICE=%OPENIDM_OPTS_SERVICE%;-Djava.util.logging.config.file=conf\logging.properties;
rem Enable debugging uncomment the line below
rem set JAVA_OPTS_SERVICE=%JAVA_OPTS_SERVICE%;-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005;

Expand Down
3 changes: 3 additions & 0 deletions openidm-zip/src/main/resources/conf/logging.properties
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ org.identityconnectors.framework.impl.api.local.LocalConnectorInfoManagerImpl.le
# Suppress warnings of failed error page model validation
org.ops4j.pax.web.service.spi.model.elements.ErrorPageModel.level=SEVERE

# Suppress noisy INFO records from pax-web bundle (servlet/error-page registration)
org.ops4j.pax.web.level=WARNING

# OrientDB 3.x: suppress harmless WARNINGs that we cannot act on
# - OScriptManager logs "ECMAScript engine not found" when no JSR-223 javascript
# engine is on the classpath (we don't ship one and don't use OrientDB JS).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@
"source" : "roles",
"transform" : {
"type" : "text/javascript",
"source" : "source.split(',').map(function (r) { return {'_ref' : (r.indexOf('openidm-') === 0 ? 'repo/internal/role/' : 'managed/role/') + r }; })"
"file" : "script/rolesToAuthzRoles.js"
},
"target" : "authzRoles"
},
Expand Down Expand Up @@ -216,7 +216,7 @@
"source" : "",
"transform" : {
"type" : "text/javascript",
"source" : "openidm.query('managed/user/' + source._id + '/authzRoles', {'_queryFilter': 'true'}).result.map(function (r) { return r._ref.split('/').pop(); } ).join(',')"
"file" : "script/authzRolesToRoles.js"
},
"target" : "roles"
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* 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 2026 3A Systems, LLC.
*/
/*global source, openidm */
// Flatten the managed/user authzRoles relationship into the comma-separated
// "roles" attribute consumed by the XML connector.
//
// File-based (vs inline in sync.json) for the same race-condition reason
// documented in rolesToAuthzRoles.js: avoids ScriptRegistry STARTING-state
// ("Script status is 8") errors during the first reconciliation after boot.
(function () {
if (source === null || source === undefined || source._id === null || source._id === undefined) {
return "";
}
var result = openidm.query(
'managed/user/' + source._id + '/authzRoles',
{'_queryFilter': 'true'}
);
if (!result || !result.result) {
return "";
}
return result.result.map(function (r) {
return r._ref.split('/').pop();
}).join(',');
}());
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* 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 2026 3A Systems, LLC.
*/

/*global source */

// Transform a comma-separated XML "roles" attribute into the array form
// expected by the managed/user "authzRoles" relationship.
//
// Lives in a separate file (rather than as an inline transform inside
// sync.json) so the script is registered through the file-based code path of
// ScriptRegistry. Inline scripts embedded in mapping configs can be invoked
// during the very first reconciliation while the registry is still moving the
// freshly compiled script from STARTING (status=8) to ACTIVE (status=32),
// producing a transient "Script status is 8" ScriptException that gets
// flagged by the strict CI log scan.
(function () {
if (source === null || source === undefined || String(source).length === 0) {
return [];
}
return String(source).split(',').map(function (r) {
return {
"_ref": (r.indexOf('openidm-') === 0 ? 'repo/internal/role/' : 'managed/role/') + r
};
});
}());

Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
information: "Portions Copyrighted [year] [name of copyright owner]".

Copyright (c) 2011-2015 ForgeRock AS. All rights reserved.
Portions Copyright 2026 3A Systems, LLC.
-->
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="Examples" id="definitions" xsi:schemaLocation=" http://www.omg.org/spec/BPMN/20100524/MODEL http://local.openicf.forgerock.org/BPMN20.xsd">
<process id="contractorOnboarding" name="Contractor onboarding process" isExecutable="true">
Expand Down Expand Up @@ -98,8 +99,12 @@

// Automatically send the user a password reset email
// Current limitation with supplying locale via http headers requires the call to be made via http
def openidmContextPath = identityServer.getProperty('openidm.context.path', '/openidm')
if (!openidmContextPath.startsWith('/')) {
openidmContextPath = '/' + openidmContextPath
}
openidm.action("external/rest", "call", [
"url": "https://localhost:"+identityServer.getProperty('openidm.port.https')+"/openidm/selfservice/reset?_action=submitRequirements",
"url": "https://localhost:"+identityServer.getProperty('openidm.port.https')+openidmContextPath+"/selfservice/reset?_action=submitRequirements",
"method": "POST",
"headers": [
"Content-Type": "application/json",
Expand Down
Loading
Loading