From 9d046255baeba2a4f92b1cd9f12e0ccc0a420edf Mon Sep 17 00:00:00 2001 From: Charles Graham Date: Tue, 7 Apr 2026 23:36:41 -0500 Subject: [PATCH 1/3] openidconnect security scheme swagger/openapi fix + test --- .../java/cwms/cda/security/OpenIDConfig.java | 19 +++++---- .../cwms/cda/security/OpenIDConfigTest.java | 42 +++++++++++++++++++ 2 files changed, 53 insertions(+), 8 deletions(-) create mode 100644 cwms-data-api/src/test/java/cwms/cda/security/OpenIDConfigTest.java diff --git a/cwms-data-api/src/main/java/cwms/cda/security/OpenIDConfig.java b/cwms-data-api/src/main/java/cwms/cda/security/OpenIDConfig.java index 3e967aed0..44fe78f28 100644 --- a/cwms-data-api/src/main/java/cwms/cda/security/OpenIDConfig.java +++ b/cwms-data-api/src/main/java/cwms/cda/security/OpenIDConfig.java @@ -56,25 +56,28 @@ public URL getJwksUrl() { return jwksUrl; } - public SecurityScheme getScheme() { - - + static SecurityScheme buildScheme(String wellKnownUrl, String clientId, String idpHint) { SecurityScheme scheme = new SecurityScheme().type(Type.OPENIDCONNECT) - .openIdConnectUrl(wellKnown.toString()) - .scheme("openid"); - if (idp_hint != null) + .openIdConnectUrl(wellKnownUrl); + if (idpHint != null) { Map hint = new HashMap<>(); hint.put("query-parameter", "kc_idp_hint"); ArrayList values = new ArrayList<>(); - for (String value: idp_hint.split(",")) { + for (String value: idpHint.split(",")) { values.add(value.trim()); } hint.put("values", values); scheme.addExtension("x-kc_idp_hint", hint); } - scheme.addExtension("x-oidc-client-id", client_id); + scheme.addExtension("x-oidc-client-id", clientId); + return scheme; + } + + public SecurityScheme getScheme() { + + SecurityScheme scheme = buildScheme(wellKnown.toString(), client_id, idp_hint); return scheme; } } diff --git a/cwms-data-api/src/test/java/cwms/cda/security/OpenIDConfigTest.java b/cwms-data-api/src/test/java/cwms/cda/security/OpenIDConfigTest.java new file mode 100644 index 000000000..cdce8c504 --- /dev/null +++ b/cwms-data-api/src/test/java/cwms/cda/security/OpenIDConfigTest.java @@ -0,0 +1,42 @@ +package cwms.cda.security; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import io.swagger.v3.oas.models.security.SecurityScheme; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.Test; + +class OpenIDConfigTest { + + @Test + void buildSchemeUsesWellKnownDiscoveryUrlWithoutHttpAuthScheme() { + SecurityScheme scheme = OpenIDConfig.buildScheme( + "https://identityc.sec.usace.army.mil/auth/realms/cwbi/.well-known/openid-configuration", + "cwms", + "federation-eams, login.gov" + ); + + assertEquals(SecurityScheme.Type.OPENIDCONNECT, scheme.getType()); + assertEquals( + "https://identityc.sec.usace.army.mil/auth/realms/cwbi/.well-known/openid-configuration", + scheme.getOpenIdConnectUrl() + ); + assertTrue(scheme.getScheme() == null || scheme.getScheme().isEmpty()); + assertNotNull(scheme.getExtensions()); + assertEquals("cwms", scheme.getExtensions().get("x-oidc-client-id")); + + @SuppressWarnings("unchecked") + Map hint = (Map) scheme.getExtensions().get("x-kc_idp_hint"); + assertNotNull(hint); + assertEquals("kc_idp_hint", hint.get("query-parameter")); + + @SuppressWarnings("unchecked") + List values = (List) hint.get("values"); + assertEquals(List.of("federation-eams", "login.gov"), values); + assertFalse(scheme.getExtensions().containsKey("flows")); + } +} From be4b8e321b58558838939ca876fe3f682d9d0892 Mon Sep 17 00:00:00 2001 From: Charles Graham Date: Tue, 7 Apr 2026 23:37:09 -0500 Subject: [PATCH 2/3] Add FINE logging to help identify issuer mismatches --- .../cwms/cda/security/OpenIdConnectIdentitityProvider.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cwms-data-api/src/main/java/cwms/cda/security/OpenIdConnectIdentitityProvider.java b/cwms-data-api/src/main/java/cwms/cda/security/OpenIdConnectIdentitityProvider.java index 7287c8aa8..48d1c8940 100644 --- a/cwms-data-api/src/main/java/cwms/cda/security/OpenIdConnectIdentitityProvider.java +++ b/cwms-data-api/src/main/java/cwms/cda/security/OpenIdConnectIdentitityProvider.java @@ -115,6 +115,10 @@ private DataApiPrincipal getUserFromToken(Context ctx) throws CwmsAuthException throw new CwmsAuthException("Not Authorized",HttpServletResponse.SC_UNAUTHORIZED); } } catch (NumberFormatException | JwtException ex) { + log.atFine().withCause(ex).log( + "JWT validation failed for bearer token from issuer configuration '%s'", + System.getProperty(ISSUER_PROPERTY, System.getenv(ISSUER_PROPERTY)) + ); throw new CwmsAuthException("JWT not valid",ex,HttpServletResponse.SC_UNAUTHORIZED); } } From 96b270d38a5c27aec00b998e3aa7e61cb7af43d0 Mon Sep 17 00:00:00 2001 From: Charles Graham Date: Tue, 7 Apr 2026 23:38:37 -0500 Subject: [PATCH 3/3] Add SWT to users seed, keycloak has SWT user --- compose_files/sql/users.sql | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compose_files/sql/users.sql b/compose_files/sql/users.sql index e8bacf1e3..e9d8329a9 100644 --- a/compose_files/sql/users.sql +++ b/compose_files/sql/users.sql @@ -19,6 +19,7 @@ begin cwms_sec.add_cwms_user('m5hectest', null, 'SWT'); cwms_sec.add_user_to_group('m5hectest', 'All Users', 'SWT'); cwms_sec.add_user_to_group('m5hectest', 'CWMS Users', 'SWT'); + cwms_sec.add_user_to_group('m5hectest', 'TS ID Creator', 'SWT'); cwms_sec.add_cwms_user('q0hectest', null, 'SWT'); cwms_sec.add_user_to_group('q0hectest', 'All Users', 'SWT'); cwms_sec.add_user_to_group('q0hectest', 'CWMS Users', 'SWT'); @@ -50,6 +51,7 @@ begin cwms_sec.add_cwms_user('m5hectest',NULL,'SWT'); cwms_sec.add_user_to_group('m5hectest','All Users', 'SWT'); cwms_sec.add_user_to_group('m5hectest','CWMS Users', 'SWT'); + cwms_sec.add_user_to_group('m5hectest','TS ID Creator', 'SWT'); execute immediate 'grant execute on cwms_20.cwms_upass to web_user'; @@ -60,4 +62,4 @@ begin end; / -quit; \ No newline at end of file +quit;