Add onQueryResult script hook to filter managed object query results#139
Add onQueryResult script hook to filter managed object query results#139
onQueryResult script hook to filter managed object query results#139Conversation
… results Agent-Logs-Url: https://github.com/OpenIdentityPlatform/OpenIDM/sessions/f2c7d70c-33e3-4504-b613-171f30b93c52 Co-authored-by: vharseko <6818498+vharseko@users.noreply.github.com>
onQueryResult script hook to filter managed object query results
Agent-Logs-Url: https://github.com/OpenIdentityPlatform/OpenIDM/sessions/e884c292-d30f-45c9-8f55-12b014bf2fd9 Co-authored-by: vharseko <6818498+vharseko@users.noreply.github.com>
Agent-Logs-Url: https://github.com/OpenIdentityPlatform/OpenIDM/sessions/f590346a-e1fb-450d-a5bc-87fdf4875fdd Co-authored-by: vharseko <6818498+vharseko@users.noreply.github.com>
…iles Agent-Logs-Url: https://github.com/OpenIdentityPlatform/OpenIDM/sessions/980c87aa-e675-45da-a77c-bbccb40d75ea Co-authored-by: vharseko <6818498+vharseko@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Adds a new managed object scripting hook to allow per-object filtering of managed object query results, enabling custom access-control and result-shaping logic during queryCollection().
Changes:
- Added a new
onQueryResultmanaged object script hook and invoked it once per query result to optionally exclude objects. - Updated integrators guide documentation to describe
onQueryResultand list it among managed.json triggers/variables. - Added test coverage and a new managed object config fixture demonstrating
onQueryResultfiltering.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| openidm-core/src/main/java/org/forgerock/openidm/managed/ManagedObjectSet.java | Introduces onQueryResult hook and executes it per query result to skip excluded resources. |
| openidm-core/src/test/java/org/forgerock/openidm/managed/ManagedObjectSetTest.java | Adds tests verifying filtering behavior with/without the new hook. |
| openidm-core/src/test/resources/conf/managed-user-on-query-result.json | Adds a managed object config fixture enabling onQueryResult filtering in tests. |
| openidm-doc/src/main/asciidoc/integrators-guide/appendix-objects.adoc | Documents the new onQueryResult hook in managed object configuration. |
| openidm-doc/src/main/asciidoc/integrators-guide/appendix-scripting.adoc | Adds onQueryResult to the managed.json script trigger listings and variables table. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Execute the onQueryResult script if configured; skip object if it returns false | ||
| try { | ||
| Object queryResultScriptResult = execScriptHook(managedContext, ScriptHook.onQueryResult, | ||
| resource.getContent(), | ||
| prepareScriptBindings(managedContext, request, resource.getId(), | ||
| new JsonValue(null), new JsonValue(null))); | ||
| if (Boolean.FALSE.equals(queryResultScriptResult)) { | ||
| // Object excluded by onQueryResult script | ||
| return true; |
There was a problem hiding this comment.
onQueryResult filtering only excludes when the script returns Boolean.FALSE. The docs/PR intent says a “falsy value” should also exclude; for example a JavaScript hook returning 0, "", or undefined will currently not be filtered. Consider either (a) normalizing the script result to a boolean using truthiness semantics (treat null/undefined as include, falsy as exclude), or (b) enforcing a strict boolean return (and updating the docs accordingly) to avoid surprising behavior.
| // Execute the onQueryResult script if configured; skip object if it returns false | |
| try { | |
| Object queryResultScriptResult = execScriptHook(managedContext, ScriptHook.onQueryResult, | |
| resource.getContent(), | |
| prepareScriptBindings(managedContext, request, resource.getId(), | |
| new JsonValue(null), new JsonValue(null))); | |
| if (Boolean.FALSE.equals(queryResultScriptResult)) { | |
| // Object excluded by onQueryResult script | |
| return true; | |
| // Execute the onQueryResult script if configured; skip object if it returns a falsy value | |
| try { | |
| Object queryResultScriptResult = execScriptHook(managedContext, ScriptHook.onQueryResult, | |
| resource.getContent(), | |
| prepareScriptBindings(managedContext, request, resource.getId(), | |
| new JsonValue(null), new JsonValue(null))); | |
| // Normalize the script result using simple truthiness semantics: | |
| // - null (or no return) => include (do not filter) | |
| // - Boolean false, numeric zero, or empty string => exclude | |
| if (queryResultScriptResult != null) { | |
| boolean include = true; | |
| if (queryResultScriptResult instanceof Boolean) { | |
| include = ((Boolean) queryResultScriptResult).booleanValue(); | |
| } else if (queryResultScriptResult instanceof Number) { | |
| include = ((Number) queryResultScriptResult).doubleValue() != 0.0d; | |
| } else if (queryResultScriptResult instanceof CharSequence) { | |
| include = ((CharSequence) queryResultScriptResult).length() != 0; | |
| } | |
| if (!include) { | |
| // Object excluded by onQueryResult script | |
| return true; | |
| } |
| import org.forgerock.json.crypto.simple.SimpleKeyStoreSelector; | ||
| import org.forgerock.json.resource.MemoryBackend; | ||
| import org.forgerock.json.resource.QueryRequest; | ||
| import org.forgerock.json.resource.QueryResponse; |
There was a problem hiding this comment.
QueryResponse is imported but not used, which will fail builds that enforce unused-import checks (and adds noise for readers). Please remove the unused import.
| import org.forgerock.json.resource.QueryResponse; |
|
@copilot apply changes based on the comments in this thread |
…ove unused QueryResponse import Agent-Logs-Url: https://github.com/OpenIdentityPlatform/OpenIDM/sessions/8e8a7bc1-be7c-4fc8-92df-839f5a0defe5 Co-authored-by: vharseko <6818498+vharseko@users.noreply.github.com>
Done in commit
Warning Firewall rules blocked me from connecting to one or more addresses (expand for details)I tried to connect to the following addresses, but was blocked by firewall rules:
If you need me to access, download, or install something from one of these locations, you can either:
|
ManagedObjectSetTest.java— already hadPortions copyright 2026 3A Systems, LLC.ManagedObjectSet.java— addedPortions copyright 2026 3A Systems, LLC.appendix-objects.adoc— updated toPortions Copyright 2024, 2026 3A Systems LLC.appendix-scripting.adoc— updated toPortions Copyright 2024-2026 3A Systems LLC.managed-user-on-query-result.json— new JSON file, no comment syntax possibleManagedObjectSet.java— replacedBoolean.FALSE.equals()check with full truthiness semantics (null=include, Boolean false/Number 0/empty CharSequence=exclude)ManagedObjectSetTest.java— removed unusedQueryResponseimportOriginal prompt
Context
Discussion #131 requests the ability to filter individual objects returned from a query on managed objects, similar to how
validSource/validTargetwork insync.jsonmappings. Currently, there is no way to include/exclude specific objects from query results based on custom scripted logic (e.g., access control based on the requester's attributes).What needs to be implemented
Add a new
onQueryResultscript hook to theManagedObjectSetclass inopenidm-core/src/main/java/org/forgerock/openidm/managed/ManagedObjectSet.java.Behaviour
queryCollection()method (lines ~1275–1309).object) plus standard bindings:context,request,resourceName.false(or a falsy value), the object is excluded from the result set and NOT passed tohandler.handleResource(...).true(or a truthy value, ornull— i.e., when no hook is configured), the object is included as normal.ex[0]and returnfalsefromhandleResource).Changes required
1.
ManagedObjectSet.javaFile:
openidm-core/src/main/java/org/forgerock/openidm/managed/ManagedObjectSet.javaonQueryResultto theScriptHookenum (afteronRetrieve, line ~150):queryCollection(), inside theQueryResourceHandler.handleResource()anonymous class, after theonRetrieveblock and before/after relationship population, invoke the new hook and skip the object if the result isBoolean.FALSE:This block should be inserted after the
onRetrieveblock and before the relationship population andhandler.handleResource(...)call.2. Documentation update
File:
openidm-doc/src/main/asciidoc/integrators-guide/appendix-objects.adocAdd documentation for the new
onQueryResulthook in the managed objects script hooks section, similar to howonRetrieveis documented.File:
openidm-doc/src/main/asciidoc/integrators-guide/appendix-scripting.adocAdd
onQueryResultto the script triggers table formanaged.json(the table that listsonCreate,onUpdate,onRetrieve, etc. with their available variables).Example configuration (
managed.json)After this change, users will be able to configure the hook like this:
{ "name": "user", "onQueryResult": { "type": "text/javascript", "source": "var requester = context.security.authenticationId; object.owner === requester || requester === 'admin';" } }The script has access to:
object— the managed object being evaluatedcontext— the current security/request contextrequest— the query requestReturn
trueto include the object in results,falseto exclude it.Files to change
openidm-core/src/main/java/org/forgerock/openidm/managed/ManagedObjectSet.javaopenidm-doc/src/main/asciidoc/integrators-guide/appendix-objects.adocopenidm-doc/src/main/asciidoc/integrators-guide/appendix-scripting.adocThe following is the prior conversation context from the user's chat exploration (may be truncated):
User: подготовь ответ и согласуй со мной
Assistant: Вот подготовленный ответ на дискуссию #131. Пожалуйста, проверьте и согласуйте:
Предлагаемый ответ на дискуссию:
Thank you for the suggestion! This is a valid use case — filtering query results per-requester based on access control logic, similar to how
validSource/validTargetwork in sync mappings.Current workaround options:
onRetrievescript hook — already available inmanaged.json, it runs for every object returned from a query (whenexecuteOnRetrieve=trueis passed). You can use it to modify the returned object (e.g., strip sensitive fields), but it cannot currently exclude an object from the result set — throwing an exception there would fail the entire query.Router filter (
router.json) — you can intercept query responses viaonResponsehook inconf/router.jsonand post-process the result list, filtering out objec...This pull request was created from Copilot chat.
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.