From 194d4f8632190ad13be53327e6a95747a24f694d Mon Sep 17 00:00:00 2001 From: ac892247 Date: Wed, 15 Apr 2026 13:48:55 +0200 Subject: [PATCH 1/6] do not distribute when infinispan is used Signed-off-by: ac892247 --- .../java/org/zowe/apiml/filter/LogoutHandler.java | 14 ++++++++++++-- .../org/zowe/apiml/filter/LogoutHandlerTest.java | 11 ++++++++--- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/apiml/src/main/java/org/zowe/apiml/filter/LogoutHandler.java b/apiml/src/main/java/org/zowe/apiml/filter/LogoutHandler.java index 083969cd82..4242e1d7cb 100644 --- a/apiml/src/main/java/org/zowe/apiml/filter/LogoutHandler.java +++ b/apiml/src/main/java/org/zowe/apiml/filter/LogoutHandler.java @@ -11,7 +11,9 @@ package org.zowe.apiml.filter; import com.netflix.eureka.registry.PeerAwareInstanceRegistryImpl; +import jakarta.annotation.PostConstruct; import lombok.RequiredArgsConstructor; +import org.springframework.context.ApplicationContext; import org.springframework.http.HttpHeaders; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; @@ -38,6 +40,14 @@ public class LogoutHandler implements ServerLogoutHandler { private final FailedAuthenticationWebHandler failure; private final PeerAwareInstanceRegistryImpl peerAwareInstanceRegistry; private final HttpUtils httpUtils; + private final ApplicationContext applicationContext; + + private boolean distribute; + + @PostConstruct + void init(){ + distribute = !applicationContext.containsBean("infinispanConfig"); + } @Override public Mono logout(WebFilterExchange exchange, Authentication authentication) { @@ -51,12 +61,12 @@ private Mono invalidateJwtToken(String token, WebFilterExchange exchange) ); } - if (Boolean.TRUE.equals(authenticationService.isInvalidated(token))) { + if (authenticationService.isInvalidated(token)) { return failure.onAuthenticationFailure(exchange,new TokenNotValidException("The token you are trying to logout is not valid")); } else { try { var app = peerAwareInstanceRegistry.getApplications().getRegisteredApplications(CoreService.GATEWAY.getServiceId()); - authenticationService.invalidateJwtTokenGateway(token, true, app); + authenticationService.invalidateJwtTokenGateway(token, distribute, app); } catch (TokenNotValidException e) { // TokenNotValidException thrown in cases where the format is not valid return failure.onAuthenticationFailure(exchange,new TokenFormatNotValidException(e.getMessage())); diff --git a/apiml/src/test/java/org/zowe/apiml/filter/LogoutHandlerTest.java b/apiml/src/test/java/org/zowe/apiml/filter/LogoutHandlerTest.java index 476273e13b..34962252e2 100644 --- a/apiml/src/test/java/org/zowe/apiml/filter/LogoutHandlerTest.java +++ b/apiml/src/test/java/org/zowe/apiml/filter/LogoutHandlerTest.java @@ -10,6 +10,7 @@ package org.zowe.apiml.filter; +import com.netflix.discovery.shared.Application; import com.netflix.discovery.shared.Applications; import com.netflix.eureka.registry.PeerAwareInstanceRegistryImpl; import org.junit.jupiter.api.BeforeEach; @@ -17,6 +18,7 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.context.ApplicationContext; import org.springframework.http.HttpCookie; import org.springframework.http.HttpHeaders; import org.springframework.mock.http.server.reactive.MockServerHttpRequest; @@ -25,6 +27,7 @@ import org.springframework.security.web.server.WebFilterExchange; import org.springframework.web.server.WebFilterChain; import org.zowe.apiml.handler.FailedAuthenticationWebHandler; +import org.zowe.apiml.product.constants.CoreService; import org.zowe.apiml.security.common.config.AuthConfigurationProperties; import org.zowe.apiml.security.common.token.TokenFormatNotValidException; import org.zowe.apiml.security.common.token.TokenNotValidException; @@ -46,6 +49,7 @@ class LogoutHandlerTest { @Mock private AuthenticationService authenticationService; @Mock private FailedAuthenticationWebHandler failureHandler; @Mock private PeerAwareInstanceRegistryImpl registry; + @Mock private ApplicationContext applicationContext; private HttpUtils httpUtils = new HttpUtils(new AuthConfigurationProperties()) { { readConfig(); @@ -56,7 +60,7 @@ class LogoutHandlerTest { @BeforeEach void setUp() { - logoutHandler = new LogoutHandler(authenticationService, failureHandler, registry, httpUtils); + logoutHandler = new LogoutHandler(authenticationService, failureHandler, registry, httpUtils, applicationContext); } @Test @@ -130,11 +134,12 @@ void shouldInvalidateValidTokenSuccessfully() { when(authenticationService.isInvalidated("token123")).thenReturn(false); Applications mockApplications = mock(Applications.class); when(registry.getApplications()).thenReturn(mockApplications); - + var application = mock(Application.class); + when(mockApplications.getRegisteredApplications(CoreService.GATEWAY.getServiceId())).thenReturn(application); StepVerifier.create(logoutHandler.logout(webFilterExchange, mock(Authentication.class))) .verifyComplete(); - verify(authenticationService).invalidateJwtTokenGateway(eq("token123"), eq(true), any()); + verify(authenticationService).invalidateJwtTokenGateway(eq("token123"), eq(false), eq(application)); } @Test From 5d6faebd5f9827559d75667374af9e171e332cae Mon Sep 17 00:00:00 2001 From: ac892247 Date: Fri, 17 Apr 2026 10:38:49 +0200 Subject: [PATCH 2/6] configure initial hosts Signed-off-by: ac892247 --- .github/workflows/integration-tests.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 41262afd59..9e52777860 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -208,6 +208,7 @@ jobs: APIML_SECURITY_AUTH_PROVIDER: saf APIML_DISCOVERY_ALLPEERSURLS: https://apiml-2:10011/eureka,https://apiml:10011/eureka APIML_SERVICE_HOSTNAME: apiml + CACHING_STORAGE_INFINISPAN_INITIALHOSTS: "apiml[7600],apiml-2[7600]" logbackService: ZWEAGW1 apiml-2: image: ghcr.io/balhar-jakub/apiml:${{ github.run_id }}-${{ github.run_number }} @@ -223,6 +224,7 @@ jobs: APIML_SECURITY_AUTH_PROVIDER: saf APIML_DISCOVERY_ALLPEERSURLS: https://apiml:10011/eureka,https://apiml-2:10011/eureka APIML_SERVICE_HOSTNAME: apiml-2 + CACHING_STORAGE_INFINISPAN_INITIALHOSTS: "apiml[7600],apiml-2[7600]" logbackService: ZWEAGW2 discoverable-client: image: ghcr.io/balhar-jakub/discoverable-client:${{ github.run_id }}-${{ github.run_number }} From 25137974b8f1de29f3b3f314e5893d7222951f20 Mon Sep 17 00:00:00 2001 From: ac892247 Date: Fri, 17 Apr 2026 12:48:58 +0200 Subject: [PATCH 3/6] fix styles Signed-off-by: ac892247 --- .../main/java/org/zowe/apiml/filter/LogoutHandler.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apiml/src/main/java/org/zowe/apiml/filter/LogoutHandler.java b/apiml/src/main/java/org/zowe/apiml/filter/LogoutHandler.java index 4242e1d7cb..10ef6754b9 100644 --- a/apiml/src/main/java/org/zowe/apiml/filter/LogoutHandler.java +++ b/apiml/src/main/java/org/zowe/apiml/filter/LogoutHandler.java @@ -45,7 +45,7 @@ public class LogoutHandler implements ServerLogoutHandler { private boolean distribute; @PostConstruct - void init(){ + void init() { distribute = !applicationContext.containsBean("infinispanConfig"); } @@ -62,20 +62,20 @@ private Mono invalidateJwtToken(String token, WebFilterExchange exchange) } if (authenticationService.isInvalidated(token)) { - return failure.onAuthenticationFailure(exchange,new TokenNotValidException("The token you are trying to logout is not valid")); + return failure.onAuthenticationFailure(exchange, new TokenNotValidException("The token you are trying to logout is not valid")); } else { try { var app = peerAwareInstanceRegistry.getApplications().getRegisteredApplications(CoreService.GATEWAY.getServiceId()); authenticationService.invalidateJwtTokenGateway(token, distribute, app); } catch (TokenNotValidException e) { // TokenNotValidException thrown in cases where the format is not valid - return failure.onAuthenticationFailure(exchange,new TokenFormatNotValidException(e.getMessage())); + return failure.onAuthenticationFailure(exchange, new TokenFormatNotValidException(e.getMessage())); } catch (AuthenticationException e) { return failure.onAuthenticationFailure(exchange, e); } catch (Exception e) { // Catch any issue like ServiceNotAccessibleException, throw TokenNotValidException // so a 401 is returned. Returning 500 gives information about the system and is thus avoided. - return failure.onAuthenticationFailure(exchange, new TokenNotValidException("Error while logging out token")); + return failure.onAuthenticationFailure(exchange, new TokenNotValidException("Error while logging out token")); } return Mono.empty(); } From 446c292ab25d64419406183cb958f37bd8b71422 Mon Sep 17 00:00:00 2001 From: ac892247 Date: Fri, 17 Apr 2026 13:22:51 +0200 Subject: [PATCH 4/6] test: update LogoutHandlerTest to cover distribute flag logic Made-with: Cursor --- .../zowe/apiml/filter/LogoutHandlerTest.java | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/apiml/src/test/java/org/zowe/apiml/filter/LogoutHandlerTest.java b/apiml/src/test/java/org/zowe/apiml/filter/LogoutHandlerTest.java index 34962252e2..ddb166e52b 100644 --- a/apiml/src/test/java/org/zowe/apiml/filter/LogoutHandlerTest.java +++ b/apiml/src/test/java/org/zowe/apiml/filter/LogoutHandlerTest.java @@ -123,7 +123,7 @@ void shouldCallFailureHandlerOnUnexpectedException() { } @Test - void shouldInvalidateValidTokenSuccessfully() { + void shouldInvalidateValidTokenSuccessfully_WithInfinispan() { var request = MockServerHttpRequest.get("/logout") .header(HttpHeaders.AUTHORIZATION, "Bearer token123") .build(); @@ -131,6 +131,9 @@ void shouldInvalidateValidTokenSuccessfully() { WebFilterChain mockChain = mock(WebFilterChain.class); var webFilterExchange = new WebFilterExchange(exchange, mockChain); + when(applicationContext.containsBean("infinispanConfig")).thenReturn(true); + logoutHandler.init(); + when(authenticationService.isInvalidated("token123")).thenReturn(false); Applications mockApplications = mock(Applications.class); when(registry.getApplications()).thenReturn(mockApplications); @@ -142,6 +145,29 @@ void shouldInvalidateValidTokenSuccessfully() { verify(authenticationService).invalidateJwtTokenGateway(eq("token123"), eq(false), eq(application)); } + @Test + void shouldInvalidateValidTokenSuccessfully_WithoutInfinispan() { + var request = MockServerHttpRequest.get("/logout") + .header(HttpHeaders.AUTHORIZATION, "Bearer token123") + .build(); + var exchange = MockServerWebExchange.from(request); + WebFilterChain mockChain = mock(WebFilterChain.class); + var webFilterExchange = new WebFilterExchange(exchange, mockChain); + + when(applicationContext.containsBean("infinispanConfig")).thenReturn(false); + logoutHandler.init(); + + when(authenticationService.isInvalidated("token123")).thenReturn(false); + Applications mockApplications = mock(Applications.class); + when(registry.getApplications()).thenReturn(mockApplications); + var application = mock(Application.class); + when(mockApplications.getRegisteredApplications(CoreService.GATEWAY.getServiceId())).thenReturn(application); + StepVerifier.create(logoutHandler.logout(webFilterExchange, mock(Authentication.class))) + .verifyComplete(); + + verify(authenticationService).invalidateJwtTokenGateway(eq("token123"), eq(true), eq(application)); + } + @Test void givenCookie_whenLogout_thenRemoveCookie() { var request = MockServerHttpRequest.get("/logout") From 6a04846667c61ebe62bac450af134313b2b5cd46 Mon Sep 17 00:00:00 2001 From: ac892247 Date: Tue, 21 Apr 2026 12:28:33 +0200 Subject: [PATCH 5/6] jgroups def Signed-off-by: ac892247 --- .github/workflows/integration-tests.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 9e52777860..aebbfdb968 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -210,6 +210,9 @@ jobs: APIML_SERVICE_HOSTNAME: apiml CACHING_STORAGE_INFINISPAN_INITIALHOSTS: "apiml[7600],apiml-2[7600]" logbackService: ZWEAGW1 + JGROUPS_BIND_PORT: 7600 + JGROUPS_BIND_ADDRESS: apiml + JGROUPS_KEYEXCHANGE_PORT: 7601 apiml-2: image: ghcr.io/balhar-jakub/apiml:${{ github.run_id }}-${{ github.run_number }} env: @@ -226,6 +229,9 @@ jobs: APIML_SERVICE_HOSTNAME: apiml-2 CACHING_STORAGE_INFINISPAN_INITIALHOSTS: "apiml[7600],apiml-2[7600]" logbackService: ZWEAGW2 + JGROUPS_BIND_PORT: 7600 + JGROUPS_BIND_ADDRESS: apiml + JGROUPS_KEYEXCHANGE_PORT: 7601 discoverable-client: image: ghcr.io/balhar-jakub/discoverable-client:${{ github.run_id }}-${{ github.run_number }} env: From f5e0c86c282c81b3939f2863a58e77b7851c15d8 Mon Sep 17 00:00:00 2001 From: ac892247 Date: Tue, 21 Apr 2026 12:51:15 +0200 Subject: [PATCH 6/6] typo in hostname Signed-off-by: ac892247 --- .github/workflows/integration-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index aebbfdb968..0104ae9a66 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -230,7 +230,7 @@ jobs: CACHING_STORAGE_INFINISPAN_INITIALHOSTS: "apiml[7600],apiml-2[7600]" logbackService: ZWEAGW2 JGROUPS_BIND_PORT: 7600 - JGROUPS_BIND_ADDRESS: apiml + JGROUPS_BIND_ADDRESS: apiml-2 JGROUPS_KEYEXCHANGE_PORT: 7601 discoverable-client: image: ghcr.io/balhar-jakub/discoverable-client:${{ github.run_id }}-${{ github.run_number }}