Repository navigation
effectiveRole calculation crashes if a mistake was done with a role assigned using time constraint #250
Description
Activity
Just to illustrate what I am saying, I added this piece of code in my effectiveRoles to sort my own situation, in case this is of any help
``
function isNowWithinInterval(interval) {
const [startString, endString] = interval.split('/');const start = new Date(startString); const end = new Date(endString); const now = new Date(); // Invalid dates if (Number.isNaN(start.getTime()) || Number.isNaN(end.getTime())) { logger.warn(`WARNING: Invalid time interval: ${interval}`); return false; } // No start: valid until the end if (!start) { return now <= end; } // No start: valid until the end if (!start) { return now <= end; } // Invalid interval: end is before start if (end < start) { logger.warn(`Invalid interval: end is before start: ${interval}`); return false; } return now >= start && now <= end;}
`Thanks for the report and for sharing your workaround, it pinpoints the problem:
DateUtil.isNowWithinIntervalparses the duration with Joda'sInterval.parse, which throws on an interval whose end is before its start. SinceeffectiveRolesisreturnByDefault, that script runs on every read and every update of the user, so the user can neither be read nor fixed throughmanaged/user.A note on the snippet:
new Date(...)only understands thedatetime/datetimeform, but a temporal constraint may also be written asdatetime/periodorperiod/datetime(e.g.2026-10-01T00:00:00Z/P30D). With that replacement such valid constraints silently stop granting the role. (Also,!startis never true for aDateobject, so those two blocks never run.)A workaround that keeps the existing interval semantics is to guard the existing call rather than re-parse the dates:
- Copy
bin/defaults/script/roles/effectiveRoles.jsto<your project>/script/roles/effectiveRoles.js— the project script directory takes precedence overbin/defaults/script, so the default file stays untouched. - In the copy, replace
processConstraintswith:
function processConstraints(object) { if (object.temporalConstraints !== undefined && object.temporalConstraints.length) { // Loops through constraints for (var index in object.temporalConstraints) { var constraint = object.temporalConstraints[index]; try { // If at least one constraint passes, the role is in effect if (org.forgerock.openidm.util.DateUtil.getDateUtil().isNowWithinInterval(constraint.duration)) { return true; } } catch (e) { // An invalid duration (e.g. end before start) neither breaks the read nor grants the role logger.warn("Ignoring temporal constraint with an invalid duration {}", String(constraint.duration)); } } return false; } // No temporal constraints return true; };
With that in place the user can be read again, and the broken constraint can be corrected or removed from the admin UI or over REST, e.g. list the grants with
GET /openidm/managed/user/<userId>/roles?_queryFilter=true&_fields=_ref,_refPropertiesand remove the bad one withDELETE /openidm/managed/user/<userId>/roles/<grantId>. If the reversed interval is on the role itself (temporalConstraintsofmanaged/role/<roleId>), the same workaround applies; fix the role afterwards.We are preparing a fix that:
- rejects such a temporal constraint with
400 Bad Requestwhen a role grant or a role is written, instead of storing it; - makes
effectiveRolesand the other temporal-constraint scripts skip, with a warning, an invalid constraint that is already stored, so it does not grant the role and no longer breaks reading the user.
Reacted by Cesar- Copy
makes completely sense.. many thx
Two corrections to the workaround above, after trying it on a build without the fix:
-
Restart OpenIDM after copying the script. Modules loaded with
require()are cached, so until the restart the server keeps running the defaultbin/defaults/script/roles/effectiveRoles.jsand the user still fails with 500. -
The grant id is
_refProperties._id, not a top-level_id. For example:GET /openidm/managed/user/<userId>/roles?_queryFilter=true&_fields=_ref,_refProperties → {"result":[{"_ref":"managed/role/<roleId>","_refProperties":{"temporalConstraints":[...],"_id":"<grantId>", ...}}]} DELETE /openidm/managed/user/<userId>/roles/<grantId>
With the restart, reading the user returned 200 again (the broken grant did not count towards
effectiveRoles), and theDELETEremoved the grant.How such a value can come from the admin UI: if the start date of a temporal constraint is set and the end date is left empty, the UI fills in the current time as the end. With a start date in the future that is an interval whose end is before its start. This happens both on the role's own temporal constraint and when adding role members with a temporal constraint.
-
- addedbugSomething isn't workingSomething isn't workingjavaPull requests that update Java codePull requests that update Java codejavascriptPull requests that update Javascript codePull requests that update Javascript codeuiAdmin and end-user web UI (openidm-ui-*)Admin and end-user web UI (openidm-ui-*)
on Oct 6, 2026 - added 4 commits that reference this issue
on Oct 6, 2026
Describe the bug
If for wahtever reason you manage to assign a Role to a user using time constraint. and the start time is more recent than the end time a crash happen that cannot be solved easily as internal error happens just accesing the use
To Reproduce
Assign a role with the endtime behind starttime, for instance, starttime today , and endtime yesterday.
Expected behavior
Any error, or warning, but not an 500 Internal Error
like this
"Oct 06, 2026 12:02:59 PM org.forgerock.openidm.servlet.internal.ServletConnectionFactory$3 handleException
WARNING: Resource exception: 500 Internal Server Error: "effectiveRoles onRetrieve script encountered exception"
org.forgerock.json.resource.InternalServerErrorException: effectiveRoles onRetrieve script encountered exception
"
Screenshots
The thing is that the default onRetrioeve script for effectiveRoles triggers this script require('roles/effectiveRoles').calculateEffectiveRoles(object, 'roles');"
"effectiveRoles" : {
"type" : "array",
"title" : "Effective Roles",
"viewable" : false,
"returnByDefault" : true,
"isVirtual" : true,
"onRetrieve" : {
"type" : "text/javascript",
"source" : "require('roles/effectiveRoles').calculateEffectiveRoles(object, 'roles');"
},
"items" : {
"type" : "object"
}
},
And then, in the script , in line 83 we have
if (org.forgerock.openidm.util.DateUtil.getDateUtil().isNowWithinInterval(constraint.duration)) {
return true;
}
That isNowWithinInternal crashes if the data is unexpected, I think that should be replaced by something more fault tolerant.
Vote to raise the priority 🖐️