[SYNCOPE-1978] Search audit events by username#1443
Merged
Conversation
Restore the ability to search audit events by the username of the entity whose payload was logged, complementing the existing entityKey (UUID) match. A new repeatable "username" query parameter on AuditQuery matches the token "username":"<value>" embedded in the serialized audit payload (before, inputs, output, throwable), with exact match and multiple values OR'ed, composing with the existing audit search filters. This is useful to retrieve a user's audit trail by login name, in particular after the user has been deleted and its key is no longer available. The filter is threaded through AuditServiceImpl, AuditLogic and the AuditEventDAO interface and all of its implementations (JPA, Neo4j, Elasticsearch, OpenSearch). The username value is bound as a query parameter (SQL/Cypher) or passed as a structured phrase query (Elasticsearch/OpenSearch), so there is no injection surface; the JPA LIKE predicate escapes metacharacters so only exact matches are returned. Covered by integration tests in AuditITCase, including the deleted-user case.
ilgrosso
pushed a commit
that referenced
this pull request
Jun 27, 2026
Member
|
Thank you @ozimakov I have cherry-picked this work to branches |
ilgrosso
pushed a commit
that referenced
this pull request
Jun 27, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
SYNCOPE-1978
What
Restores the ability to search audit events by the username of the entity whose payload was logged, complementing the existing
entityKey(UUID) match. A new repeatableusernamequery parameter onAuditQuerymatches the token"username":"<value>"embedded in the serialized audit payload (before,inputs,output,throwable):?username=jdoe?username=jdoe&username=asmithIt is an exact match (no wildcards) and composes (AND) with the existing audit search filters (
entityKey,who,type,category,op,outcome,before/after).Why
Today an audit search can be pinned to a specific user only via
entityKey, which requires the user's UUID. That UUID is unavailable once the user has been deleted, so there is no way to retrieve a deleted user's audit trail. The username, however, is preserved inside thebeforesnapshot of the relevant audit events, so matching by username makes that trail searchable — and saves a resolve-then-query round-trip for live users.Syncope 3.0 allowed an approximation by abusing
entityKey=<username>(an impreciseLIKE '%key%<value>%'); the 4.0 audit refactor correctly tightenedentityKeyto match only the UUID, which removed that side effect. This change reintroduces the capability deliberately and precisely, as a dedicated filter rather than re-looseningentityKey.Implementation
The filter is threaded through
AuditServiceImpl→AuditLogic→ theAuditEventDAOinterface and all of its implementations:(beforeValue/inputs/output/throwable LIKE ? ESCAPE '#')matching%"username":"<value>"%, OR-ed; the value is bound as a parameter and itsLIKEmetacharacters are escaped (so%/_in a username match literally);ANY(u IN $usernames WHERE n.before CONTAINS u OR ...)— a literalCONTAINSwith a bound list parameter (no regex);multi_matchphrase query of"username":"<value>"over the payload fields, OR-ed viabool/should.All values are bound as query parameters (SQL/Cypher) or passed as structured phrase queries (Elasticsearch/OpenSearch), so there is no injection surface; the pre-existing
entityKeypredicate is intentionally left unchanged.Tests
Integration tests in
AuditITCasecover exact match (asserting the matched event's payload truly carries the username), multiple values (OR), non-match exclusion, exact-not-prefix matching, and the deleted-user case (a user is created then deleted, and is still found by username via the delete event'sbeforesnapshot). Verified end-to-end against embedded PostgreSQL, Neo4j and Elasticsearch.