Skip to content

[backend] feat(audit-log): login/logout/SSO auth event hooks + tests (#5483)#5807

Open
a19836 wants to merge 11 commits into
mainfrom
issue/5483
Open

[backend] feat(audit-log): login/logout/SSO auth event hooks + tests (#5483)#5807
a19836 wants to merge 11 commits into
mainfrom
issue/5483

Conversation

@a19836
Copy link
Copy Markdown
Contributor

@a19836 a19836 commented May 18, 2026

Authentication events (login, logout, SSO) do not flow through the AOP aspect. They are captured via targeted hooks in existing auth components. Ensure all auth events are captured correctly.

Proposed changes

  • Change theUserApi.login(), SsoRefererAuthenticationSuccessHandler, SsoRefererAuthenticationFailureHandler and AppSecurityConfig logout handlers to call the correspondent LogService to log this auth event.

Testing Instructions

  1. Login and logout success and unsuccessfully in the platform.
  2. Go to your console. You should see a json payload with your auth actions details.

Related issues

@a19836 a19836 self-assigned this May 18, 2026
@a19836 a19836 added filigran team use to identify PR from the Filigran team audit-logging labels May 18, 2026
@a19836 a19836 changed the base branch from issue/5480 to issue/5526 May 18, 2026 10:18
@a19836 a19836 changed the title [Audit Logging] Login/logout/SSO auth event hooks + tests (#5483) [backend] auth(audit-log): login/logout/SSO auth event hooks + tests (#5483) May 20, 2026
@a19836 a19836 requested review from Dimfacion and Copilot May 20, 2026 06:10
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR aims to ensure authentication events (local login success/failure, SSO login success/failure, logout) are captured by the audit logging system via targeted hooks in auth-related components (since these flows bypass the AOP aspect).

Changes:

  • Added explicit audit auth-event logging calls in UserApi.login(), SSO success/failure handlers, and the Spring Security logout handler.
  • Refactored/extended log infrastructure: generic vs audit log enable flags, generic transport dispatcher naming, and added a log correlation UUID support path.
  • Introduced object normalization/clean-up utilities and a new cache manager intended to speed up audit child-resource detection.

Reviewed changes

Copilot reviewed 26 out of 26 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
openaev-model/src/main/java/io/openaev/config/EngineConfig.java Adds default audit-log lifecycle config fields (retention/rollover).
openaev-api/src/main/resources/application.properties Renames generic log enablement properties; updates audit log service enabled flag.
openaev-api/src/main/java/io/openaev/utils/object/ObjectSanitizationUtils.java Removes prior sanitization utility (logic moved/merged into normalization).
openaev-api/src/main/java/io/openaev/utils/object/ObjectRedactionUtils.java Adds SHA-256 hashing helper for redacting values (e.g., user email).
openaev-api/src/main/java/io/openaev/utils/object/ObjectNormalizationUtils.java Adds normalization + stripping of insignificant values for stable audit diffs/logs.
openaev-api/src/main/java/io/openaev/utils/object/ObjectDiffUtils.java Switches skip/empty-node helpers to ObjectNormalizationUtils and adds TODO.
openaev-api/src/main/java/io/openaev/utils/log/transport/GenericLogTransportUtils.java Renames generic transport interface from the previous LogTransportUtils.
openaev-api/src/main/java/io/openaev/utils/log/transport/GenericFileLogTransportUtils.java Updates to new generic log properties + interface name.
openaev-api/src/main/java/io/openaev/utils/log/transport/GenericConsoleLogTransportUtils.java Updates to new generic log properties + interface name.
openaev-api/src/main/java/io/openaev/utils/log/transport/AuditConsoleLogTransportUtils.java Minor comment change in audit console transport.
openaev-api/src/main/java/io/openaev/utils/log/dispatcher/GenericLogTransportDispatcherUtils.java Renames dispatcher to use GenericLogTransportUtils list.
openaev-api/src/main/java/io/openaev/utils/HttpReqRespUtils.java Adds HTTP method extraction fallback to thread context; request getter formatting changed.
openaev-api/src/main/java/io/openaev/service/LogService.java Renames generic log flags/type, adds log UUID support, switches to normalization utility, and updates dispatchers.
openaev-api/src/main/java/io/openaev/security/SsoRefererAuthenticationSuccessHandler.java Logs SSO login success audit event (provider from OAuth2 registration id).
openaev-api/src/main/java/io/openaev/security/SsoRefererAuthenticationFailureHandler.java Logs SSO login failure audit event; constructor updated to accept audit logger.
openaev-api/src/main/java/io/openaev/rest/user/UserApi.java Logs local login success/failure audit events.
openaev-api/src/main/java/io/openaev/rest/log/LogApi.java Updates generic log API endpoint to new AuditLogType.GENERIC + log UUID param.
openaev-api/src/main/java/io/openaev/config/ThreadPoolTaskLoggerConfig.java Captures HTTP method into thread context for async logging correlation.
openaev-api/src/main/java/io/openaev/config/security/OpenSamlConfig.java Wires the new SSO success handler constructor with the audit logger.
openaev-api/src/main/java/io/openaev/config/CachingConfig.java Registers a new Caffeine cache for audit child-resource detection.
openaev-api/src/main/java/io/openaev/config/cache/AuditChildResourceCacheManager.java New cache manager intended to cache audit child-resource detection results.
openaev-api/src/main/java/io/openaev/config/AppSecurityConfig.java Adds logout audit hook; wires SSO handlers with audit logger.
openaev-api/src/main/java/io/openaev/aop/audit_log/AuditResourceDetector.java Delegates child resource detection to cache manager; adds extensive WARN logging/TODOs.
openaev-api/src/main/java/io/openaev/aop/audit_log/AuditRequestValidator.java Updates audit enabled property key to openaev.audit-logs.service.enabled.
openaev-api/src/main/java/io/openaev/aop/audit_log/AccessControlAuditLogger.java Propagates logUUID through audit logging calls; uses request-method extraction helper.
openaev-api/src/main/java/io/openaev/aop/audit_log/AccessControlAuditLogAspect.java Generates correlation UUID per audited request and passes it through.
Comments suppressed due to low confidence (6)

openaev-api/src/main/java/io/openaev/config/cache/AuditChildResourceCacheManager.java:59

  • issue (blocking): The @Cacheable key only includes methodSignature/resourceType/parentResourceId, but the resolved value is a specific child resource (childId + snapshot). If multiple child IDs exist under the same parent, this cache can return the wrong child snapshot for later requests. Include the childId in the cache key (or avoid caching ChildResourceInfo/snapshots across requests) to prevent incorrect audit attribution.
  @Cacheable(
      value = "auditChildResource",
      key = "#methodSignature + '|' + #resourceType + '|' + #parentResourceId",
      unless = "#result == null")
  public ChildResourceInfo resolveChildResource(

openaev-api/src/main/java/io/openaev/config/cache/AuditChildResourceCacheManager.java:75

  • issue (blocking): Debug scaffolding is still present (TODO + multiple log.warn + System.out.println). This will spam logs in production and can leak identifiers. Please remove System.out.println, downgrade to debug where needed, and either fix the cache logic or remove the non-working TODO before merging.
    //TODO AUDIT: cache not working bc  parentResourceId == paramValue

    log.warn("pathVariableValues {}", pathVariableValues);
    log.warn("parentResourceId {}", parentResourceId);
    for (String paramValue : pathVariableValues) {
      if (paramValue == null || paramValue.equals(parentResourceId)) {
        continue;
      }

      System.out.println("methodSignature " + methodSignature + "; paramValue " + paramValue + "; parentResourceId " + parentResourceId);
      log.warn("methodSignature {}", methodSignature);

openaev-api/src/main/java/io/openaev/aop/audit_log/AuditResourceDetector.java:136

  • issue (blocking): detectChildResource() contains WARN-level trace logs and a TODO indicating the implementation is not working ("childInfo is always null"). This should be fixed or the incomplete code removed before merging; otherwise audit child-resource detection will silently fail and logs will be noisy.
    public ChildResourceInfo detectChildResource(ProceedingJoinPoint joinPoint, ResourceType resourceType, String parentResourceId) {
        try {
            MethodSignature sig = (MethodSignature) joinPoint.getSignature();
            String methodSignature = sig.toLongString();
            String[] pathVariableValues = extractPathVariableValues(sig, joinPoint.getArgs());

            log.warn("start detectChildResource");
            ChildResourceInfo childInfo = auditChildResourceCacheManager.resolveChildResource(methodSignature, resourceType, parentResourceId, pathVariableValues);
            log.warn("detectChildResource {}", childInfo);

            //TODO AUDIT: childInfo is always null and cache not working bc  parentResourceId == paramValue

openaev-api/src/main/java/io/openaev/utils/object/ObjectNormalizationUtils.java:103

  • issue (blocking): The JavaDoc for stripInsignificantValues() is indented as if it’s inside another block (starts mid-column). This will likely fail Spotless/google-java-format and reduces readability. Reformat the block so the JavaDoc aligns with the method declaration.
        /**
         * Recursively strips insignificant values (nulls, empty strings, empty arrays, false booleans)
         * from a {@link JsonNode} tree. Also removes fields listed in {@link #DIFF_SKIP_FIELDS}. This
         * keeps create-event audit entries concise — only meaningful, non-default values are logged.
         *
         * <p>For arrays, each element is cleaned recursively (but elements are never removed, to preserve
         * positional semantics). For objects, fields whose cleaned value is insignificant are dropped.
         *
         * @return a new, cleaned copy of the tree — the original is never mutated
         */
    public JsonNode stripInsignificantValues(JsonNode node) {

openaev-api/src/main/java/io/openaev/utils/HttpReqRespUtils.java:160

  • issue (blocking): The getCurrentRequest() method is mis-indented (extra leading spaces) which is inconsistent with the rest of the file and likely to fail Spotless. Please run formatting on this section.
    public static HttpServletRequest getCurrentRequest() {
    try {
      ServletRequestAttributes attrs = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
      return attrs != null ? attrs.getRequest() : null;
    } catch (Exception e) {
      return null;

openaev-api/src/main/java/io/openaev/rest/user/UserApi.java:81

  • issue: UserApi.login() now emits an audit auth event but still writes a UserEventService DB event (login success/failure). If the intent is to migrate to audit logging (per #5483), keeping both will duplicate events and complicate deprecation. Consider removing the UserEventService calls here (or clearly documenting why both are needed).
      if (userService.isUserPasswordValid(user, input.getPassword())) {
        userService.createUserSession(user);
        userEventService.createLoginSuccessEvent(user);
        accessControlAuditLogger.logAuthEvent("login", "success", "local", null, null);
        return user;
      }
    }
    userEventService.createLoginFailedEvent("local login", BadCredentialsException.class.getSimpleName());
    accessControlAuditLogger.logAuthEvent("login", "error", "local", BadCredentialsException.class.getSimpleName(), null);

Comment thread openaev-api/src/main/java/io/openaev/aop/audit_log/AuditResourceDetector.java Outdated
Comment thread openaev-api/src/main/java/io/openaev/utils/object/ObjectNormalizationUtils.java Outdated
Comment thread openaev-api/src/main/java/io/openaev/utils/object/ObjectNormalizationUtils.java Outdated
Comment thread openaev-api/src/main/java/io/openaev/rest/user/UserApi.java
Comment on lines +239 to +241
openaev.generic-logs.service.enabled=true
openaev.generic-logs.console.enabled=true
openaev.generic-logs.file.enabled=true
Comment thread openaev-api/src/main/java/io/openaev/utils/object/ObjectDiffUtils.java Outdated
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 7 comments.

Comments suppressed due to low confidence (3)

openaev-api/src/main/java/io/openaev/config/AppSecurityConfig.java:130

  • issue (blocking): the logout handler logs a "logout"/"success" auth event even when authentication is null (e.g., anonymous user calling /logout, expired session, etc.). This can produce misleading audit events; consider skipping when authentication == null or logging event_status="error" (and/or provider) based on the actual authentication state.
                    .addLogoutHandler(
                        (request, response, authentication) -> {
                          try {
                            accessControlAuditLogger.logAuthEvent(
                                "logout", "success", null, null, null);

openaev-api/src/main/java/io/openaev/rest/user/UserApi.java:82

  • issue (blocking): UserEventService is still used to create login failure events. If the intention is to replace legacy user_events with audit auth events (per issue #5483), this call should be removed/migrated to avoid duplicate records and to unblock removing the legacy tables.
    userEventService.createLoginFailedEvent(
        "local login", BadCredentialsException.class.getSimpleName());
    accessControlAuditLogger.logAuthEvent(
        "login", "error", "local", BadCredentialsException.class.getSimpleName(), null);

openaev-api/src/main/java/io/openaev/rest/user/UserApi.java:82

  • todo (blocking): new behavior (audit auth event emission) isn’t covered by existing UserApiTest login assertions. Add a test that mocks AccessControlAuditLogger (or LogService) and verifies logAuthEvent("login", "success", "local", ...) and logAuthEvent("login", "error", "local", ...) are called for the corresponding login outcomes (see testing.instructions.md / CodingGuidelineID:1000004).
        accessControlAuditLogger.logAuthEvent("login", "success", "local", null, null);
        return user;
      }
    }
    userEventService.createLoginFailedEvent(
        "local login", BadCredentialsException.class.getSimpleName());
    accessControlAuditLogger.logAuthEvent(
        "login", "error", "local", BadCredentialsException.class.getSimpleName(), null);

Comment on lines +35 to +39
if (authentication instanceof OAuth2AuthenticationToken oauth2Token) {
provider = oauth2Token.getAuthorizedClientRegistrationId();
}

accessControlAuditLogger.logAuthEvent("login", "success", provider, null, null);
Comment on lines 34 to +38
userEventService.createLoginFailedEvent(
request.getRequestURI(), exception.getClass().getSimpleName());

accessControlAuditLogger.logAuthEvent(
"login", "error", request.getRequestURI(), exception.getClass().getSimpleName(), null);
Comment on lines 34 to 36
userEventService.createLoginFailedEvent(
request.getRequestURI(), exception.getClass().getSimpleName());

Comment on lines +73 to +75
userService.createUserSession(user);
userEventService.createLoginSuccessEvent(user);
accessControlAuditLogger.logAuthEvent("login", "success", "local", null, null);
Comment on lines +32 to +39
// Audit: log SSO login success
try {
String provider = "sso";
if (authentication instanceof OAuth2AuthenticationToken oauth2Token) {
provider = oauth2Token.getAuthorizedClientRegistrationId();
}

accessControlAuditLogger.logAuthEvent("login", "success", provider, null, null);
Comment on lines 34 to +39
userEventService.createLoginFailedEvent(
request.getRequestURI(), exception.getClass().getSimpleName());

accessControlAuditLogger.logAuthEvent(
"login", "error", request.getRequestURI(), exception.getClass().getSimpleName(), null);

Comment on lines 120 to +134
.logout(
logout ->
logout
// Audit Log: audit handler fires first, then Spring Security's built-in
// SecurityContextLogoutHandler
// invalidates the session and clears cookies
.addLogoutHandler(
(request, response, authentication) -> {
try {
accessControlAuditLogger.logAuthEvent(
"logout", "success", null, null, null);
} catch (Exception e) {
// Never block the logout flow
}
})
@a19836 a19836 changed the title [backend] auth(audit-log): login/logout/SSO auth event hooks + tests (#5483) [backend] feat(audit-log): login/logout/SSO auth event hooks + tests (#5483) May 20, 2026
@a19836 a19836 changed the base branch from issue/5526 to main May 25, 2026 09:43
@a19836 a19836 removed the request for review from Dimfacion May 25, 2026 15:03
@a19836 a19836 marked this pull request as ready for review May 26, 2026 09:19
@a19836 a19836 requested a review from Dimfacion May 26, 2026 09:19
@a19836 a19836 requested a review from Copilot May 26, 2026 10:52
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

audit-logging filigran team use to identify PR from the Filigran team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

US.1-T.4 -> [Audit Logging][Collection: Auth] Login/logout/SSO auth event hooks + tests

2 participants