Skip to content

fix: detect z/OSMF expired password in 401 response — GH#4083#4800

Open
balhar-jakub wants to merge 1 commit into
v3.x.xfrom
hermes/gh4083
Open

fix: detect z/OSMF expired password in 401 response — GH#4083#4800
balhar-jakub wants to merge 1 commit into
v3.x.xfrom
hermes/gh4083

Conversation

@balhar-jakub

Copy link
Copy Markdown
Member

Closes #4083

Summary

When z/OSMF returns HTTP 401 for an expired password, the response body now contains SAF indicators (SAFReturnCode=8, SAFReasonCode=24) when z/OSMF is configured for detailed auth messages. Previously, all 401s were blindly mapped to BadCredentialsException (ZWEAG120E). This fix inspects the 401 response body and throws ZosAuthenticationException with EMVSEXPIRE (ZWEAT412E) when expired password is detected.

Changes

  • Added private helper in ZosmfService.java — parses 401 response body for SAF expired-password indicators
  • Modified to catch HttpClientErrorException.Unauthorized before generic RuntimeException
  • 4 unit tests: expired password → ZosAuthenticationException, invalid credentials → BadCredentialsException, empty body → BadCredentialsException, malformed JSON → BadCredentialsException

QA review + CI gate to follow

Add isExpiredPassword() helper that parses z/OSMF 401 response body
for SAF messages indicating an expired password. Intercept
HttpClientErrorException.Unauthorized in issueAuthenticationRequest()
before the generic RuntimeException catch. When expired password is
detected, throw ZosAuthenticationException(EMVSEXPIRE, errno 168).

Closes #4083

Signed-off-by: Jakub Balhar <jakub.balhar@broadcom.com>
@balhar-jakub

Copy link
Copy Markdown
Member Author

QA + Security Review — PR #4800 (GH#4083)

Verdict: APPROVED

Architecture Compliance ✅

Implementation matches architect's solution design exactly:

  • HttpClientErrorException.Unauthorized intercepted in issueAuthenticationRequest() (not in handleExceptionOnCall())
  • ✅ Response body parsed for safMessages array with SAFReturnCode=8 + SAFReasonCode=24
  • ✅ Throws ZosAuthenticationException(EMVSEXPIRE) when detected
  • ✅ Falls back to handleExceptionOnCall()BadCredentialsException for other 401 cases
  • ✅ Files modified: ZosmfService.java + ZosmfServiceTest.java only (matches design)
  • ✅ Zero config changes, zero new DTOs

Pavel's Lens — All 8 Rules ✅

Rule Status Notes
1. Config Consistency ✅ N/A No config changes
2. Deduplication Reuses existing securityObjectMapper, PlatformReturned.builder(), PlatformPwdErrno.EMVSEXPIRE — no new constants or DTOs
3. Null Safety isExpiredPassword() guards null/empty responseBody, null-checks safMessages, safReturnCode, safReasonCode before accessing
4. Test Parametrization 4 tests cover distinct scenarios (expired password, invalid creds, empty body, malformed JSON). Not trivially parametrizable — different assertions (ZosAuthenticationException vs BadCredentialsException)
5. Security Boundaries Debug log only includes e1.getMessage() (no response body or credentials). SAF codes are validated against specific integer values, not string patterns
6. z/OS Awareness Fallback chain handles z/OSMF not configured for detailed info (empty body → current behavior). No race conditions. Jackson parsing is stateless
7. Log Quality Debug-level logging with error message context. No sensitive data in logs
8. TODO Tracking No new TODOs introduced. Existing TODOs at lines 397/412 are pre-existing (issue #2995, separate concern)

Code Quality Notes

  • Exception catch ordering correct: HttpClientErrorException.Unauthorized before generic RuntimeException
  • isExpiredPassword() is stateless and thread-safe
  • asInt() default returns 0 for non-integer nodes — safe because non-8/non-24 values won't match
  • Constructor test setup correctly passes mock restTemplate as restTemplateWithoutKeystore parameter

CI Status

  • DCO: ✅ pass
  • WIP: ✅ pass
  • CITestsZaas: ✅ pass
  • All CITests*: ✅ pass (40+ jobs)
  • BuildAndTest: ❌ flake in gateway-service:GatewayServiceApplicationTest (Discovery connection refused) — unrelated to this change. Re-triggered.

Files Reviewed

  • zaas-service/src/main/java/.../zosmf/ZosmfService.java — +41 lines (isExpiredPassword helper + catch block)
  • zaas-service/src/test/java/.../zosmf/ZosmfServiceTest.java — +112 lines (4 test cases in GivenExpiredPasswordResponse nested class)

@sonarqubecloud

Copy link
Copy Markdown

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

Labels

Sensitive Sensitive change that requires peer review size/L

Projects

Development

Successfully merging this pull request may close these issues.

Login via APIML gateway returns ZWEAG120E instead of ZWEAT412E for expired password using auth zosmf

1 participant