From 8fa0f7020811cd12c2d9c83471517bb9875e06e5 Mon Sep 17 00:00:00 2001 From: Richard Salac Date: Thu, 30 Jan 2025 10:02:21 +0100 Subject: [PATCH 01/11] fix: replace default x509 with categorize certs filter in zaas Signed-off-by: Richard Salac --- .../security/SecurityConfiguration.java | 2 +- .../common/auth/saf/SafResourceAccessSaf.java | 5 + .../common/filter/CategorizeCertsFilter.java | 24 ++- .../common/login/X509AuthAwareFilter.java | 11 +- ...9ForwardingAwareAuthenticationFilter.java} | 16 +- .../auth/saf/SafResourceAccessSafTest.java | 14 +- .../filter/CategorizeCertsFilterTest.java | 44 +++++- .../pat/AccessTokenServiceTest.java | 2 +- ...eTest.java => ZaasSchemeNegativeTest.java} | 12 +- .../config/NewSecurityConfiguration.java | 142 ++++++++---------- .../zaas/zaas/ZaasAuthenticationFilter.java | 9 +- .../org/zowe/apiml/acceptance/ZaasTest.java | 2 +- ...wardingAwareAuthenticationFilterTest.java} | 22 +-- .../zaas/ZaasAuthenticationFilterTest.java | 4 + .../zaas/zaas/ZaasExceptionHandlerTest.java | 25 +++ .../src/test/resources/application.yml | 2 +- 16 files changed, 214 insertions(+), 122 deletions(-) rename apiml-security-common/src/main/java/org/zowe/apiml/security/common/login/{X509AuthenticationFilter.java => X509ForwardingAwareAuthenticationFilter.java} (85%) rename integration-tests/src/test/java/org/zowe/apiml/integration/zaas/{ZaasNegativeTest.java => ZaasSchemeNegativeTest.java} (92%) rename zaas-service/src/test/java/org/zowe/apiml/zaas/security/config/{X509AuthenticationFilterTest.java => X509ForwardingAwareAuthenticationFilterTest.java} (83%) diff --git a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/security/SecurityConfiguration.java b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/security/SecurityConfiguration.java index 08b20773f3..5b339f50ae 100644 --- a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/security/SecurityConfiguration.java +++ b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/security/SecurityConfiguration.java @@ -130,7 +130,7 @@ private UserDetailsService x509UserDetailsService() { } private CategorizeCertsFilter reversedCategorizeCertFilter() { - CategorizeCertsFilter out = new CategorizeCertsFilter(publicKeyCertificatesBase64, certificateValidator); + CategorizeCertsFilter out = new CategorizeCertsFilter(publicKeyCertificatesBase64, certificateValidator, handlerInitializer.getUnAuthorizedHandler().getHandler(), false); out.setCertificateForClientAuth(crt -> out.getPublicKeyCertificatesBase64().contains(CategorizeCertsFilter.base64EncodePublicKey(crt))); out.setApimlCertificate(crt -> !out.getPublicKeyCertificatesBase64().contains(CategorizeCertsFilter.base64EncodePublicKey(crt))); return out; diff --git a/apiml-security-common/src/main/java/org/zowe/apiml/security/common/auth/saf/SafResourceAccessSaf.java b/apiml-security-common/src/main/java/org/zowe/apiml/security/common/auth/saf/SafResourceAccessSaf.java index 44b47d41eb..6d1429f0b5 100644 --- a/apiml-security-common/src/main/java/org/zowe/apiml/security/common/auth/saf/SafResourceAccessSaf.java +++ b/apiml-security-common/src/main/java/org/zowe/apiml/security/common/auth/saf/SafResourceAccessSaf.java @@ -11,6 +11,7 @@ package org.zowe.apiml.security.common.auth.saf; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; import org.springframework.security.core.Authentication; import java.lang.invoke.MethodHandle; @@ -96,6 +97,10 @@ private boolean checkPermission(String userId, String resourceType, String resou @Override public boolean hasSafResourceAccess(Authentication authentication, String resourceClass, String resourceName, String accessLevel) { String userid = authentication.getName(); + if (StringUtils.isEmpty(userid) || userid.length() > 8) { + log.debug("UserId {} is not valid for SAF permissions check", userid); + return false; + } AccessLevel level = AccessLevel.valueOf(accessLevel); log.debug("Evaluating access of user {} to resource {} in class {} level {}", userid, resourceClass, resourceName, level); return checkPermission(userid, resourceClass, resourceName, level.getValue(), true); diff --git a/apiml-security-common/src/main/java/org/zowe/apiml/security/common/filter/CategorizeCertsFilter.java b/apiml-security-common/src/main/java/org/zowe/apiml/security/common/filter/CategorizeCertsFilter.java index da94e26914..e597afcc6e 100644 --- a/apiml-security-common/src/main/java/org/zowe/apiml/security/common/filter/CategorizeCertsFilter.java +++ b/apiml-security-common/src/main/java/org/zowe/apiml/security/common/filter/CategorizeCertsFilter.java @@ -22,9 +22,11 @@ import lombok.Setter; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; +import org.springframework.security.authentication.InsufficientAuthenticationException; import org.springframework.web.filter.OncePerRequestFilter; import org.zowe.apiml.message.log.ApimlLogger; import org.zowe.apiml.product.logging.annotations.InjectApimlLogger; +import org.zowe.apiml.security.common.error.AuthExceptionHandler; import org.zowe.apiml.security.common.verify.CertificateValidator; import java.io.ByteArrayInputStream; @@ -55,6 +57,14 @@ public class CategorizeCertsFilter extends OncePerRequestFilter { private final CertificateValidator certificateValidator; + private final AuthExceptionHandler authExceptionHandler; + + private final boolean rejectIfZoweCertificateMissing; + + public CategorizeCertsFilter(Set publicKeyCertificatesBase64, CertificateValidator certificateValidator, AuthExceptionHandler authExceptionHandler) { + this(publicKeyCertificatesBase64, certificateValidator, authExceptionHandler, true); + } + /** * Get certificates from request (if exists), separate them (to use only APIML certificate to request sign and * other for authentication) and store again into request. @@ -64,10 +74,11 @@ public class CategorizeCertsFilter extends OncePerRequestFilter { * in the default attribute. * * @param request Request to filter certificates + * @throws InsufficientAuthenticationException Optionally, if the Zowe server certificate is missing and rejectIfZoweCertificateMissing is set to true */ private void categorizeCerts(ServletRequest request) { X509Certificate[] certs = (X509Certificate[]) request.getAttribute(ATTRNAME_JAKARTA_SERVLET_REQUEST_X509_CERTIFICATE); - if (certs != null) { + if (certs != null && certs.length > 0) { Optional clientCert = getClientCertFromHeader((HttpServletRequest) request); if (certificateValidator.isForwardingEnabled() && certificateValidator.isTrusted(certs) && clientCert.isPresent()) { certificateValidator.updateAPIMLPublicKeyCertificates(certs); @@ -81,7 +92,12 @@ private void categorizeCerts(ServletRequest request) { } log.debug(LOG_FORMAT_FILTERING_CERTIFICATES, ATTRNAME_CLIENT_AUTH_X509_CERTIFICATE, request.getAttribute(ATTRNAME_CLIENT_AUTH_X509_CERTIFICATE)); + + } else if (rejectIfZoweCertificateMissing) { + log.debug("No X509 certificate found in request attribute {}", ATTRNAME_JAKARTA_SERVLET_REQUEST_X509_CERTIFICATE); + throw new InsufficientAuthenticationException("No Zowe Server X509 certificate found in request"); } + } private Optional getClientCertFromHeader(HttpServletRequest request) { @@ -138,7 +154,11 @@ public Enumeration getHeaders(String name) { **/ @Override protected void doFilterInternal(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull FilterChain filterChain) throws ServletException, IOException { - categorizeCerts(request); + try { + categorizeCerts(request); + } catch (InsufficientAuthenticationException ex) { + authExceptionHandler.handleException(request, response, ex); + } filterChain.doFilter(mutate(request), response); } diff --git a/apiml-security-common/src/main/java/org/zowe/apiml/security/common/login/X509AuthAwareFilter.java b/apiml-security-common/src/main/java/org/zowe/apiml/security/common/login/X509AuthAwareFilter.java index 31e384ccbf..015937b9d1 100644 --- a/apiml-security-common/src/main/java/org/zowe/apiml/security/common/login/X509AuthAwareFilter.java +++ b/apiml-security-common/src/main/java/org/zowe/apiml/security/common/login/X509AuthAwareFilter.java @@ -10,22 +10,21 @@ package org.zowe.apiml.security.common.login; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.authentication.AuthenticationFailureHandler; - -import jakarta.servlet.FilterChain; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; import java.io.IOException; -public class X509AuthAwareFilter extends X509AuthenticationFilter { +public class X509AuthAwareFilter extends X509ForwardingAwareAuthenticationFilter { private final AuthenticationFailureHandler failureHandler; public X509AuthAwareFilter(String endpoint, AuthenticationFailureHandler failureHandler, AuthenticationProvider authenticationProvider) { diff --git a/apiml-security-common/src/main/java/org/zowe/apiml/security/common/login/X509AuthenticationFilter.java b/apiml-security-common/src/main/java/org/zowe/apiml/security/common/login/X509ForwardingAwareAuthenticationFilter.java similarity index 85% rename from apiml-security-common/src/main/java/org/zowe/apiml/security/common/login/X509AuthenticationFilter.java rename to apiml-security-common/src/main/java/org/zowe/apiml/security/common/login/X509ForwardingAwareAuthenticationFilter.java index b38c41f656..002a99c183 100644 --- a/apiml-security-common/src/main/java/org/zowe/apiml/security/common/login/X509AuthenticationFilter.java +++ b/apiml-security-common/src/main/java/org/zowe/apiml/security/common/login/X509ForwardingAwareAuthenticationFilter.java @@ -10,28 +10,28 @@ package org.zowe.apiml.security.common.login; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.core.Authentication; import org.springframework.security.web.authentication.AuthenticationSuccessHandler; import org.zowe.apiml.security.common.token.X509AuthenticationToken; -import jakarta.servlet.FilterChain; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.security.cert.X509Certificate; @Slf4j -public class X509AuthenticationFilter extends NonCompulsoryAuthenticationProcessingFilter { +public class X509ForwardingAwareAuthenticationFilter extends NonCompulsoryAuthenticationProcessingFilter { private final AuthenticationProvider authenticationProvider; private final AuthenticationSuccessHandler successHandler; - public X509AuthenticationFilter(String endpoint, - AuthenticationSuccessHandler successHandler, - AuthenticationProvider authenticationProvider) { + public X509ForwardingAwareAuthenticationFilter(String endpoint, + AuthenticationSuccessHandler successHandler, + AuthenticationProvider authenticationProvider) { super(endpoint); this.authenticationProvider = authenticationProvider; this.successHandler = successHandler; diff --git a/apiml-security-common/src/test/java/org/zowe/apiml/security/common/auth/saf/SafResourceAccessSafTest.java b/apiml-security-common/src/test/java/org/zowe/apiml/security/common/auth/saf/SafResourceAccessSafTest.java index 00d8bb5530..c47f067db6 100644 --- a/apiml-security-common/src/test/java/org/zowe/apiml/security/common/auth/saf/SafResourceAccessSafTest.java +++ b/apiml-security-common/src/test/java/org/zowe/apiml/security/common/auth/saf/SafResourceAccessSafTest.java @@ -16,14 +16,15 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.NullSource; +import org.junit.jupiter.params.provider.ValueSource; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.*; @ExtendWith(MockitoExtension.class) class SafResourceAccessSafTest { @@ -88,6 +89,15 @@ void testHasSafResourceAccess_whenNoResponse_thenTrue() { assertTrue(safResourceAccessVerifying.hasSafResourceAccess(authentication, CLASS, RESOURCE, LEVEL.name())); } + @ParameterizedTest + @NullSource + @ValueSource(strings = {"", "tooLongUserId"}) + void testInvalidUserIds_thenSkipped(String userId) { + var auth = new UsernamePasswordAuthenticationToken(userId, ""); + assertDoesNotThrow(() -> safResourceAccessVerifying.hasSafResourceAccess(auth, CLASS, RESOURCE, LEVEL.name())); + verify(checkPermissionMock, never()).checkPermission(any(), any(), any(), anyInt()); + } + @Builder public static class TestPlatformReturned { diff --git a/apiml-security-common/src/test/java/org/zowe/apiml/security/common/filter/CategorizeCertsFilterTest.java b/apiml-security-common/src/test/java/org/zowe/apiml/security/common/filter/CategorizeCertsFilterTest.java index c71467b3c4..d04c20a26c 100644 --- a/apiml-security-common/src/test/java/org/zowe/apiml/security/common/filter/CategorizeCertsFilterTest.java +++ b/apiml-security-common/src/test/java/org/zowe/apiml/security/common/filter/CategorizeCertsFilterTest.java @@ -10,18 +10,21 @@ package org.zowe.apiml.security.common.filter; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; import org.springframework.mock.web.MockFilterChain; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.security.authentication.InsufficientAuthenticationException; +import org.zowe.apiml.security.common.error.AuthExceptionHandler; import org.zowe.apiml.security.common.utils.X509Utils; import org.zowe.apiml.security.common.verify.CertificateValidator; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; @@ -71,6 +74,7 @@ class CategorizeCertsFilterTest { private X509Certificate[] clientCerts; private CertificateValidator certificateValidator; + private AuthExceptionHandler authExceptionHandler; @BeforeAll public static void init() throws CertificateException { @@ -80,13 +84,15 @@ public static void init() throws CertificateException { } @BeforeEach - public void setUp() { + public void setUp() throws ServletException { request = new MockHttpServletRequest(); response = new MockHttpServletResponse(); chain = new MockFilterChain(); certificateValidator = mock(CertificateValidator.class); + authExceptionHandler = mock(AuthExceptionHandler.class); when(certificateValidator.isForwardingEnabled()).thenReturn(false); when(certificateValidator.isTrusted(any())).thenReturn(false); + doNothing().when(authExceptionHandler).handleException(any(), any(), any()); } @Nested @@ -94,7 +100,7 @@ class GivenNoPublicKeysInFilter { @BeforeEach void setUp() { - filter = new CategorizeCertsFilter(new HashSet<>(), certificateValidator); + filter = new CategorizeCertsFilter(new HashSet<>(), certificateValidator, authExceptionHandler); } @Nested @@ -309,6 +315,7 @@ class WhenOtherHeadersInRequest { private static final String COMMON_HEADER = "User-Agent"; private static final String COMMON_HEADER_VALUE = "dummy"; + @BeforeEach void setUp() { request.addHeader(COMMON_HEADER, COMMON_HEADER_VALUE); @@ -336,7 +343,7 @@ void setUp() { X509Utils.correctBase64("apimlCert1"), X509Utils.correctBase64("apimlCertCA") )); - filter = new CategorizeCertsFilter(serverCertChain, certificateValidator); + filter = new CategorizeCertsFilter(serverCertChain, certificateValidator, authExceptionHandler); } @Nested @@ -486,4 +493,31 @@ void thenClientCertHeaderIgnored() throws ServletException, IOException { } } } + + @Nested + class WhenNoZoweServerCertificateProvided { + + private MockHttpServletRequest requestWithNoCertificates; + + @BeforeEach + void setup() { + requestWithNoCertificates = new MockHttpServletRequest(); + } + + @Test + void givenRejectOnMissingZoweServerCertificateEnabled_thenThrowException() throws ServletException, IOException { + var rejectOnFilter = new CategorizeCertsFilter(new HashSet<>(), certificateValidator, authExceptionHandler); + ArgumentCaptor argument = ArgumentCaptor.forClass(RuntimeException.class); + rejectOnFilter.doFilter(requestWithNoCertificates, response, chain); + verify(authExceptionHandler, times(1)).handleException(any(), any(), argument.capture()); + assertInstanceOf(InsufficientAuthenticationException.class, argument.getValue()); + } + + @Test + void givenRejectOnMissingZoweServerCertificateDisabled_thenDoNotThrowException() throws ServletException, IOException { + var rejectOffFilter = new CategorizeCertsFilter(new HashSet<>(), certificateValidator, authExceptionHandler, false); + rejectOffFilter.doFilter(request, response, chain); + verify(authExceptionHandler, never()).handleException(any(), any(), any()); + } + } } diff --git a/integration-tests/src/test/java/org/zowe/apiml/integration/authentication/pat/AccessTokenServiceTest.java b/integration-tests/src/test/java/org/zowe/apiml/integration/authentication/pat/AccessTokenServiceTest.java index f91606383d..b26035cb53 100644 --- a/integration-tests/src/test/java/org/zowe/apiml/integration/authentication/pat/AccessTokenServiceTest.java +++ b/integration-tests/src/test/java/org/zowe/apiml/integration/authentication/pat/AccessTokenServiceTest.java @@ -218,7 +218,7 @@ void givenNotAuthorizedCall_thenDontAllowToRevokeTokensForUser() { requestBody.put("userId", SecurityUtils.USERNAME); given().contentType(ContentType.JSON).config(SslContext.clientCertApiml).body(requestBody) .when().delete(REVOKE_FOR_USER_ENDPOINT) - .then().statusCode(403); + .then().statusCode(401); // validate after revocation rule given().contentType(ContentType.JSON).body(bodyContent).when() .post(VALIDATE_ENDPOINT) diff --git a/integration-tests/src/test/java/org/zowe/apiml/integration/zaas/ZaasNegativeTest.java b/integration-tests/src/test/java/org/zowe/apiml/integration/zaas/ZaasSchemeNegativeTest.java similarity index 92% rename from integration-tests/src/test/java/org/zowe/apiml/integration/zaas/ZaasNegativeTest.java rename to integration-tests/src/test/java/org/zowe/apiml/integration/zaas/ZaasSchemeNegativeTest.java index 10e9ebc9f9..417de3b625 100644 --- a/integration-tests/src/test/java/org/zowe/apiml/integration/zaas/ZaasNegativeTest.java +++ b/integration-tests/src/test/java/org/zowe/apiml/integration/zaas/ZaasSchemeNegativeTest.java @@ -40,7 +40,7 @@ import static org.zowe.apiml.util.SecurityUtils.*; @ZaasTest -public class ZaasNegativeTest { +public class ZaasSchemeNegativeTest { private final static String APPLICATION_NAME = ConfigReader.environmentConfiguration().getDiscoverableClientConfiguration().getApplId(); private final static SafIdtConfiguration SAFIDT_CONF = ConfigReader.environmentConfiguration().getSafIdtConfiguration(); @@ -115,7 +115,7 @@ void setUpCertificateAndToken() { } @ParameterizedTest - @MethodSource("org.zowe.apiml.integration.zaas.ZaasNegativeTest#provideZaasEndpoints") + @MethodSource("org.zowe.apiml.integration.zaas.ZaasSchemeNegativeTest#provideZaasEndpoints") void givenNoToken(URI uri, RequestSpecification requestSpecification) { //@formatter:off requestSpecification @@ -127,7 +127,7 @@ void givenNoToken(URI uri, RequestSpecification requestSpecification) { } @ParameterizedTest - @MethodSource("org.zowe.apiml.integration.zaas.ZaasNegativeTest#provideZaasEndpointsWithAllTokens") + @MethodSource("org.zowe.apiml.integration.zaas.ZaasSchemeNegativeTest#provideZaasEndpointsWithAllTokens") void givenInvalidToken(URI uri, RequestSpecification requestSpecification, String token) { //@formatter:off requestSpecification @@ -140,7 +140,7 @@ void givenInvalidToken(URI uri, RequestSpecification requestSpecification, Strin } @ParameterizedTest - @MethodSource("org.zowe.apiml.integration.zaas.ZaasNegativeTest#provideZaasEndpoints") + @MethodSource("org.zowe.apiml.integration.zaas.ZaasSchemeNegativeTest#provideZaasEndpoints") void givenOKTATokenWithNoMapping(URI uri, RequestSpecification requestSpecification) { String oktaTokenNoMapping = SecurityUtils.validOktaAccessToken(false); //@formatter:off @@ -165,7 +165,7 @@ class GivenBadCertificate { String keystore; @ParameterizedTest - @MethodSource("org.zowe.apiml.integration.zaas.ZaasNegativeTest#provideZaasEndpoints") + @MethodSource("org.zowe.apiml.integration.zaas.ZaasSchemeNegativeTest#provideZaasEndpoints") void givenNoCertificate_thenReturnUnauthorized(URI uri, RequestSpecification requestSpecification) { //@formatter:off requestSpecification @@ -179,7 +179,7 @@ void givenNoCertificate_thenReturnUnauthorized(URI uri, RequestSpecification req } @ParameterizedTest - @MethodSource("org.zowe.apiml.integration.zaas.ZaasNegativeTest#provideZaasTokenEndpoints") + @MethodSource("org.zowe.apiml.integration.zaas.ZaasSchemeNegativeTest#provideZaasTokenEndpoints") void givenClientAndHeaderCertificates_thenReturnTokenFromClientCert(URI uri, RequestSpecification requestSpecification) throws Exception { TlsConfiguration tlsCfg = ConfigReader.environmentConfiguration().getTlsConfiguration(); SslContextConfigurer sslContextConfigurer = new SslContextConfigurer(tlsCfg.getKeyStorePassword(), tlsCfg.getClientKeystore(), tlsCfg.getKeyStore()); diff --git a/zaas-service/src/main/java/org/zowe/apiml/zaas/security/config/NewSecurityConfiguration.java b/zaas-service/src/main/java/org/zowe/apiml/zaas/security/config/NewSecurityConfiguration.java index 791be2595a..610501471a 100644 --- a/zaas-service/src/main/java/org/zowe/apiml/zaas/security/config/NewSecurityConfiguration.java +++ b/zaas-service/src/main/java/org/zowe/apiml/zaas/security/config/NewSecurityConfiguration.java @@ -30,8 +30,6 @@ import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer; import org.springframework.security.config.http.SessionCreationPolicy; -import org.springframework.security.core.userdetails.User; -import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.AnonymousAuthenticationFilter; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; @@ -68,7 +66,6 @@ import org.zowe.apiml.zaas.zaas.ExtractAuthSourceFilter; import org.zowe.apiml.zaas.zaas.ZaasAuthenticationFilter; -import java.util.Collections; import java.util.Set; import static org.springframework.security.config.Customizer.withDefaults; @@ -140,13 +137,12 @@ public SecurityFilterChain authenticationFunctionalityFilterChain(HttpSecurity h .authorizeHttpRequests(requests -> requests .anyRequest().permitAll()) - .x509(x509 -> x509.userDetailsService(x509UserDetailsService())) .logout(logout -> logout - .logoutRequestMatcher(new AntPathRequestMatcher( - authConfigurationProperties.getZaasLogoutEndpoint(), HttpMethod.POST.name() - )) - .addLogoutHandler(logoutHandler()) - .logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler(HttpStatus.NO_CONTENT))) + .logoutRequestMatcher(new AntPathRequestMatcher( + authConfigurationProperties.getZaasLogoutEndpoint(), HttpMethod.POST.name() + )) + .addLogoutHandler(logoutHandler()) + .logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler(HttpStatus.NO_CONTENT))) .authenticationProvider(compoundAuthProvider) // for authenticating credentials .authenticationProvider(new CertificateAuthenticationProvider()) // this is a dummy auth provider so the x509 prefiltering doesn't fail with nullpointer (no auth provider) or No AuthenticationProvider found for org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken @@ -160,9 +156,9 @@ private class CustomSecurityFilters extends AbstractHttpConfigurer matchers.requestMatchers( // no http method to catch all attempts to login and handle them here. Otherwise it falls to default filterchain and tries to route the calls, which doesnt make sense authConfigurationProperties.getZaasAccessTokenEndpoint() ))) - .authorizeHttpRequests(requests -> requests - .anyRequest().permitAll()) - .x509(x509 -> x509.userDetailsService(x509UserDetailsService())) - .authenticationProvider(compoundAuthProvider) // for authenticating credentials - .authenticationProvider(tokenAuthenticationProvider) - .authenticationProvider(new CertificateAuthenticationProvider()) // this is a dummy auth provider so the x509 prefiltering doesn't fail with nullpointer (no auth provider) or No AuthenticationProvider found for org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken - .with(new CustomSecurityFilters(), Customizer.withDefaults()); + .authorizeHttpRequests(requests -> requests + .anyRequest().permitAll()) + .authenticationProvider(compoundAuthProvider) // for authenticating credentials + .authenticationProvider(tokenAuthenticationProvider) + .authenticationProvider(new CertificateAuthenticationProvider()) // this is a dummy auth provider so the x509 prefiltering doesn't fail with nullpointer (no auth provider) or No AuthenticationProvider found for org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken + .with(new CustomSecurityFilters(), Customizer.withDefaults()); return http.build(); } @@ -220,10 +215,10 @@ private class CustomSecurityFilters extends AbstractHttpConfigurer requests - .anyRequest().authenticated()) - .x509(x509 -> x509.userDetailsService(x509UserDetailsService())) - .authenticationProvider(compoundAuthProvider) // for authenticating credentials - .with(new CustomSecurityFilters(), Customizer.withDefaults()); + .authorizeHttpRequests(requests -> requests + .anyRequest().authenticated()) + .authenticationProvider(compoundAuthProvider) // for authenticating credentials + .with(new CustomSecurityFilters(), Customizer.withDefaults()); return http.build(); } private class CustomSecurityFilters extends AbstractHttpConfigurer { @Override public void configure(HttpSecurity http) { - http.addFilterBefore(new CategorizeCertsFilter(publicKeyCertificatesBase64, certificateValidator), org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter.class) - .addFilterBefore(loginFilter(http), org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter.class) - .addFilterAfter(x509AuthenticationFilter(), org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter.class); + http.addFilterAfter(new CategorizeCertsFilter(publicKeyCertificatesBase64, certificateValidator, authExceptionHandler), org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter.class) + .addFilterAfter(x509ForwardingAwareAuthenticationFilter(), CategorizeCertsFilter.class) + .addFilterAfter(loginFilter(http), X509AuthAwareFilter.class); } private NonCompulsoryAuthenticationProcessingFilter loginFilter(HttpSecurity http) { @@ -282,7 +276,7 @@ private NonCompulsoryAuthenticationProcessingFilter loginFilter(HttpSecurity htt handlerInitializer.getResourceAccessExceptionHandler()); } - private X509AuthenticationFilter x509AuthenticationFilter() { + private X509ForwardingAwareAuthenticationFilter x509ForwardingAwareAuthenticationFilter() { return new X509AuthAwareFilter("/**", handlerInitializer.getAuthenticationFailureHandler(), x509AuthenticationProvider); @@ -302,12 +296,11 @@ public SecurityFilterChain authZaasEndpointsFilterChain(HttpSecurity http) throw baseConfigure(http.securityMatchers(matchers -> matchers.requestMatchers( // no http method to catch all attempts to login and handle them here. Otherwise it falls to default filterchain and tries to route the calls, which doesnt make sense "/zaas/scheme/**" ))) - .authorizeHttpRequests(requests -> requests - .anyRequest().authenticated()) - .x509(x509 -> x509.userDetailsService(x509UserDetailsService())) - .addFilterAfter(new CategorizeCertsFilter(publicKeyCertificatesBase64, certificateValidator), org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter.class) - .addFilterAfter(new ExtractAuthSourceFilter(authSourceService, authExceptionHandler), org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter.class) - .addFilterAfter(new ZaasAuthenticationFilter(authSourceService, authExceptionHandler), CategorizeCertsFilter.class); + .authorizeHttpRequests(requests -> requests + .anyRequest().authenticated()) + .addFilterAfter(new CategorizeCertsFilter(publicKeyCertificatesBase64, certificateValidator, authExceptionHandler), org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter.class) + .addFilterAfter(new ExtractAuthSourceFilter(authSourceService, authExceptionHandler), org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter.class) + .addFilterAfter(new ZaasAuthenticationFilter(authSourceService, authExceptionHandler), CategorizeCertsFilter.class); return http.build(); } @@ -373,9 +366,9 @@ class Ticket { @Bean public SecurityFilterChain ticketFilterChain(HttpSecurity http) throws Exception { return baseConfigure(http.securityMatchers(matchers -> matchers.requestMatchers( - authConfigurationProperties.getZaasTicketEndpoint() - ))).authorizeHttpRequests(requests -> requests.anyRequest().authenticated()) - .addFilterBefore(new CategorizeCertsFilter(publicKeyCertificatesBase64, certificateValidator), org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter.class) + authConfigurationProperties.getZaasTicketEndpoint() + ))).authorizeHttpRequests(requests -> requests.anyRequest().authenticated()) + .addFilterBefore(new CategorizeCertsFilter(publicKeyCertificatesBase64, certificateValidator, authExceptionHandler), org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter.class) .authenticationProvider(tokenAuthenticationProvider) .logout(logout -> logout.disable()) // logout filter in this chain not needed .x509(x509 -> x509 //default x509 filter, authenticates trusted cert, ticketFilter(..) depends on this @@ -423,11 +416,11 @@ public SecurityFilterChain refreshFilterChain(HttpSecurity http) throws Exceptio authConfigurationProperties.getZaasRefreshEndpoint() ))).authorizeHttpRequests(requests -> requests .anyRequest().authenticated()) - .authenticationProvider(tokenAuthenticationProvider) - .logout(logout -> logout.disable()) // logout filter in this chain not needed - .x509(x509 -> x509 //default x509 filter, authenticates trusted cert, ticketFilter(..) depends on this - .userDetailsService(new SimpleUserDetailService())) - .with(new CustomSecurityFilters(), Customizer.withDefaults()); + .authenticationProvider(tokenAuthenticationProvider) + .logout(logout -> logout.disable()) // logout filter in this chain not needed + .x509(x509 -> x509 //default x509 filter, authenticates trusted cert, refreshFilter(..) depends on this + .userDetailsService(new SimpleUserDetailService())) + .with(new CustomSecurityFilters(), Customizer.withDefaults()); return http.build(); } @@ -463,12 +456,12 @@ class CertificateProtectedEndpoints { @Bean public SecurityFilterChain certificateEndpointsFilterChain(HttpSecurity http) throws Exception { return baseConfigure(http.securityMatchers(matchers -> matchers - .requestMatchers(AuthController.CONTROLLER_PATH + AuthController.INVALIDATE_PATH, AuthController.CONTROLLER_PATH + AuthController.DISTRIBUTE_PATH)) + .requestMatchers(AuthController.CONTROLLER_PATH + AuthController.INVALIDATE_PATH, AuthController.CONTROLLER_PATH + AuthController.DISTRIBUTE_PATH)) ).authorizeHttpRequests(requests -> requests .anyRequest().authenticated()) - .logout(AbstractHttpConfigurer::disable) // logout filter in this chain not needed - .x509(x509 -> x509 // default x509 filter, authenticates trusted cert - .userDetailsService(new SimpleUserDetailService())).build(); + .logout(AbstractHttpConfigurer::disable) // logout filter in this chain not needed + .x509(x509 -> x509 // default x509 filter, authenticates trusted cert + .userDetailsService(new SimpleUserDetailService())).build(); } } @@ -498,25 +491,25 @@ class CertificateOrAuthProtectedEndpoints { public SecurityFilterChain certificateOrAuthEndpointsFilterChain(HttpSecurity http) throws Exception { baseConfigure( http.securityMatchers(matchers -> matchers - .requestMatchers("/application/**") - .requestMatchers(HttpMethod.POST, SafResourceAccessController.FULL_CONTEXT_PATH) - .requestMatchers("/gateway/conformance/**") - .requestMatchers("/gateway/api/v1/conformance/**") - .requestMatchers("/gateway/validate") - .requestMatchers("/gateway/api/v1/validate") - ) + .requestMatchers("/application/**") + .requestMatchers(HttpMethod.POST, SafResourceAccessController.FULL_CONTEXT_PATH) + .requestMatchers("/gateway/conformance/**") + .requestMatchers("/gateway/api/v1/conformance/**") + .requestMatchers("/gateway/validate") + .requestMatchers("/gateway/api/v1/validate") + ) ).authorizeHttpRequests(requests -> requests - .anyRequest() - .authenticated() - ) - .logout(logout -> logout.disable()); // logout filter in this chain not needed + .anyRequest() + .authenticated() + ) + .logout(logout -> logout.disable()); // logout filter in this chain not needed if (isAttlsEnabled) { http.x509(withDefaults()) - // filter out API ML certificate - .addFilterBefore(reversedCategorizeCertFilter(), org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter.class); + // filter out API ML certificate + .addFilterBefore(reversedCategorizeCertFilter(), org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter.class); } else { - http.x509(x509 -> x509.userDetailsService(x509UserDetailsService())); // default x509 filter, authenticates trusted cert + http.addFilterAfter(new CategorizeCertsFilter(publicKeyCertificatesBase64, certificateValidator, authExceptionHandler), org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter.class); } return http.authenticationProvider(compoundAuthProvider) // for authenticating credentials @@ -573,7 +566,7 @@ private BearerContentFilter bearerContentFilter(AuthenticationManager authentica } private CategorizeCertsFilter reversedCategorizeCertFilter() { - CategorizeCertsFilter out = new CategorizeCertsFilter(publicKeyCertificatesBase64, certificateValidator); + CategorizeCertsFilter out = new CategorizeCertsFilter(publicKeyCertificatesBase64, certificateValidator, authExceptionHandler); out.setCertificateForClientAuth(crt -> out.getPublicKeyCertificatesBase64().contains(CategorizeCertsFilter.base64EncodePublicKey(crt))); out.setApimlCertificate(crt -> !out.getPublicKeyCertificatesBase64().contains(CategorizeCertsFilter.base64EncodePublicKey(crt))); return out; @@ -612,12 +605,12 @@ public WebSecurityCustomizer webSecurityCustomizer() { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { return baseConfigure(http.securityMatchers(matchers -> matchers.requestMatchers("/**", "/gateway/version"))) - .authorizeHttpRequests(requests -> requests - .anyRequest() - .permitAll()).logout(logout -> logout.disable()) - // sort out client and apiml internal certificates - .addFilterBefore(new CategorizeCertsFilter(publicKeyCertificatesBase64, certificateValidator), AnonymousAuthenticationFilter.class) - .build(); + .authorizeHttpRequests(requests -> requests + .anyRequest() + .permitAll()).logout(logout -> logout.disable()) + // sort out client and apiml internal certificates + .addFilterBefore(new CategorizeCertsFilter(publicKeyCertificatesBase64, certificateValidator, authExceptionHandler), AnonymousAuthenticationFilter.class) + .build(); } } @@ -639,9 +632,4 @@ protected HttpSecurity baseConfigure(HttpSecurity http) throws Exception { .sessionManagement(management -> management.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .exceptionHandling(handling -> handling.authenticationEntryPoint(handlerInitializer.getBasicAuthUnauthorizedHandler())); } - - private UserDetailsService x509UserDetailsService() { - return username -> new User(username, "", Collections.emptyList()); - } - } diff --git a/zaas-service/src/main/java/org/zowe/apiml/zaas/zaas/ZaasAuthenticationFilter.java b/zaas-service/src/main/java/org/zowe/apiml/zaas/zaas/ZaasAuthenticationFilter.java index 38cecd15e1..b8dac91b52 100644 --- a/zaas-service/src/main/java/org/zowe/apiml/zaas/zaas/ZaasAuthenticationFilter.java +++ b/zaas-service/src/main/java/org/zowe/apiml/zaas/zaas/ZaasAuthenticationFilter.java @@ -17,6 +17,8 @@ import lombok.NonNull; import lombok.RequiredArgsConstructor; import org.springframework.security.authentication.InsufficientAuthenticationException; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; import org.springframework.web.filter.OncePerRequestFilter; import org.zowe.apiml.security.common.error.AuthExceptionHandler; import org.zowe.apiml.zaas.security.service.schema.source.AuthSource; @@ -37,8 +39,13 @@ public class ZaasAuthenticationFilter extends OncePerRequestFilter { protected void doFilterInternal(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull FilterChain filterChain) throws ServletException, IOException { try { Optional authSource = Optional.ofNullable((AuthSource) request.getAttribute(AUTH_SOURCE_ATTR)); - if (!authSource.isPresent() || !authSourceService.isValid(authSource.get())) { + if (authSource.isEmpty() || !authSourceService.isValid(authSource.get())) { throw new InsufficientAuthenticationException("Authentication failed."); + } else { + var auth = new PreAuthenticatedAuthenticationToken( + authSourceService.parse(authSource.get()).getUserId(), null); + auth.setAuthenticated(true); + SecurityContextHolder.getContext().setAuthentication(auth); } filterChain.doFilter(request, response); } catch (RuntimeException e) { diff --git a/zaas-service/src/test/java/org/zowe/apiml/acceptance/ZaasTest.java b/zaas-service/src/test/java/org/zowe/apiml/acceptance/ZaasTest.java index 68f15cf4a4..8e06a8693e 100644 --- a/zaas-service/src/test/java/org/zowe/apiml/acceptance/ZaasTest.java +++ b/zaas-service/src/test/java/org/zowe/apiml/acceptance/ZaasTest.java @@ -53,7 +53,7 @@ void givenZosmfCookieAndDummyAuthProvider_whenZoweJwtRequest_thenUnavailable() { //@formatter:off given().config(config().sslConfig(new SSLConfig().sslSocketFactory( - new SSLSocketFactory(httpConfig.getSecureSslContextWithoutKeystore(), ALLOW_ALL_HOSTNAME_VERIFIER))) + new SSLSocketFactory(httpConfig.getSecureSslContext(), ALLOW_ALL_HOSTNAME_VERIFIER))) ) .cookie(COOKIE, zosmfJwt) .when() diff --git a/zaas-service/src/test/java/org/zowe/apiml/zaas/security/config/X509AuthenticationFilterTest.java b/zaas-service/src/test/java/org/zowe/apiml/zaas/security/config/X509ForwardingAwareAuthenticationFilterTest.java similarity index 83% rename from zaas-service/src/test/java/org/zowe/apiml/zaas/security/config/X509AuthenticationFilterTest.java rename to zaas-service/src/test/java/org/zowe/apiml/zaas/security/config/X509ForwardingAwareAuthenticationFilterTest.java index 2f225d4c96..e4cc64f403 100644 --- a/zaas-service/src/test/java/org/zowe/apiml/zaas/security/config/X509AuthenticationFilterTest.java +++ b/zaas-service/src/test/java/org/zowe/apiml/zaas/security/config/X509ForwardingAwareAuthenticationFilterTest.java @@ -11,6 +11,8 @@ package org.zowe.apiml.zaas.security.config; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.http.HttpMethod; @@ -18,26 +20,24 @@ import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.web.authentication.AuthenticationSuccessHandler; -import org.zowe.apiml.zaas.security.service.AuthenticationService; -import org.zowe.apiml.zaas.utils.X509Utils; -import org.zowe.apiml.security.common.login.X509AuthenticationFilter; +import org.zowe.apiml.security.common.login.X509ForwardingAwareAuthenticationFilter; import org.zowe.apiml.security.common.token.TokenAuthentication; import org.zowe.apiml.security.common.token.X509AuthenticationToken; +import org.zowe.apiml.zaas.security.service.AuthenticationService; +import org.zowe.apiml.zaas.utils.X509Utils; -import jakarta.servlet.FilterChain; -import jakarta.servlet.ServletException; import java.io.IOException; import java.security.cert.X509Certificate; import java.util.Optional; import static org.mockito.Mockito.*; -class X509AuthenticationFilterTest { +class X509ForwardingAwareAuthenticationFilterTest { private MockHttpServletRequest httpServletRequest; private MockHttpServletResponse httpServletResponse; - private X509AuthenticationFilter x509AuthenticationFilter; + private X509ForwardingAwareAuthenticationFilter x509ForwardingAwareAuthenticationFilter; private AuthenticationSuccessHandler successHandler; @@ -55,7 +55,7 @@ void setup() { authenticationProvider = mock(AuthenticationProvider.class); filterChain = mock(FilterChain.class); authenticationService = mock(AuthenticationService.class); - x509AuthenticationFilter = new X509AuthenticationFilter("/api/v1/zaas/auth/login", successHandler, authenticationProvider); + x509ForwardingAwareAuthenticationFilter = new X509ForwardingAwareAuthenticationFilter("/api/v1/zaas/auth/login", successHandler, authenticationProvider); when(authenticationService.getJwtTokenFromRequest(httpServletRequest)).thenReturn(Optional.of("jwt")); } @@ -67,7 +67,7 @@ void shouldCallAuthProviderWithCertificate() { httpServletRequest.setAttribute("client.auth.X509Certificate", x509Certificate); httpServletResponse = new MockHttpServletResponse(); - x509AuthenticationFilter.attemptAuthentication(httpServletRequest, httpServletResponse); + x509ForwardingAwareAuthenticationFilter.attemptAuthentication(httpServletRequest, httpServletResponse); verify(authenticationProvider).authenticate(new X509AuthenticationToken(x509Certificate)); } @@ -82,7 +82,7 @@ void shouldAuthenticateWithCertificate() throws ServletException, IOException { httpServletResponse = new MockHttpServletResponse(); when(authenticationProvider.authenticate(new X509AuthenticationToken(x509Certificate))) .thenReturn(new TokenAuthentication("user", "jwt")); - x509AuthenticationFilter.doFilter(httpServletRequest, httpServletResponse, filterChain); + x509ForwardingAwareAuthenticationFilter.doFilter(httpServletRequest, httpServletResponse, filterChain); verify(authenticationProvider).authenticate(new X509AuthenticationToken(x509Certificate)); } @@ -93,7 +93,7 @@ void shouldNotAuthenticate() { httpServletRequest.setMethod(HttpMethod.POST.name()); httpServletResponse = new MockHttpServletResponse(); - x509AuthenticationFilter.attemptAuthentication(httpServletRequest, httpServletResponse); + x509ForwardingAwareAuthenticationFilter.attemptAuthentication(httpServletRequest, httpServletResponse); verify(authenticationProvider, times(0)).authenticate(new X509AuthenticationToken(x509Certificate)); } diff --git a/zaas-service/src/test/java/org/zowe/apiml/zaas/zaas/ZaasAuthenticationFilterTest.java b/zaas-service/src/test/java/org/zowe/apiml/zaas/zaas/ZaasAuthenticationFilterTest.java index bf2b0cac30..47df6f3bda 100644 --- a/zaas-service/src/test/java/org/zowe/apiml/zaas/zaas/ZaasAuthenticationFilterTest.java +++ b/zaas-service/src/test/java/org/zowe/apiml/zaas/zaas/ZaasAuthenticationFilterTest.java @@ -27,6 +27,7 @@ import org.zowe.apiml.zaas.security.service.schema.source.AuthSource; import org.zowe.apiml.zaas.security.service.schema.source.AuthSourceService; import org.zowe.apiml.zaas.security.service.schema.source.JwtAuthSource; +import org.zowe.apiml.zaas.security.service.schema.source.ParsedTokenAuthSource; import java.io.IOException; @@ -112,6 +113,9 @@ private void mockAuthSource(boolean isValid) { AuthSource authSource = new JwtAuthSource("token"); request.setAttribute(AUTH_SOURCE_ATTR, authSource); when(authSourceService.isValid(authSource)).thenReturn(isValid); + if (isValid) { + when(authSourceService.parse(authSource)).thenReturn(new ParsedTokenAuthSource("user", null, null, null)); + } } } diff --git a/zaas-service/src/test/java/org/zowe/apiml/zaas/zaas/ZaasExceptionHandlerTest.java b/zaas-service/src/test/java/org/zowe/apiml/zaas/zaas/ZaasExceptionHandlerTest.java index fbb3ab57c1..f798092318 100644 --- a/zaas-service/src/test/java/org/zowe/apiml/zaas/zaas/ZaasExceptionHandlerTest.java +++ b/zaas-service/src/test/java/org/zowe/apiml/zaas/zaas/ZaasExceptionHandlerTest.java @@ -11,16 +11,21 @@ package org.zowe.apiml.zaas.zaas; import io.restassured.RestAssured; +import io.restassured.config.RestAssuredConfig; +import io.restassured.config.SSLConfig; +import org.apache.http.conn.ssl.SSLSocketFactory; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import org.zowe.apiml.product.web.HttpConfig; import org.zowe.apiml.security.common.auth.saf.EndpointImproperlyConfigureException; import org.zowe.apiml.security.common.auth.saf.UnsupportedResourceClassException; @@ -41,11 +46,17 @@ class ZaasExceptionHandlerTest { @LocalServerPort private int port; + @Autowired + private HttpConfig httpConfig; + @BeforeEach void init() { RestAssured.baseURI = "https://localhost"; RestAssured.port = port; RestAssured.useRelaxedHTTPSValidation(); + RestAssured.config = RestAssured.config.sslConfig(new SSLConfig().sslSocketFactory( + new SSLSocketFactory(httpConfig.getSecureSslContext(), SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER) + )); } @Test @@ -70,6 +81,20 @@ void givenNoCredentials_whenCallZaas_thenReturns401WithMessage(String url) { .body("messages[0].messageKey", is("org.zowe.apiml.security.authRequired")); } + @ParameterizedTest + @ValueSource(strings = { + "/zaas/scheme/ticket", + "/application/health" + }) + void givenNoCertificateAndNoCredentials_whenCallZaas_thenReturns401WithMessage(String url) { + given().config(RestAssuredConfig.newConfig()).relaxedHTTPSValidation() + .when() + .get(url) + .then() + .statusCode(401) + .body("messages[0].messageKey", is("org.zowe.apiml.security.authRequired")); + } + @ParameterizedTest @ValueSource(strings = { "/zaas/api/v1/auth/login" diff --git a/zaas-service/src/test/resources/application.yml b/zaas-service/src/test/resources/application.yml index 4e3ebcd815..484ba1e594 100644 --- a/zaas-service/src/test/resources/application.yml +++ b/zaas-service/src/test/resources/application.yml @@ -188,4 +188,4 @@ management: --- spring.config.activate.on-profile: dev -logbackServiceName: ZWEAGW1 +logbackServiceName: ZWEAZS1 From beb4dea9367c15097b65bca4d435a12475e93c71 Mon Sep 17 00:00:00 2001 From: Richard Salac Date: Thu, 30 Jan 2025 15:48:04 +0100 Subject: [PATCH 02/11] wip: force gateway always use server certificate when calling zaas Signed-off-by: Richard Salac Signed-off-by: Richard Salac --- .../org/zowe/apiml/gateway/config/AuthEndpointConfig.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/gateway-service/src/main/java/org/zowe/apiml/gateway/config/AuthEndpointConfig.java b/gateway-service/src/main/java/org/zowe/apiml/gateway/config/AuthEndpointConfig.java index d27530195c..4bd1da8d70 100644 --- a/gateway-service/src/main/java/org/zowe/apiml/gateway/config/AuthEndpointConfig.java +++ b/gateway-service/src/main/java/org/zowe/apiml/gateway/config/AuthEndpointConfig.java @@ -77,7 +77,9 @@ private WebClient createLoadBalanced(WebClient webClient, ReactiveLoadBalancer.F private WebClient.RequestBodySpec getWebclient(ServerRequest serverRequest, String path) { var sslInfo = serverRequest.exchange().getRequest().getSslInfo(); - var client = sslInfo == null ? this.webClient : this.webClientClientCert; + //TODO: remove after checking integration tests + //var client = sslInfo == null ? this.webClient : this.webClientClientCert; + var client = this.webClientClientCert; var request = client .method(serverRequest.method()) From 027cf253ec29e4f86a6c57419c493d195a42b27a Mon Sep 17 00:00:00 2001 From: Richard Salac Date: Fri, 31 Jan 2025 14:00:18 +0100 Subject: [PATCH 03/11] wip: new filter to check for client certificate presence Signed-off-by: Richard Salac Signed-off-by: Richard Salac --- .../common/filter/CategorizeCertsFilter.java | 3 +- .../X509ClientRejectIfMissingFilter.java | 45 ++++++++++++ ...pulsoryAuthenticationProcessingFilter.java | 10 +-- .../common/verify/CertificateValidator.java | 2 +- .../filter/CategorizeCertsFilterTest.java | 16 ++--- .../X509ClientRejectIfMissingFilterTest.java | 68 +++++++++++++++++++ .../verify/CertificateValidatorTest.java | 26 +++---- ...cceptForwardedClientCertFilterFactory.java | 2 +- ...tForwardedClientCertFilterFactoryTest.java | 4 +- .../config/NewSecurityConfiguration.java | 13 ++-- 10 files changed, 153 insertions(+), 36 deletions(-) create mode 100644 apiml-security-common/src/main/java/org/zowe/apiml/security/common/filter/X509ClientRejectIfMissingFilter.java create mode 100644 apiml-security-common/src/test/java/org/zowe/apiml/security/common/filter/X509ClientRejectIfMissingFilterTest.java diff --git a/apiml-security-common/src/main/java/org/zowe/apiml/security/common/filter/CategorizeCertsFilter.java b/apiml-security-common/src/main/java/org/zowe/apiml/security/common/filter/CategorizeCertsFilter.java index e597afcc6e..db59a0e9ac 100644 --- a/apiml-security-common/src/main/java/org/zowe/apiml/security/common/filter/CategorizeCertsFilter.java +++ b/apiml-security-common/src/main/java/org/zowe/apiml/security/common/filter/CategorizeCertsFilter.java @@ -80,7 +80,7 @@ private void categorizeCerts(ServletRequest request) { X509Certificate[] certs = (X509Certificate[]) request.getAttribute(ATTRNAME_JAKARTA_SERVLET_REQUEST_X509_CERTIFICATE); if (certs != null && certs.length > 0) { Optional clientCert = getClientCertFromHeader((HttpServletRequest) request); - if (certificateValidator.isForwardingEnabled() && certificateValidator.isTrusted(certs) && clientCert.isPresent()) { + if (certificateValidator.isForwardingEnabled() && certificateValidator.hasGatewayChain(certs) && clientCert.isPresent()) { certificateValidator.updateAPIMLPublicKeyCertificates(certs); // add the client certificate to the certs array String subjectDN = ((X509Certificate) clientCert.get()).getSubjectX500Principal().getName(); @@ -158,6 +158,7 @@ protected void doFilterInternal(@NonNull HttpServletRequest request, @NonNull Ht categorizeCerts(request); } catch (InsufficientAuthenticationException ex) { authExceptionHandler.handleException(request, response, ex); + return; } filterChain.doFilter(mutate(request), response); } diff --git a/apiml-security-common/src/main/java/org/zowe/apiml/security/common/filter/X509ClientRejectIfMissingFilter.java b/apiml-security-common/src/main/java/org/zowe/apiml/security/common/filter/X509ClientRejectIfMissingFilter.java new file mode 100644 index 0000000000..7c27d4e28d --- /dev/null +++ b/apiml-security-common/src/main/java/org/zowe/apiml/security/common/filter/X509ClientRejectIfMissingFilter.java @@ -0,0 +1,45 @@ +/* + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright Contributors to the Zowe Project. + */ + +package org.zowe.apiml.security.common.filter; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.filter.OncePerRequestFilter; +import org.zowe.apiml.security.common.error.AuthExceptionHandler; +import org.zowe.apiml.security.common.error.InvalidCertificateException; + +import java.io.IOException; +import java.security.cert.X509Certificate; + +@Slf4j +@RequiredArgsConstructor +public class X509ClientRejectIfMissingFilter extends OncePerRequestFilter { + + static final String ATTRNAME_CLIENT_AUTH_X509_CERTIFICATE = "client.auth.X509Certificate"; + + private final AuthExceptionHandler authExceptionHandler; + + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + X509Certificate[] certs = (X509Certificate[]) request.getAttribute(ATTRNAME_CLIENT_AUTH_X509_CERTIFICATE); + if (certs == null || certs.length == 0) { + log.debug("No X509 client certificate found in request."); + authExceptionHandler.handleException(request, response, new InvalidCertificateException("X509 client certificate required but missing.")); + return; + } + filterChain.doFilter(request, response); + } +} diff --git a/apiml-security-common/src/main/java/org/zowe/apiml/security/common/login/NonCompulsoryAuthenticationProcessingFilter.java b/apiml-security-common/src/main/java/org/zowe/apiml/security/common/login/NonCompulsoryAuthenticationProcessingFilter.java index 466e682987..364540190f 100644 --- a/apiml-security-common/src/main/java/org/zowe/apiml/security/common/login/NonCompulsoryAuthenticationProcessingFilter.java +++ b/apiml-security-common/src/main/java/org/zowe/apiml/security/common/login/NonCompulsoryAuthenticationProcessingFilter.java @@ -10,13 +10,16 @@ package org.zowe.apiml.security.common.login; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter; -import jakarta.servlet.*; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; /** @@ -42,7 +45,6 @@ public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) try { authResult = attemptAuthentication(request, response); } - catch (AuthenticationException failed) { // Authentication failed unsuccessfulAuthentication(request, response, failed); diff --git a/apiml-security-common/src/main/java/org/zowe/apiml/security/common/verify/CertificateValidator.java b/apiml-security-common/src/main/java/org/zowe/apiml/security/common/verify/CertificateValidator.java index ca2aafa8f6..db2131035c 100644 --- a/apiml-security-common/src/main/java/org/zowe/apiml/security/common/verify/CertificateValidator.java +++ b/apiml-security-common/src/main/java/org/zowe/apiml/security/common/verify/CertificateValidator.java @@ -56,7 +56,7 @@ public CertificateValidator(TrustedCertificatesProvider trustedCertificatesProvi * @param certs Certificates to compare with known trusted ones * @return true if all given certificates are known false otherwise */ - public boolean isTrusted(X509Certificate[] certs) { + public boolean hasGatewayChain(X509Certificate[] certs) { if ((proxyCertificatesEndpoints == null) || (proxyCertificatesEndpoints.length == 0)) { log.debug("No endpoint configured to retrieve trusted certificates. Provide URL via apiml.security.x509.certificatesUrls"); return false; diff --git a/apiml-security-common/src/test/java/org/zowe/apiml/security/common/filter/CategorizeCertsFilterTest.java b/apiml-security-common/src/test/java/org/zowe/apiml/security/common/filter/CategorizeCertsFilterTest.java index d04c20a26c..14a1cb2ca1 100644 --- a/apiml-security-common/src/test/java/org/zowe/apiml/security/common/filter/CategorizeCertsFilterTest.java +++ b/apiml-security-common/src/test/java/org/zowe/apiml/security/common/filter/CategorizeCertsFilterTest.java @@ -91,7 +91,7 @@ public void setUp() throws ServletException { certificateValidator = mock(CertificateValidator.class); authExceptionHandler = mock(AuthExceptionHandler.class); when(certificateValidator.isForwardingEnabled()).thenReturn(false); - when(certificateValidator.isTrusted(any())).thenReturn(false); + when(certificateValidator.hasGatewayChain(any())).thenReturn(false); doNothing().when(authExceptionHandler).handleException(any(), any(), any()); } @@ -100,7 +100,7 @@ class GivenNoPublicKeysInFilter { @BeforeEach void setUp() { - filter = new CategorizeCertsFilter(new HashSet<>(), certificateValidator, authExceptionHandler); + filter = new CategorizeCertsFilter(new HashSet<>(), certificateValidator, authExceptionHandler, false); } @Nested @@ -124,7 +124,7 @@ class WhenForwardingEnabled { @BeforeEach void setUp() { when(certificateValidator.isForwardingEnabled()).thenReturn(true); - when(certificateValidator.isTrusted(any())).thenReturn(true); + when(certificateValidator.hasGatewayChain(any())).thenReturn(true); } @Test @@ -206,7 +206,7 @@ public void setUp() { @Test void givenTrustedCerts_thenClientCertHeaderAccepted() throws ServletException, IOException { - when(certificateValidator.isTrusted(certificates)).thenReturn(true); + when(certificateValidator.hasGatewayChain(certificates)).thenReturn(true); // when incoming certs are all trusted means that all their public keys are added to the filter filter.getPublicKeyCertificatesBase64().add(X509Utils.correctBase64("apimlCert1")); filter.getPublicKeyCertificatesBase64().add(X509Utils.correctBase64("apimlCertCA")); @@ -231,7 +231,7 @@ void givenTrustedCerts_thenClientCertHeaderAccepted() throws ServletException, I @Test void givenNotTrustedCerts_thenClientCertHeaderIgnored() throws ServletException, IOException { - when(certificateValidator.isTrusted(certificates)).thenReturn(false); + when(certificateValidator.hasGatewayChain(certificates)).thenReturn(false); filter.doFilter(request, response, chain); HttpServletRequest nextRequest = (HttpServletRequest) chain.getRequest(); assertNotNull(nextRequest); @@ -286,7 +286,7 @@ class WhenInvalidCertificateInHeaderAndForwardingEnabled { public void setUp() { request.addHeader(CLIENT_CERT_HEADER, "invalid_cert"); when(certificateValidator.isForwardingEnabled()).thenReturn(true); - when(certificateValidator.isTrusted(certificates)).thenReturn(true); + when(certificateValidator.hasGatewayChain(certificates)).thenReturn(true); } @Test @@ -417,7 +417,7 @@ public void setUp() { @Test void givenTrustedCerts_thenClientCertHeaderAccepted() throws ServletException, IOException { - when(certificateValidator.isTrusted(certificates)).thenReturn(true); + when(certificateValidator.hasGatewayChain(certificates)).thenReturn(true); // when incoming certs are all trusted means that all their public keys are added to the filter filter.getPublicKeyCertificatesBase64().add(X509Utils.correctBase64("apimlCert1")); filter.getPublicKeyCertificatesBase64().add(X509Utils.correctBase64("apimlCertCA")); @@ -442,7 +442,7 @@ void givenTrustedCerts_thenClientCertHeaderAccepted() throws ServletException, I @Test void givenNotTrustedCerts_thenClientCertHeaderIgnored() throws ServletException, IOException { - when(certificateValidator.isTrusted(certificates)).thenReturn(false); + when(certificateValidator.hasGatewayChain(certificates)).thenReturn(false); filter.doFilter(request, response, chain); HttpServletRequest nextRequest = (HttpServletRequest) chain.getRequest(); assertNotNull(nextRequest); diff --git a/apiml-security-common/src/test/java/org/zowe/apiml/security/common/filter/X509ClientRejectIfMissingFilterTest.java b/apiml-security-common/src/test/java/org/zowe/apiml/security/common/filter/X509ClientRejectIfMissingFilterTest.java new file mode 100644 index 0000000000..a415cacc7e --- /dev/null +++ b/apiml-security-common/src/test/java/org/zowe/apiml/security/common/filter/X509ClientRejectIfMissingFilterTest.java @@ -0,0 +1,68 @@ +/* + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright Contributors to the Zowe Project. + */ + +package org.zowe.apiml.security.common.filter; + +import jakarta.servlet.Filter; +import jakarta.servlet.Servlet; +import jakarta.servlet.ServletException; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.mock.web.MockFilterChain; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.zowe.apiml.security.common.error.AuthExceptionHandler; +import org.zowe.apiml.security.common.error.InvalidCertificateException; +import org.zowe.apiml.security.common.utils.X509Utils; + +import java.io.IOException; +import java.security.cert.X509Certificate; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; +import static org.zowe.apiml.security.common.filter.X509ClientRejectIfMissingFilter.ATTRNAME_CLIENT_AUTH_X509_CERTIFICATE; + +class X509ClientRejectIfMissingFilterTest { + + private MockHttpServletRequest request; + private MockHttpServletResponse response; + private MockFilterChain chain; + private AuthExceptionHandler authExceptionHandler; + private Filter nextFilter; + + private final X509Certificate[] x509Certificate = + new X509Certificate[]{X509Utils.getCertificate(X509Utils.correctBase64("zowe"), "CN=user"),}; + + @BeforeEach + void setUp() { + authExceptionHandler = Mockito.mock(AuthExceptionHandler.class); + request = new MockHttpServletRequest(); + response = new MockHttpServletResponse(); + nextFilter = Mockito.mock(Filter.class); + chain = new MockFilterChain(Mockito.mock(Servlet.class), new X509ClientRejectIfMissingFilter(authExceptionHandler), nextFilter); + } + + @Test + void whenClientCertMissing_thenRejectAndStop() throws ServletException, IOException { + chain.doFilter(request, response); + verify(authExceptionHandler, times(1)) + .handleException(any(), any(), any(InvalidCertificateException.class)); + verify(nextFilter, never()).doFilter(any(), any(), eq(chain)); + } + + @Test + void whenClientCertPresent_thenAllowAndContinue() throws ServletException, IOException { + request.setAttribute(ATTRNAME_CLIENT_AUTH_X509_CERTIFICATE, x509Certificate); + chain.doFilter(request, response); + verify(authExceptionHandler, never()).handleException(any(), any(), any()); + verify(nextFilter, times(1)).doFilter(any(), any(), eq(chain)); + } +} diff --git a/apiml-security-common/src/test/java/org/zowe/apiml/security/common/verify/CertificateValidatorTest.java b/apiml-security-common/src/test/java/org/zowe/apiml/security/common/verify/CertificateValidatorTest.java index cad41bc1e0..0f7da43631 100644 --- a/apiml-security-common/src/test/java/org/zowe/apiml/security/common/verify/CertificateValidatorTest.java +++ b/apiml-security-common/src/test/java/org/zowe/apiml/security/common/verify/CertificateValidatorTest.java @@ -60,16 +60,16 @@ void setUp() { } @Test void whenAllCertificatesFoundThenTheyAreTrusted() { - assertTrue(certificateValidator.isTrusted(new X509Certificate[]{cert1})); - assertTrue(certificateValidator.isTrusted(new X509Certificate[]{cert2})); - assertTrue(certificateValidator.isTrusted(new X509Certificate[]{cert1, cert2})); + assertTrue(certificateValidator.hasGatewayChain(new X509Certificate[]{cert1})); + assertTrue(certificateValidator.hasGatewayChain(new X509Certificate[]{cert2})); + assertTrue(certificateValidator.hasGatewayChain(new X509Certificate[]{cert1, cert2})); } @Test void whenSomeCertificateNotFoundThenAllUntrusted() { - assertFalse(certificateValidator.isTrusted(new X509Certificate[]{cert3})); - assertFalse(certificateValidator.isTrusted(new X509Certificate[]{cert1, cert3})); - assertFalse(certificateValidator.isTrusted(new X509Certificate[]{cert2, cert3})); + assertFalse(certificateValidator.hasGatewayChain(new X509Certificate[]{cert3})); + assertFalse(certificateValidator.hasGatewayChain(new X509Certificate[]{cert1, cert3})); + assertFalse(certificateValidator.hasGatewayChain(new X509Certificate[]{cert2, cert3})); } } @@ -82,9 +82,9 @@ void setUp() { } @Test void thenAnyCertificateIsNotTrusted() { - assertFalse(certificateValidator.isTrusted(new X509Certificate[]{cert1})); - assertFalse(certificateValidator.isTrusted(new X509Certificate[]{cert2})); - assertFalse(certificateValidator.isTrusted(new X509Certificate[]{cert3})); + assertFalse(certificateValidator.hasGatewayChain(new X509Certificate[]{cert1})); + assertFalse(certificateValidator.hasGatewayChain(new X509Certificate[]{cert2})); + assertFalse(certificateValidator.hasGatewayChain(new X509Certificate[]{cert3})); } } @@ -119,10 +119,10 @@ void setUp() { @Test void whenAllCertificatesFoundThenTheyAreTrusted() { - assertTrue(certificateValidator.isTrusted(new X509Certificate[]{cert1})); - assertTrue(certificateValidator.isTrusted(new X509Certificate[]{cert2})); - assertTrue(certificateValidator.isTrusted(new X509Certificate[]{cert3})); - assertTrue(certificateValidator.isTrusted(new X509Certificate[]{cert1, cert3})); + assertTrue(certificateValidator.hasGatewayChain(new X509Certificate[]{cert1})); + assertTrue(certificateValidator.hasGatewayChain(new X509Certificate[]{cert2})); + assertTrue(certificateValidator.hasGatewayChain(new X509Certificate[]{cert3})); + assertTrue(certificateValidator.hasGatewayChain(new X509Certificate[]{cert1, cert3})); } } diff --git a/gateway-service/src/main/java/org/zowe/apiml/gateway/x509/AcceptForwardedClientCertFilterFactory.java b/gateway-service/src/main/java/org/zowe/apiml/gateway/x509/AcceptForwardedClientCertFilterFactory.java index 7bf1fb8e13..54826b3651 100644 --- a/gateway-service/src/main/java/org/zowe/apiml/gateway/x509/AcceptForwardedClientCertFilterFactory.java +++ b/gateway-service/src/main/java/org/zowe/apiml/gateway/x509/AcceptForwardedClientCertFilterFactory.java @@ -60,7 +60,7 @@ public GatewayFilter apply(AcceptForwardedClientCertFilterFactory.Config config) return (exchange, chain) -> { SslInfo sslInfo = exchange.getRequest().getSslInfo(); X509Certificate[] x509Certificates = sslInfo == null ? null : sslInfo.getPeerCertificates(); - if ((x509Certificates != null) && (x509Certificates.length > 0) && certificateValidator.isTrusted(x509Certificates)) { + if ((x509Certificates != null) && (x509Certificates.length > 0) && certificateValidator.hasGatewayChain(x509Certificates)) { X509Certificate[] forwardedClientCertificate = getClientCertificateFromHeader(exchange.getRequest()); if (forwardedClientCertificate.length > 0) { log.debug("Accepting forwarded client certificate {}", forwardedClientCertificate[0].getSubjectX500Principal().getName()); diff --git a/gateway-service/src/test/java/org/zowe/apiml/gateway/x509/AcceptForwardedClientCertFilterFactoryTest.java b/gateway-service/src/test/java/org/zowe/apiml/gateway/x509/AcceptForwardedClientCertFilterFactoryTest.java index e542a7c638..6f9b3d28b8 100644 --- a/gateway-service/src/test/java/org/zowe/apiml/gateway/x509/AcceptForwardedClientCertFilterFactoryTest.java +++ b/gateway-service/src/test/java/org/zowe/apiml/gateway/x509/AcceptForwardedClientCertFilterFactoryTest.java @@ -64,7 +64,7 @@ class AcceptForwardedClientCertFilterFactoryTest { @Test void givenClientCertificateInHeader_andTrustedClientCertificateInHandshake_thenUpdateWebExchange() { var validator = mock(CertificateValidator.class); - when(validator.isTrusted(any())).thenReturn(true); + when(validator.hasGatewayChain(any())).thenReturn(true); var factory = new AcceptForwardedClientCertFilterFactory(validator); var exchange = mock(ServerWebExchange.class); var req = mock(ServerHttpRequest.class); @@ -89,7 +89,7 @@ void givenClientCertificateInHeader_andTrustedClientCertificateInHandshake_thenU @Test void givenClientCertificateInHeader_andInvalidClientCertificateInHandshake_thenDoNothing() { var validator = mock(CertificateValidator.class); - when(validator.isTrusted(any())).thenReturn(false); + when(validator.hasGatewayChain(any())).thenReturn(false); var factory = new AcceptForwardedClientCertFilterFactory(validator); var exchange = mock(ServerWebExchange.class); var req = mock(ServerHttpRequest.class); diff --git a/zaas-service/src/main/java/org/zowe/apiml/zaas/security/config/NewSecurityConfiguration.java b/zaas-service/src/main/java/org/zowe/apiml/zaas/security/config/NewSecurityConfiguration.java index 610501471a..eab0ceccd0 100644 --- a/zaas-service/src/main/java/org/zowe/apiml/zaas/security/config/NewSecurityConfiguration.java +++ b/zaas-service/src/main/java/org/zowe/apiml/zaas/security/config/NewSecurityConfiguration.java @@ -35,6 +35,7 @@ import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.authentication.logout.HttpStatusReturningLogoutSuccessHandler; import org.springframework.security.web.authentication.logout.LogoutHandler; +import org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.zowe.apiml.filter.AttlsFilter; import org.zowe.apiml.filter.SecureConnectionFilter; @@ -48,6 +49,7 @@ import org.zowe.apiml.security.common.error.AuthExceptionHandler; import org.zowe.apiml.security.common.filter.CategorizeCertsFilter; import org.zowe.apiml.security.common.filter.StoreAccessTokenInfoFilter; +import org.zowe.apiml.security.common.filter.X509ClientRejectIfMissingFilter; import org.zowe.apiml.security.common.handler.FailedAccessTokenHandler; import org.zowe.apiml.security.common.handler.FailedAuthenticationHandler; import org.zowe.apiml.security.common.handler.SuccessfulAccessTokenHandler; @@ -368,12 +370,11 @@ public SecurityFilterChain ticketFilterChain(HttpSecurity http) throws Exception return baseConfigure(http.securityMatchers(matchers -> matchers.requestMatchers( authConfigurationProperties.getZaasTicketEndpoint() ))).authorizeHttpRequests(requests -> requests.anyRequest().authenticated()) - .addFilterBefore(new CategorizeCertsFilter(publicKeyCertificatesBase64, certificateValidator, authExceptionHandler), org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter.class) + .addFilterBefore(new CategorizeCertsFilter(publicKeyCertificatesBase64, certificateValidator, authExceptionHandler), X509AuthenticationFilter.class) + .addFilterAfter(new X509ClientRejectIfMissingFilter(authExceptionHandler), CategorizeCertsFilter.class) .authenticationProvider(tokenAuthenticationProvider) - .logout(logout -> logout.disable()) // logout filter in this chain not needed - .x509(x509 -> x509 //default x509 filter, authenticates trusted cert, ticketFilter(..) depends on this - .userDetailsService(new SimpleUserDetailService()) - ).with(new CustomSecurityFilters(), Customizer.withDefaults()) + .logout(AbstractHttpConfigurer::disable) // logout filter in this chain not needed + .with(new CustomSecurityFilters(), withDefaults()) .build(); } @@ -502,7 +503,7 @@ public SecurityFilterChain certificateOrAuthEndpointsFilterChain(HttpSecurity ht .anyRequest() .authenticated() ) - .logout(logout -> logout.disable()); // logout filter in this chain not needed + .logout(AbstractHttpConfigurer::disable); // logout filter in this chain not needed if (isAttlsEnabled) { http.x509(withDefaults()) From a3595fe32557b0c66cd9bbe322c123607ab6f371 Mon Sep 17 00:00:00 2001 From: Richard Salac Date: Fri, 31 Jan 2025 15:10:15 +0100 Subject: [PATCH 04/11] fix: replace default spring x509 authentication in zaas Signed-off-by: Richard Salac --- .../common/auth/saf/SafResourceAccessSaf.java | 1 + .../common/filter/CategorizeCertsFilter.java | 15 ++--- .../X509ClientRejectIfMissingFilter.java | 34 ++++++++---- ...pulsoryAuthenticationProcessingFilter.java | 3 +- .../common/verify/CertificateValidator.java | 6 +- .../filter/CategorizeCertsFilterTest.java | 55 +++++++++---------- .../X509ClientRejectIfMissingFilterTest.java | 44 ++++++++++++--- .../verify/CertificateValidatorTest.java | 10 ++-- .../gateway/config/AuthEndpointConfig.java | 4 +- .../pat/AccessTokenServiceTest.java | 6 +- ...egativeTest.java => ZaasNegativeTest.java} | 12 ++-- .../apiml/integration/zos/PassTicketTest.java | 12 ++++ .../org/zowe/apiml/zaas/ZaasApplication.java | 2 +- .../config/NewSecurityConfiguration.java | 27 ++++++--- .../zaas/zaas/ZaasAuthenticationFilter.java | 12 ++-- .../org/zowe/apiml/acceptance/ZaasTest.java | 2 +- .../zaas/ZaasAuthenticationFilterTest.java | 8 +-- .../zaas/zaas/ZaasExceptionHandlerTest.java | 37 ++++++------- .../src/test/resources/application-test.yml | 2 +- .../src/test/resources/application.yml | 2 +- 20 files changed, 175 insertions(+), 119 deletions(-) rename integration-tests/src/test/java/org/zowe/apiml/integration/zaas/{ZaasSchemeNegativeTest.java => ZaasNegativeTest.java} (92%) diff --git a/apiml-security-common/src/main/java/org/zowe/apiml/security/common/auth/saf/SafResourceAccessSaf.java b/apiml-security-common/src/main/java/org/zowe/apiml/security/common/auth/saf/SafResourceAccessSaf.java index ab01c2d5d4..6d1429f0b5 100644 --- a/apiml-security-common/src/main/java/org/zowe/apiml/security/common/auth/saf/SafResourceAccessSaf.java +++ b/apiml-security-common/src/main/java/org/zowe/apiml/security/common/auth/saf/SafResourceAccessSaf.java @@ -99,6 +99,7 @@ public boolean hasSafResourceAccess(Authentication authentication, String resour String userid = authentication.getName(); if (StringUtils.isEmpty(userid) || userid.length() > 8) { log.debug("UserId {} is not valid for SAF permissions check", userid); + return false; } AccessLevel level = AccessLevel.valueOf(accessLevel); log.debug("Evaluating access of user {} to resource {} in class {} level {}", userid, resourceClass, resourceName, level); diff --git a/apiml-security-common/src/main/java/org/zowe/apiml/security/common/filter/CategorizeCertsFilter.java b/apiml-security-common/src/main/java/org/zowe/apiml/security/common/filter/CategorizeCertsFilter.java index db59a0e9ac..21e51e540f 100644 --- a/apiml-security-common/src/main/java/org/zowe/apiml/security/common/filter/CategorizeCertsFilter.java +++ b/apiml-security-common/src/main/java/org/zowe/apiml/security/common/filter/CategorizeCertsFilter.java @@ -55,9 +55,10 @@ public class CategorizeCertsFilter extends OncePerRequestFilter { @Getter private final Set publicKeyCertificatesBase64; + //TODO:cleanup private final CertificateValidator certificateValidator; - private final AuthExceptionHandler authExceptionHandler; + final AuthExceptionHandler authExceptionHandler; private final boolean rejectIfZoweCertificateMissing; @@ -78,7 +79,7 @@ public CategorizeCertsFilter(Set publicKeyCertificatesBase64, Certificat */ private void categorizeCerts(ServletRequest request) { X509Certificate[] certs = (X509Certificate[]) request.getAttribute(ATTRNAME_JAKARTA_SERVLET_REQUEST_X509_CERTIFICATE); - if (certs != null && certs.length > 0) { + if (certs != null && certs.length > 0 && certs[0] != null) { Optional clientCert = getClientCertFromHeader((HttpServletRequest) request); if (certificateValidator.isForwardingEnabled() && certificateValidator.hasGatewayChain(certs) && clientCert.isPresent()) { certificateValidator.updateAPIMLPublicKeyCertificates(certs); @@ -93,14 +94,14 @@ private void categorizeCerts(ServletRequest request) { log.debug(LOG_FORMAT_FILTERING_CERTIFICATES, ATTRNAME_CLIENT_AUTH_X509_CERTIFICATE, request.getAttribute(ATTRNAME_CLIENT_AUTH_X509_CERTIFICATE)); - } else if (rejectIfZoweCertificateMissing) { - log.debug("No X509 certificate found in request attribute {}", ATTRNAME_JAKARTA_SERVLET_REQUEST_X509_CERTIFICATE); - throw new InsufficientAuthenticationException("No Zowe Server X509 certificate found in request"); +// } else if (rejectIfZoweCertificateMissing) { +// log.debug("No X509 certificate found in request attribute {}", ATTRNAME_JAKARTA_SERVLET_REQUEST_X509_CERTIFICATE); +// throw new InsufficientAuthenticationException("No Zowe Server X509 certificate found in request"); } } - private Optional getClientCertFromHeader(HttpServletRequest request) { + Optional getClientCertFromHeader(HttpServletRequest request) { String certFromHeader = request.getHeader(CLIENT_CERT_HEADER); if (StringUtils.isNotEmpty(certFromHeader)) { @@ -123,7 +124,7 @@ private Optional getClientCertFromHeader(HttpServletRequest request * @param originalRequest incoming original http request object * @return wrapped http request object with overridden functions */ - private HttpServletRequest mutate(HttpServletRequest originalRequest) { + HttpServletRequest mutate(HttpServletRequest originalRequest) { return new HttpServletRequestWrapper(originalRequest) { @Override diff --git a/apiml-security-common/src/main/java/org/zowe/apiml/security/common/filter/X509ClientRejectIfMissingFilter.java b/apiml-security-common/src/main/java/org/zowe/apiml/security/common/filter/X509ClientRejectIfMissingFilter.java index 7c27d4e28d..67057be665 100644 --- a/apiml-security-common/src/main/java/org/zowe/apiml/security/common/filter/X509ClientRejectIfMissingFilter.java +++ b/apiml-security-common/src/main/java/org/zowe/apiml/security/common/filter/X509ClientRejectIfMissingFilter.java @@ -14,32 +14,44 @@ import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.web.filter.OncePerRequestFilter; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; import org.zowe.apiml.security.common.error.AuthExceptionHandler; import org.zowe.apiml.security.common.error.InvalidCertificateException; +import org.zowe.apiml.security.common.verify.CertificateValidator; import java.io.IOException; import java.security.cert.X509Certificate; +import java.util.HashSet; -@Slf4j -@RequiredArgsConstructor -public class X509ClientRejectIfMissingFilter extends OncePerRequestFilter { - - static final String ATTRNAME_CLIENT_AUTH_X509_CERTIFICATE = "client.auth.X509Certificate"; +/** + * Checks the client certificate is present in the request. No further validation is done, the client certificate + * was already validated by Gateway. Servers as a counterpart to {@link CategorizeCertsFilter} for simple scenarios + * when client certificate is required to be present but is not used for authorization. + */ - private final AuthExceptionHandler authExceptionHandler; +@Slf4j +public class X509ClientRejectIfMissingFilter extends CategorizeCertsFilter { + public X509ClientRejectIfMissingFilter(CertificateValidator certificateValidator, AuthExceptionHandler authExceptionHandler) { + super(new HashSet<>(), certificateValidator, authExceptionHandler, true); + } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { - X509Certificate[] certs = (X509Certificate[]) request.getAttribute(ATTRNAME_CLIENT_AUTH_X509_CERTIFICATE); - if (certs == null || certs.length == 0) { + var certOpt = getClientCertFromHeader(request); + if (certOpt.isEmpty()) { log.debug("No X509 client certificate found in request."); authExceptionHandler.handleException(request, response, new InvalidCertificateException("X509 client certificate required but missing.")); return; } - filterChain.doFilter(request, response); + + var cert = (X509Certificate) certOpt.get(); + var auth = new PreAuthenticatedAuthenticationToken(cert.getSubjectX500Principal(), cert); + auth.setAuthenticated(true); + SecurityContextHolder.getContext().setAuthentication(auth); + + filterChain.doFilter(mutate(request), response); } } diff --git a/apiml-security-common/src/main/java/org/zowe/apiml/security/common/login/NonCompulsoryAuthenticationProcessingFilter.java b/apiml-security-common/src/main/java/org/zowe/apiml/security/common/login/NonCompulsoryAuthenticationProcessingFilter.java index 364540190f..1442edc142 100644 --- a/apiml-security-common/src/main/java/org/zowe/apiml/security/common/login/NonCompulsoryAuthenticationProcessingFilter.java +++ b/apiml-security-common/src/main/java/org/zowe/apiml/security/common/login/NonCompulsoryAuthenticationProcessingFilter.java @@ -44,8 +44,7 @@ public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) try { authResult = attemptAuthentication(request, response); - } - catch (AuthenticationException failed) { + } catch (AuthenticationException failed) { // Authentication failed unsuccessfulAuthentication(request, response, failed); return; diff --git a/apiml-security-common/src/main/java/org/zowe/apiml/security/common/verify/CertificateValidator.java b/apiml-security-common/src/main/java/org/zowe/apiml/security/common/verify/CertificateValidator.java index db2131035c..c37a189468 100644 --- a/apiml-security-common/src/main/java/org/zowe/apiml/security/common/verify/CertificateValidator.java +++ b/apiml-security-common/src/main/java/org/zowe/apiml/security/common/verify/CertificateValidator.java @@ -62,9 +62,9 @@ public boolean hasGatewayChain(X509Certificate[] certs) { return false; } List trustedCerts = Arrays.stream(proxyCertificatesEndpoints) - .map(trustedCertificatesProvider::getTrustedCerts) - .flatMap(List::stream) - .toList(); + .map(trustedCertificatesProvider::getTrustedCerts) + .flatMap(List::stream) + .toList(); for (X509Certificate cert : certs) { if (!trustedCerts.contains(cert)) { log.debug("Certificate is not trusted by endpoint {}. Untrusted certificate is {}", proxyCertificatesEndpoints, cert); diff --git a/apiml-security-common/src/test/java/org/zowe/apiml/security/common/filter/CategorizeCertsFilterTest.java b/apiml-security-common/src/test/java/org/zowe/apiml/security/common/filter/CategorizeCertsFilterTest.java index 14a1cb2ca1..38fb36c9e9 100644 --- a/apiml-security-common/src/test/java/org/zowe/apiml/security/common/filter/CategorizeCertsFilterTest.java +++ b/apiml-security-common/src/test/java/org/zowe/apiml/security/common/filter/CategorizeCertsFilterTest.java @@ -16,11 +16,9 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -import org.mockito.ArgumentCaptor; import org.springframework.mock.web.MockFilterChain; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; -import org.springframework.security.authentication.InsufficientAuthenticationException; import org.zowe.apiml.security.common.error.AuthExceptionHandler; import org.zowe.apiml.security.common.utils.X509Utils; import org.zowe.apiml.security.common.verify.CertificateValidator; @@ -494,30 +492,31 @@ void thenClientCertHeaderIgnored() throws ServletException, IOException { } } - @Nested - class WhenNoZoweServerCertificateProvided { - - private MockHttpServletRequest requestWithNoCertificates; - - @BeforeEach - void setup() { - requestWithNoCertificates = new MockHttpServletRequest(); - } - - @Test - void givenRejectOnMissingZoweServerCertificateEnabled_thenThrowException() throws ServletException, IOException { - var rejectOnFilter = new CategorizeCertsFilter(new HashSet<>(), certificateValidator, authExceptionHandler); - ArgumentCaptor argument = ArgumentCaptor.forClass(RuntimeException.class); - rejectOnFilter.doFilter(requestWithNoCertificates, response, chain); - verify(authExceptionHandler, times(1)).handleException(any(), any(), argument.capture()); - assertInstanceOf(InsufficientAuthenticationException.class, argument.getValue()); - } - - @Test - void givenRejectOnMissingZoweServerCertificateDisabled_thenDoNotThrowException() throws ServletException, IOException { - var rejectOffFilter = new CategorizeCertsFilter(new HashSet<>(), certificateValidator, authExceptionHandler, false); - rejectOffFilter.doFilter(request, response, chain); - verify(authExceptionHandler, never()).handleException(any(), any(), any()); - } - } + //TODO: validate if needed +// @Nested +// class WhenNoZoweServerCertificateProvided { +// +// private MockHttpServletRequest requestWithNoCertificates; +// +// @BeforeEach +// void setup() { +// requestWithNoCertificates = new MockHttpServletRequest(); +// } +// +// @Test +// void givenRejectOnMissingZoweServerCertificateEnabled_thenThrowException() throws ServletException, IOException { +// var rejectOnFilter = new CategorizeCertsFilter(new HashSet<>(), certificateValidator, authExceptionHandler); +// ArgumentCaptor argument = ArgumentCaptor.forClass(RuntimeException.class); +// rejectOnFilter.doFilter(requestWithNoCertificates, response, chain); +// verify(authExceptionHandler, times(1)).handleException(any(), any(), argument.capture()); +// assertInstanceOf(InsufficientAuthenticationException.class, argument.getValue()); +// } +// +// @Test +// void givenRejectOnMissingZoweServerCertificateDisabled_thenDoNotThrowException() throws ServletException, IOException { +// var rejectOffFilter = new CategorizeCertsFilter(new HashSet<>(), certificateValidator, authExceptionHandler, false); +// rejectOffFilter.doFilter(request, response, chain); +// verify(authExceptionHandler, never()).handleException(any(), any(), any()); +// } +// } } diff --git a/apiml-security-common/src/test/java/org/zowe/apiml/security/common/filter/X509ClientRejectIfMissingFilterTest.java b/apiml-security-common/src/test/java/org/zowe/apiml/security/common/filter/X509ClientRejectIfMissingFilterTest.java index a415cacc7e..d37a57cf6f 100644 --- a/apiml-security-common/src/test/java/org/zowe/apiml/security/common/filter/X509ClientRejectIfMissingFilterTest.java +++ b/apiml-security-common/src/test/java/org/zowe/apiml/security/common/filter/X509ClientRejectIfMissingFilterTest.java @@ -19,35 +19,61 @@ import org.springframework.mock.web.MockFilterChain; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.security.core.context.SecurityContextHolder; import org.zowe.apiml.security.common.error.AuthExceptionHandler; import org.zowe.apiml.security.common.error.InvalidCertificateException; -import org.zowe.apiml.security.common.utils.X509Utils; +import org.zowe.apiml.security.common.verify.CertificateValidator; import java.io.IOException; -import java.security.cert.X509Certificate; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.*; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; -import static org.zowe.apiml.security.common.filter.X509ClientRejectIfMissingFilter.ATTRNAME_CLIENT_AUTH_X509_CERTIFICATE; class X509ClientRejectIfMissingFilterTest { + private static final String CLIENT_CERT_HEADER = "Client-Cert"; + private static final String CLIENT_CERT_HEADER_VALUE = + "MIIEFTCCAv2gAwIBAgIEKWdbVTANBgkqhkiG9w0BAQsFADCBjDELMAkGA1UEBhMC" + + "Q1oxDTALBgNVBAgTBEJybm8xDTALBgNVBAcTBEJybm8xFDASBgNVBAoTC1pvd2Ug" + + "U2FtcGxlMRwwGgYDVQQLExNBUEkgTWVkaWF0aW9uIExheWVyMSswKQYDVQQDEyJa" + + "b3dlIFNlbGYtU2lnbmVkIFVudHJ1c3RlZCBTZXJ2aWNlMB4XDTE4MTIwNzIwMDc1" + + "MloXDTI4MTIwNDIwMDc1MlowgYwxCzAJBgNVBAYTAkNaMQ0wCwYDVQQIEwRCcm5v" + + "MQ0wCwYDVQQHEwRCcm5vMRQwEgYDVQQKEwtab3dlIFNhbXBsZTEcMBoGA1UECxMT" + + "QVBJIE1lZGlhdGlvbiBMYXllcjErMCkGA1UEAxMiWm93ZSBTZWxmLVNpZ25lZCBV" + + "bnRydXN0ZWQgU2VydmljZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB" + + "AJti8p4nr8ztRSbemrAv1ytVLQMbXozhLe3lNaiVADGTFPZYeJ2lDt7oAl238HOY" + + "ScpOz+JjTeUkL0jsjNYgMhi4J07II/3sJL0SBfVqvvgjUL4BvcpdBl0crSuI/3D4" + + "OaPue+ZmPFijwdCcw5JbazMoOka/zUwpYYdbwxPUH2BbKfwtmmygX88nkJcRSoQO" + + "KBdNsUs+QRuUiokZ/FJi7uiOsNZ8eEfQv6qJ7mOJ7l1IrMcNm3jHgodoQi/4jXO1" + + "np/hZaz/ZDni9kBwcyd64AViB2v7VrrBmjdESt1mtCIMvKMlwAZAqrDO75Q9pepO" + + "Y7zbN4s9s7IUfyb9431xg2MCAwEAAaN9MHswHQYDVR0lBBYwFAYIKwYBBQUHAwIG" + + "CCsGAQUFBwMBMA4GA1UdDwEB/wQEAwIE8DArBgNVHREEJDAighVsb2NhbGhvc3Qu" + + "bG9jYWxkb21haW6CCWxvY2FsaG9zdDAdBgNVHQ4EFgQUIeSN7aNtwH2MnBAGDLre" + + "TtcSaZ4wDQYJKoZIhvcNAQELBQADggEBAELPbHlG60nO164yrBjZcpQJ/2e5ThOR" + + "8efXUWExuy/NpwVx0vJg4tb8s9NI3X4pRh3WyD0uGPGkO9w+CAvgUaECePLYjkov" + + "KIS6Cvlcav9nWqdZau1fywltmOLu8Sq5i42Yvb7ZcPOEwDShpuq0ql7LR7j7P4XH" + + "+JkA0k9Zi6RfYJAyOOpbD2R4JoMbxBKrxUVs7cEajl2ltckjyRWoB6FBud1IthRR" + + "mZoPMtlCleKlsKp7yJiE13hpX+qIGnzEQE2gNgQ94dSl4m2xO6pnyDRMAEncmd33" + + "oehy77omRxNsLzkWe6mjaC8ShMGzG9jYR02iN2h4083/PVXvTZIqwhg="; + private MockHttpServletRequest request; private MockHttpServletResponse response; private MockFilterChain chain; private AuthExceptionHandler authExceptionHandler; private Filter nextFilter; - - private final X509Certificate[] x509Certificate = - new X509Certificate[]{X509Utils.getCertificate(X509Utils.correctBase64("zowe"), "CN=user"),}; + private CertificateValidator certificateValidator; @BeforeEach void setUp() { authExceptionHandler = Mockito.mock(AuthExceptionHandler.class); + certificateValidator = Mockito.mock(CertificateValidator.class); request = new MockHttpServletRequest(); response = new MockHttpServletResponse(); nextFilter = Mockito.mock(Filter.class); - chain = new MockFilterChain(Mockito.mock(Servlet.class), new X509ClientRejectIfMissingFilter(authExceptionHandler), nextFilter); + chain = new MockFilterChain(Mockito.mock(Servlet.class), new X509ClientRejectIfMissingFilter(certificateValidator, authExceptionHandler), nextFilter); + SecurityContextHolder.clearContext(); } @Test @@ -56,13 +82,15 @@ void whenClientCertMissing_thenRejectAndStop() throws ServletException, IOExcept verify(authExceptionHandler, times(1)) .handleException(any(), any(), any(InvalidCertificateException.class)); verify(nextFilter, never()).doFilter(any(), any(), eq(chain)); + assertThat(SecurityContextHolder.getContext().getAuthentication(), is(nullValue())); } @Test void whenClientCertPresent_thenAllowAndContinue() throws ServletException, IOException { - request.setAttribute(ATTRNAME_CLIENT_AUTH_X509_CERTIFICATE, x509Certificate); + request.addHeader(CLIENT_CERT_HEADER, CLIENT_CERT_HEADER_VALUE); chain.doFilter(request, response); verify(authExceptionHandler, never()).handleException(any(), any(), any()); verify(nextFilter, times(1)).doFilter(any(), any(), eq(chain)); + assertThat(SecurityContextHolder.getContext().getAuthentication().getCredentials(), is(notNullValue())); } } diff --git a/apiml-security-common/src/test/java/org/zowe/apiml/security/common/verify/CertificateValidatorTest.java b/apiml-security-common/src/test/java/org/zowe/apiml/security/common/verify/CertificateValidatorTest.java index 0f7da43631..7b297fc619 100644 --- a/apiml-security-common/src/test/java/org/zowe/apiml/security/common/verify/CertificateValidatorTest.java +++ b/apiml-security-common/src/test/java/org/zowe/apiml/security/common/verify/CertificateValidatorTest.java @@ -56,7 +56,7 @@ class WhenTrustedCertsProvided { @BeforeEach void setUp() { - ReflectionTestUtils.setField(certificateValidator, "proxyCertificatesEndpoints", new String[] {URL_PROVIDE_TWO_TRUSTED_CERTS}); + ReflectionTestUtils.setField(certificateValidator, "proxyCertificatesEndpoints", new String[]{URL_PROVIDE_TWO_TRUSTED_CERTS}); } @Test void whenAllCertificatesFoundThenTheyAreTrusted() { @@ -78,7 +78,7 @@ class WhenNoTrustedCertsProvided { @BeforeEach void setUp() { - ReflectionTestUtils.setField(certificateValidator, "proxyCertificatesEndpoints", new String[] {URL_WITH_NO_TRUSTED_CERTS}); + ReflectionTestUtils.setField(certificateValidator, "proxyCertificatesEndpoints", new String[]{URL_WITH_NO_TRUSTED_CERTS}); } @Test void thenAnyCertificateIsNotTrusted() { @@ -114,7 +114,7 @@ class WhenMultipleSources { @BeforeEach void setUp() { - ReflectionTestUtils.setField(certificateValidator, "proxyCertificatesEndpoints", new String[] {URL_PROVIDE_TWO_TRUSTED_CERTS, URL_PROVIDE_THIRD_TRUSTED_CERT}); + ReflectionTestUtils.setField(certificateValidator, "proxyCertificatesEndpoints", new String[]{URL_PROVIDE_TWO_TRUSTED_CERTS, URL_PROVIDE_THIRD_TRUSTED_CERT}); } @Test @@ -144,7 +144,7 @@ class OldPropertyValue { @Test void thenUrlIsSetAsListCorrectly() { - assertArrayEquals(new String[] {"url1"}, (String[]) ReflectionTestUtils.getField(certificateValidator, "proxyCertificatesEndpoints")); + assertArrayEquals(new String[]{"url1"}, (String[]) ReflectionTestUtils.getField(certificateValidator, "proxyCertificatesEndpoints")); } } @@ -161,7 +161,7 @@ class NewPropertyValue { @Test void thenUrlsAreSetCorrectly() { - assertArrayEquals(new String[] {"url1", "url2"}, (String[]) ReflectionTestUtils.getField(certificateValidator, "proxyCertificatesEndpoints")); + assertArrayEquals(new String[]{"url1", "url2"}, (String[]) ReflectionTestUtils.getField(certificateValidator, "proxyCertificatesEndpoints")); } } diff --git a/gateway-service/src/main/java/org/zowe/apiml/gateway/config/AuthEndpointConfig.java b/gateway-service/src/main/java/org/zowe/apiml/gateway/config/AuthEndpointConfig.java index 4bd1da8d70..0a00696007 100644 --- a/gateway-service/src/main/java/org/zowe/apiml/gateway/config/AuthEndpointConfig.java +++ b/gateway-service/src/main/java/org/zowe/apiml/gateway/config/AuthEndpointConfig.java @@ -78,8 +78,8 @@ private WebClient createLoadBalanced(WebClient webClient, ReactiveLoadBalancer.F private WebClient.RequestBodySpec getWebclient(ServerRequest serverRequest, String path) { var sslInfo = serverRequest.exchange().getRequest().getSslInfo(); //TODO: remove after checking integration tests - //var client = sslInfo == null ? this.webClient : this.webClientClientCert; - var client = this.webClientClientCert; + var client = sslInfo == null ? this.webClient : this.webClientClientCert; + //var client = this.webClientClientCert; var request = client .method(serverRequest.method()) diff --git a/integration-tests/src/test/java/org/zowe/apiml/integration/authentication/pat/AccessTokenServiceTest.java b/integration-tests/src/test/java/org/zowe/apiml/integration/authentication/pat/AccessTokenServiceTest.java index 725868aabb..7610d336c6 100644 --- a/integration-tests/src/test/java/org/zowe/apiml/integration/authentication/pat/AccessTokenServiceTest.java +++ b/integration-tests/src/test/java/org/zowe/apiml/integration/authentication/pat/AccessTokenServiceTest.java @@ -14,7 +14,6 @@ import io.restassured.http.ContentType; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.zowe.apiml.util.SecurityUtils; @@ -288,7 +287,6 @@ void givenAuthorizedRequest_thenEvictRules() { } @Test - @Disabled("Disable for now; fix is in progress") void givenNotAuthorizedCall_thenDontAllowToRevokeTokensForUser() { String pat = SecurityUtils.personalAccessTokenWithClientCert(SslContext.clientCertValid); bodyContent = new ValidateRequestModel(); @@ -315,12 +313,12 @@ void givenNotAuthorizedCall_thenDontAllowToRevokeTokensForUser() { requestBody.put("userId", SecurityUtils.USERNAME); given() .contentType(ContentType.JSON) - .config(SslContext.clientCertApiml) + .config(SslContext.clientCertUnknownUser) .body(requestBody) .when() .delete(REVOKE_FOR_USER_ENDPOINT) .then() - .statusCode(403); + .statusCode(401); } } diff --git a/integration-tests/src/test/java/org/zowe/apiml/integration/zaas/ZaasSchemeNegativeTest.java b/integration-tests/src/test/java/org/zowe/apiml/integration/zaas/ZaasNegativeTest.java similarity index 92% rename from integration-tests/src/test/java/org/zowe/apiml/integration/zaas/ZaasSchemeNegativeTest.java rename to integration-tests/src/test/java/org/zowe/apiml/integration/zaas/ZaasNegativeTest.java index 417de3b625..10e9ebc9f9 100644 --- a/integration-tests/src/test/java/org/zowe/apiml/integration/zaas/ZaasSchemeNegativeTest.java +++ b/integration-tests/src/test/java/org/zowe/apiml/integration/zaas/ZaasNegativeTest.java @@ -40,7 +40,7 @@ import static org.zowe.apiml.util.SecurityUtils.*; @ZaasTest -public class ZaasSchemeNegativeTest { +public class ZaasNegativeTest { private final static String APPLICATION_NAME = ConfigReader.environmentConfiguration().getDiscoverableClientConfiguration().getApplId(); private final static SafIdtConfiguration SAFIDT_CONF = ConfigReader.environmentConfiguration().getSafIdtConfiguration(); @@ -115,7 +115,7 @@ void setUpCertificateAndToken() { } @ParameterizedTest - @MethodSource("org.zowe.apiml.integration.zaas.ZaasSchemeNegativeTest#provideZaasEndpoints") + @MethodSource("org.zowe.apiml.integration.zaas.ZaasNegativeTest#provideZaasEndpoints") void givenNoToken(URI uri, RequestSpecification requestSpecification) { //@formatter:off requestSpecification @@ -127,7 +127,7 @@ void givenNoToken(URI uri, RequestSpecification requestSpecification) { } @ParameterizedTest - @MethodSource("org.zowe.apiml.integration.zaas.ZaasSchemeNegativeTest#provideZaasEndpointsWithAllTokens") + @MethodSource("org.zowe.apiml.integration.zaas.ZaasNegativeTest#provideZaasEndpointsWithAllTokens") void givenInvalidToken(URI uri, RequestSpecification requestSpecification, String token) { //@formatter:off requestSpecification @@ -140,7 +140,7 @@ void givenInvalidToken(URI uri, RequestSpecification requestSpecification, Strin } @ParameterizedTest - @MethodSource("org.zowe.apiml.integration.zaas.ZaasSchemeNegativeTest#provideZaasEndpoints") + @MethodSource("org.zowe.apiml.integration.zaas.ZaasNegativeTest#provideZaasEndpoints") void givenOKTATokenWithNoMapping(URI uri, RequestSpecification requestSpecification) { String oktaTokenNoMapping = SecurityUtils.validOktaAccessToken(false); //@formatter:off @@ -165,7 +165,7 @@ class GivenBadCertificate { String keystore; @ParameterizedTest - @MethodSource("org.zowe.apiml.integration.zaas.ZaasSchemeNegativeTest#provideZaasEndpoints") + @MethodSource("org.zowe.apiml.integration.zaas.ZaasNegativeTest#provideZaasEndpoints") void givenNoCertificate_thenReturnUnauthorized(URI uri, RequestSpecification requestSpecification) { //@formatter:off requestSpecification @@ -179,7 +179,7 @@ void givenNoCertificate_thenReturnUnauthorized(URI uri, RequestSpecification req } @ParameterizedTest - @MethodSource("org.zowe.apiml.integration.zaas.ZaasSchemeNegativeTest#provideZaasTokenEndpoints") + @MethodSource("org.zowe.apiml.integration.zaas.ZaasNegativeTest#provideZaasTokenEndpoints") void givenClientAndHeaderCertificates_thenReturnTokenFromClientCert(URI uri, RequestSpecification requestSpecification) throws Exception { TlsConfiguration tlsCfg = ConfigReader.environmentConfiguration().getTlsConfiguration(); SslContextConfigurer sslContextConfigurer = new SslContextConfigurer(tlsCfg.getKeyStorePassword(), tlsCfg.getClientKeystore(), tlsCfg.getKeyStore()); diff --git a/integration-tests/src/test/java/org/zowe/apiml/integration/zos/PassTicketTest.java b/integration-tests/src/test/java/org/zowe/apiml/integration/zos/PassTicketTest.java index a72e97914b..a023b53b25 100644 --- a/integration-tests/src/test/java/org/zowe/apiml/integration/zos/PassTicketTest.java +++ b/integration-tests/src/test/java/org/zowe/apiml/integration/zos/PassTicketTest.java @@ -11,6 +11,8 @@ package org.zowe.apiml.integration.zos; import io.restassured.RestAssured; +import io.restassured.config.RestAssuredConfig; +import io.restassured.config.SSLConfig; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -205,6 +207,11 @@ void givenInvalidApplicationName() { @Nested class ReturnForbidden { + @BeforeEach + void resetCertsToNone() { + RestAssured.config = RestAssuredConfig.newConfig().sslConfig(SSLConfig.sslConfig().relaxedHTTPSValidation()); + } + @Test void givenNoCertificate() { given() @@ -220,6 +227,11 @@ void givenNoCertificate() { @Nested class ReturnMethodNotAllowed { + @BeforeEach + void setUpCertificate() { + RestAssured.config = RestAssured.config().sslConfig(getConfiguredSslConfig()); + } + @Test void givenInvalidHttpMethod() { String expectedMessage = "Authentication method 'GET' is not supported for URL '" + ZAAS_PASSTICKET_PATH + "'"; diff --git a/zaas-service/src/main/java/org/zowe/apiml/zaas/ZaasApplication.java b/zaas-service/src/main/java/org/zowe/apiml/zaas/ZaasApplication.java index 8280e32cb3..5afdd24d4f 100644 --- a/zaas-service/src/main/java/org/zowe/apiml/zaas/ZaasApplication.java +++ b/zaas-service/src/main/java/org/zowe/apiml/zaas/ZaasApplication.java @@ -24,7 +24,7 @@ import static org.zowe.apiml.extension.ZoweRuntimeEnvironment.defaultEnv; -@EnableWebSecurity +@EnableWebSecurity(debug = true) @SpringBootApplication @EnableDiscoveryClient @ComponentScan( diff --git a/zaas-service/src/main/java/org/zowe/apiml/zaas/security/config/NewSecurityConfiguration.java b/zaas-service/src/main/java/org/zowe/apiml/zaas/security/config/NewSecurityConfiguration.java index eab0ceccd0..8c014c43b8 100644 --- a/zaas-service/src/main/java/org/zowe/apiml/zaas/security/config/NewSecurityConfiguration.java +++ b/zaas-service/src/main/java/org/zowe/apiml/zaas/security/config/NewSecurityConfiguration.java @@ -30,12 +30,12 @@ import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer; import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.web.SecurityFilterChain; -import org.springframework.security.web.authentication.AnonymousAuthenticationFilter; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.authentication.logout.HttpStatusReturningLogoutSuccessHandler; import org.springframework.security.web.authentication.logout.LogoutHandler; -import org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.zowe.apiml.filter.AttlsFilter; import org.zowe.apiml.filter.SecureConnectionFilter; @@ -49,7 +49,6 @@ import org.zowe.apiml.security.common.error.AuthExceptionHandler; import org.zowe.apiml.security.common.filter.CategorizeCertsFilter; import org.zowe.apiml.security.common.filter.StoreAccessTokenInfoFilter; -import org.zowe.apiml.security.common.filter.X509ClientRejectIfMissingFilter; import org.zowe.apiml.security.common.handler.FailedAccessTokenHandler; import org.zowe.apiml.security.common.handler.FailedAuthenticationHandler; import org.zowe.apiml.security.common.handler.SuccessfulAccessTokenHandler; @@ -68,6 +67,7 @@ import org.zowe.apiml.zaas.zaas.ExtractAuthSourceFilter; import org.zowe.apiml.zaas.zaas.ZaasAuthenticationFilter; +import java.util.Collections; import java.util.Set; import static org.springframework.security.config.Customizer.withDefaults; @@ -300,6 +300,7 @@ public SecurityFilterChain authZaasEndpointsFilterChain(HttpSecurity http) throw ))) .authorizeHttpRequests(requests -> requests .anyRequest().authenticated()) + .x509(x509 -> x509.userDetailsService(x509UserDetailsService())) .addFilterAfter(new CategorizeCertsFilter(publicKeyCertificatesBase64, certificateValidator, authExceptionHandler), org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter.class) .addFilterAfter(new ExtractAuthSourceFilter(authSourceService, authExceptionHandler), org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter.class) .addFilterAfter(new ZaasAuthenticationFilter(authSourceService, authExceptionHandler), CategorizeCertsFilter.class); @@ -370,10 +371,12 @@ public SecurityFilterChain ticketFilterChain(HttpSecurity http) throws Exception return baseConfigure(http.securityMatchers(matchers -> matchers.requestMatchers( authConfigurationProperties.getZaasTicketEndpoint() ))).authorizeHttpRequests(requests -> requests.anyRequest().authenticated()) - .addFilterBefore(new CategorizeCertsFilter(publicKeyCertificatesBase64, certificateValidator, authExceptionHandler), X509AuthenticationFilter.class) - .addFilterAfter(new X509ClientRejectIfMissingFilter(authExceptionHandler), CategorizeCertsFilter.class) .authenticationProvider(tokenAuthenticationProvider) .logout(AbstractHttpConfigurer::disable) // logout filter in this chain not needed + //Todo: validate + .x509(x509 -> x509 //default x509 filter, authenticates trusted cert, ticketFilter(..) depends on this + .userDetailsService(new SimpleUserDetailService()) + ) .with(new CustomSecurityFilters(), withDefaults()) .build(); } @@ -382,7 +385,10 @@ private class CustomSecurityFilters extends AbstractHttpConfigurer requests .anyRequest().authenticated()) .authenticationProvider(tokenAuthenticationProvider) - .logout(logout -> logout.disable()) // logout filter in this chain not needed + .logout(AbstractHttpConfigurer::disable) // logout filter in this chain not needed .x509(x509 -> x509 //default x509 filter, authenticates trusted cert, refreshFilter(..) depends on this .userDetailsService(new SimpleUserDetailService())) .with(new CustomSecurityFilters(), Customizer.withDefaults()); @@ -608,9 +614,8 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { return baseConfigure(http.securityMatchers(matchers -> matchers.requestMatchers("/**", "/gateway/version"))) .authorizeHttpRequests(requests -> requests .anyRequest() - .permitAll()).logout(logout -> logout.disable()) + .permitAll()).logout(AbstractHttpConfigurer::disable) // sort out client and apiml internal certificates - .addFilterBefore(new CategorizeCertsFilter(publicKeyCertificatesBase64, certificateValidator, authExceptionHandler), AnonymousAuthenticationFilter.class) .build(); } } @@ -633,4 +638,8 @@ protected HttpSecurity baseConfigure(HttpSecurity http) throws Exception { .sessionManagement(management -> management.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .exceptionHandling(handling -> handling.authenticationEntryPoint(handlerInitializer.getBasicAuthUnauthorizedHandler())); } + + private UserDetailsService x509UserDetailsService() { + return username -> new User(username, "", Collections.emptyList()); + } } diff --git a/zaas-service/src/main/java/org/zowe/apiml/zaas/zaas/ZaasAuthenticationFilter.java b/zaas-service/src/main/java/org/zowe/apiml/zaas/zaas/ZaasAuthenticationFilter.java index b8dac91b52..17380d71ab 100644 --- a/zaas-service/src/main/java/org/zowe/apiml/zaas/zaas/ZaasAuthenticationFilter.java +++ b/zaas-service/src/main/java/org/zowe/apiml/zaas/zaas/ZaasAuthenticationFilter.java @@ -17,8 +17,6 @@ import lombok.NonNull; import lombok.RequiredArgsConstructor; import org.springframework.security.authentication.InsufficientAuthenticationException; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; import org.springframework.web.filter.OncePerRequestFilter; import org.zowe.apiml.security.common.error.AuthExceptionHandler; import org.zowe.apiml.zaas.security.service.schema.source.AuthSource; @@ -41,11 +39,11 @@ protected void doFilterInternal(@NonNull HttpServletRequest request, @NonNull Ht Optional authSource = Optional.ofNullable((AuthSource) request.getAttribute(AUTH_SOURCE_ATTR)); if (authSource.isEmpty() || !authSourceService.isValid(authSource.get())) { throw new InsufficientAuthenticationException("Authentication failed."); - } else { - var auth = new PreAuthenticatedAuthenticationToken( - authSourceService.parse(authSource.get()).getUserId(), null); - auth.setAuthenticated(true); - SecurityContextHolder.getContext().setAuthentication(auth); +// } else { +// var auth = new PreAuthenticatedAuthenticationToken( +// authSourceService.parse(authSource.get()).getUserId(), null); +// auth.setAuthenticated(true); +// SecurityContextHolder.getContext().setAuthentication(auth); } filterChain.doFilter(request, response); } catch (RuntimeException e) { diff --git a/zaas-service/src/test/java/org/zowe/apiml/acceptance/ZaasTest.java b/zaas-service/src/test/java/org/zowe/apiml/acceptance/ZaasTest.java index 8e06a8693e..68f15cf4a4 100644 --- a/zaas-service/src/test/java/org/zowe/apiml/acceptance/ZaasTest.java +++ b/zaas-service/src/test/java/org/zowe/apiml/acceptance/ZaasTest.java @@ -53,7 +53,7 @@ void givenZosmfCookieAndDummyAuthProvider_whenZoweJwtRequest_thenUnavailable() { //@formatter:off given().config(config().sslConfig(new SSLConfig().sslSocketFactory( - new SSLSocketFactory(httpConfig.getSecureSslContext(), ALLOW_ALL_HOSTNAME_VERIFIER))) + new SSLSocketFactory(httpConfig.getSecureSslContextWithoutKeystore(), ALLOW_ALL_HOSTNAME_VERIFIER))) ) .cookie(COOKIE, zosmfJwt) .when() diff --git a/zaas-service/src/test/java/org/zowe/apiml/zaas/zaas/ZaasAuthenticationFilterTest.java b/zaas-service/src/test/java/org/zowe/apiml/zaas/zaas/ZaasAuthenticationFilterTest.java index 47df6f3bda..c51f187dc4 100644 --- a/zaas-service/src/test/java/org/zowe/apiml/zaas/zaas/ZaasAuthenticationFilterTest.java +++ b/zaas-service/src/test/java/org/zowe/apiml/zaas/zaas/ZaasAuthenticationFilterTest.java @@ -27,7 +27,6 @@ import org.zowe.apiml.zaas.security.service.schema.source.AuthSource; import org.zowe.apiml.zaas.security.service.schema.source.AuthSourceService; import org.zowe.apiml.zaas.security.service.schema.source.JwtAuthSource; -import org.zowe.apiml.zaas.security.service.schema.source.ParsedTokenAuthSource; import java.io.IOException; @@ -113,9 +112,10 @@ private void mockAuthSource(boolean isValid) { AuthSource authSource = new JwtAuthSource("token"); request.setAttribute(AUTH_SOURCE_ATTR, authSource); when(authSourceService.isValid(authSource)).thenReturn(isValid); - if (isValid) { - when(authSourceService.parse(authSource)).thenReturn(new ParsedTokenAuthSource("user", null, null, null)); - } + //TODO + //if (isValid) { + // when(authSourceService.parse(authSource)).thenReturn(new ParsedTokenAuthSource("user", null, null, null)); + //} } } diff --git a/zaas-service/src/test/java/org/zowe/apiml/zaas/zaas/ZaasExceptionHandlerTest.java b/zaas-service/src/test/java/org/zowe/apiml/zaas/zaas/ZaasExceptionHandlerTest.java index f798092318..36c521db4f 100644 --- a/zaas-service/src/test/java/org/zowe/apiml/zaas/zaas/ZaasExceptionHandlerTest.java +++ b/zaas-service/src/test/java/org/zowe/apiml/zaas/zaas/ZaasExceptionHandlerTest.java @@ -11,9 +11,6 @@ package org.zowe.apiml.zaas.zaas; import io.restassured.RestAssured; -import io.restassured.config.RestAssuredConfig; -import io.restassured.config.SSLConfig; -import org.apache.http.conn.ssl.SSLSocketFactory; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -54,9 +51,10 @@ void init() { RestAssured.baseURI = "https://localhost"; RestAssured.port = port; RestAssured.useRelaxedHTTPSValidation(); - RestAssured.config = RestAssured.config.sslConfig(new SSLConfig().sslSocketFactory( - new SSLSocketFactory(httpConfig.getSecureSslContext(), SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER) - )); +// TODO: validate +// RestAssured.config = RestAssured.config.sslConfig(new SSLConfig().sslSocketFactory( +// new SSLSocketFactory(httpConfig.getSecureSslContext(), SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER) +// )); } @Test @@ -81,19 +79,20 @@ void givenNoCredentials_whenCallZaas_thenReturns401WithMessage(String url) { .body("messages[0].messageKey", is("org.zowe.apiml.security.authRequired")); } - @ParameterizedTest - @ValueSource(strings = { - "/zaas/scheme/ticket", - "/application/health" - }) - void givenNoCertificateAndNoCredentials_whenCallZaas_thenReturns401WithMessage(String url) { - given().config(RestAssuredConfig.newConfig()).relaxedHTTPSValidation() - .when() - .get(url) - .then() - .statusCode(401) - .body("messages[0].messageKey", is("org.zowe.apiml.security.authRequired")); - } + //TODO: validate +// @ParameterizedTest +// @ValueSource(strings = { +// "/zaas/scheme/ticket", +// "/application/health" +// }) +// void givenNoCertificateAndNoCredentials_whenCallZaas_thenReturns401WithMessage(String url) { +// given().config(RestAssuredConfig.newConfig()).relaxedHTTPSValidation() +// .when() +// .get(url) +// .then() +// .statusCode(401) +// .body("messages[0].messageKey", is("org.zowe.apiml.security.authRequired")); +// } @ParameterizedTest @ValueSource(strings = { diff --git a/zaas-service/src/test/resources/application-test.yml b/zaas-service/src/test/resources/application-test.yml index 614a771fa3..4d564e9240 100644 --- a/zaas-service/src/test/resources/application-test.yml +++ b/zaas-service/src/test/resources/application-test.yml @@ -2,7 +2,7 @@ environment.older: false logging: level: - ROOT: TRACE + ROOT: INFO apiml: # The `apiml` node contains API Mediation Layer specific configuration diff --git a/zaas-service/src/test/resources/application.yml b/zaas-service/src/test/resources/application.yml index 484ba1e594..a5f9f1ea6b 100644 --- a/zaas-service/src/test/resources/application.yml +++ b/zaas-service/src/test/resources/application.yml @@ -1,6 +1,6 @@ logging: level: - ROOT: TRACE + ROOT: INFO apiml: service: From db7213410797afdc9836aa28d26d7805494bdf0b Mon Sep 17 00:00:00 2001 From: Richard Salac Date: Tue, 4 Feb 2025 15:56:36 +0100 Subject: [PATCH 05/11] fix: merge and cleanup Signed-off-by: Richard Salac --- .github/workflows/integration-tests.yml | 3 + .../apiml/apicatalog/config/BeanConfig.java | 11 +- .../controllers/api/ApiCatalogController.java | 1 + .../security/SecurityConfiguration.java | 5 +- .../listeners/GatewayLookupEventListener.java | 12 +- ...inerRetrievalTestContextConfiguration.java | 10 +- ...piDocNotFoundTestContextConfiguration.java | 9 +- ...rviceNotFoundTestContextConfiguration.java | 9 +- .../controllers/api/MockControllerTest.java | 8 +- .../standalone/StandaloneInitializerTest.java | 15 +- .../StaticAPIRefreshControllerTest.java | 7 + .../StaticApiContextConfiguration.java | 19 +- .../e2e/detail-page/swagger-rendering.cy.js | 2 +- api-catalog-ui/frontend/package-lock.json | 187 ++++----- api-catalog-ui/frontend/package.json | 26 +- .../web/DiscoveryRestTemplateConfig.java | 22 +- .../GatewayInstanceInitializerTest.java | 18 +- .../apiml/extension/ExtensionsLoaderTest.java | 6 +- .../common/filter/CategorizeCertsFilter.java | 28 +- .../X509ClientRejectIfMissingFilter.java | 57 --- .../common/verify/CertificateValidator.java | 6 +- .../filter/CategorizeCertsFilterTest.java | 39 +- .../X509ClientRejectIfMissingFilterTest.java | 96 ----- .../verify/CertificateValidatorTest.java | 6 +- build.gradle | 6 +- .../java/org/zowe/apiml/util/CorsUtils.java | 2 +- .../ApiMediationClientTestControllerTest.java | 7 +- .../client/api/PetControllerDeleteTest.java | 4 +- .../client/api/PetControllerGetAllTest.java | 4 +- .../client/api/PetControllerGetOneTest.java | 4 +- .../client/api/PetControllerPostPetTest.java | 21 +- .../client/api/PetControllerPutTest.java | 22 +- .../api/ZaasClientTestControllerTest.java | 7 +- .../gateway/config/AuthEndpointConfig.java | 2 - .../gateway/config/ConnectionsConfig.java | 49 ++- .../DiscoveryClientOrderProcessorBean.java | 37 ++ .../apiml/gateway/config/EurekaFactory.java | 6 +- .../gateway/config/ServiceCorsUpdater.java | 67 ++++ .../apiml/gateway/service/RouteLocator.java | 53 +-- .../src/main/resources/application.yml | 5 +- .../config/DiscoveryClientTestConfig.java | 13 +- .../netflix/ApimlDiscoveryClientStub.java | 6 +- .../config/ServiceCorsUpdaterTest.java | 104 +++++ .../CertificatesRestControllerTest.java | 6 +- .../controllers/VersionControllerTest.java | 8 +- .../gateway/service/RouteLocatorTest.java | 52 +-- gradle/versions.gradle | 33 +- .../package.json | 2 +- onboarding-enabler-nodejs/package-lock.json | 2 +- .../config/EnableApiDiscoveryConfigTest.java | 4 +- .../ApimlDisabledRegisterToApiLayerTest.java | 4 +- .../ApimlEnabledRegisterToApiLayerTest.java | 4 +- scripts/docs/package.json | 2 +- scripts/release_components/package.json | 2 +- scripts/release_docs/package.json | 2 +- .../org/zowe/apiml/zaas/ZaasApplication.java | 2 +- .../DiscoveryClientOrderProcessorBean.java | 2 +- .../config/NewSecurityConfiguration.java | 28 +- .../apiml/acceptance/RefreshEndpointTest.java | 6 +- .../org/zowe/apiml/acceptance/ZaasTest.java | 6 +- .../apiml/zaas/config/CacheConfigTest.java | 18 +- .../zaas/security/config/AttlsConfigTest.java | 6 +- .../service/AuthenticationServiceTest.java | 19 +- .../zaas/ZaasAuthenticationFilterTest.java | 4 - .../zaas/zaas/ZaasExceptionHandlerTest.java | 26 +- .../package-lock.json | 373 +++++++++--------- zowe-cli-id-federation-plugin/package.json | 24 +- 67 files changed, 800 insertions(+), 856 deletions(-) delete mode 100644 apiml-security-common/src/main/java/org/zowe/apiml/security/common/filter/X509ClientRejectIfMissingFilter.java delete mode 100644 apiml-security-common/src/test/java/org/zowe/apiml/security/common/filter/X509ClientRejectIfMissingFilterTest.java create mode 100644 gateway-service/src/main/java/org/zowe/apiml/gateway/config/DiscoveryClientOrderProcessorBean.java create mode 100644 gateway-service/src/main/java/org/zowe/apiml/gateway/config/ServiceCorsUpdater.java create mode 100644 gateway-service/src/test/java/org/zowe/apiml/gateway/config/ServiceCorsUpdaterTest.java diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index af5123e434..86fd1eaff0 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -43,6 +43,9 @@ jobs: image: ghcr.io/balhar-jakub/api-catalog-services-standalone:${{ github.run_id }}-${{ github.run_number }} volumes: - /api-defs:/api-defs + env: + APIML_SERVICE_HOSTNAME: api-catalog-services-2 + APIML_HEALTH_PROTECTED: false api-catalog-services: image: ghcr.io/balhar-jakub/api-catalog-services:${{ github.run_id }}-${{ github.run_number }} volumes: diff --git a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/config/BeanConfig.java b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/config/BeanConfig.java index 9d6d7d2c04..5fb9dda53c 100644 --- a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/config/BeanConfig.java +++ b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/config/BeanConfig.java @@ -10,12 +10,13 @@ package org.zowe.apiml.apicatalog.config; -import org.zowe.apiml.message.core.MessageService; -import org.zowe.apiml.product.gateway.GatewayClient; -import org.zowe.apiml.product.routing.transform.TransformService; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; +import org.zowe.apiml.message.core.MessageService; +import org.zowe.apiml.message.yaml.YamlMessageServiceInstance; +import org.zowe.apiml.product.gateway.GatewayClient; +import org.zowe.apiml.product.routing.transform.TransformService; /** * General configuration of the API Catalog. @@ -25,7 +26,9 @@ public class BeanConfig { @Bean @Primary - public MessageService messageServiceCatalog(MessageService messageService) { + public MessageService messageServiceCatalog() { + MessageService messageService = YamlMessageServiceInstance.getInstance(); + messageService.loadMessages("/security-client-log-messages.yml"); messageService.loadMessages("/utility-log-messages.yml"); messageService.loadMessages("/common-log-messages.yml"); messageService.loadMessages("/security-common-log-messages.yml"); diff --git a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/controllers/api/ApiCatalogController.java b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/controllers/api/ApiCatalogController.java index bc05db7d4c..f32e1afce1 100644 --- a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/controllers/api/ApiCatalogController.java +++ b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/controllers/api/ApiCatalogController.java @@ -92,6 +92,7 @@ public ResponseEntity> getOidcProvider() { ) @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "OK"), + @ApiResponse(responseCode = "204", description = "No service available"), @ApiResponse(responseCode = "401", description = "Unauthorized"), @ApiResponse(responseCode = "403", description = "Forbidden"), @ApiResponse(responseCode = "404", description = "URI not found"), diff --git a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/security/SecurityConfiguration.java b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/security/SecurityConfiguration.java index 5b339f50ae..949d17e084 100644 --- a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/security/SecurityConfiguration.java +++ b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/security/SecurityConfiguration.java @@ -14,6 +14,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -68,6 +69,7 @@ @EnableApimlAuth @EnableMethodSecurity @EnableConfigurationProperties(SafSecurityConfigurationProperties.class) +@ConditionalOnProperty(value = "apiml.catalog.standalone.enabled", havingValue = "false", matchIfMissing = true) public class SecurityConfiguration { private static final String APIDOC_ROUTES = "/apidoc/**"; private static final String STATIC_REFRESH_ROUTE = "/static-api/refresh"; @@ -130,7 +132,7 @@ private UserDetailsService x509UserDetailsService() { } private CategorizeCertsFilter reversedCategorizeCertFilter() { - CategorizeCertsFilter out = new CategorizeCertsFilter(publicKeyCertificatesBase64, certificateValidator, handlerInitializer.getUnAuthorizedHandler().getHandler(), false); + CategorizeCertsFilter out = new CategorizeCertsFilter(publicKeyCertificatesBase64, certificateValidator); out.setCertificateForClientAuth(crt -> out.getPublicKeyCertificatesBase64().contains(CategorizeCertsFilter.base64EncodePublicKey(crt))); out.setApimlCertificate(crt -> !out.getPublicKeyCertificatesBase64().contains(CategorizeCertsFilter.base64EncodePublicKey(crt))); return out; @@ -299,4 +301,5 @@ private OidcContentFilter oidcFilter(AuthenticationManager authenticationManager public LogoutSuccessHandler logoutSuccessHandler() { return new ApiCatalogLogoutSuccessHandler(authConfigurationProperties); } + } diff --git a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/status/listeners/GatewayLookupEventListener.java b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/status/listeners/GatewayLookupEventListener.java index d19532adb1..0985f0de97 100644 --- a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/status/listeners/GatewayLookupEventListener.java +++ b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/services/status/listeners/GatewayLookupEventListener.java @@ -11,6 +11,7 @@ package org.zowe.apiml.apicatalog.services.status.listeners; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.event.EventListener; import org.springframework.core.Ordered; @@ -18,7 +19,6 @@ import org.springframework.stereotype.Component; import org.zowe.apiml.apicatalog.instance.InstanceInitializeService; import org.zowe.apiml.product.gateway.GatewayLookupCompleteEvent; -import org.zowe.apiml.product.registry.CannotRegisterServiceException; import java.util.concurrent.atomic.AtomicBoolean; @@ -26,6 +26,7 @@ * This class fires on GatewayLookupCompleteEvent event * Initializes Catalog instances from Eureka */ +@Slf4j @Component @ConditionalOnProperty( value = "apiml.catalog.standalone.enabled", @@ -39,10 +40,15 @@ public class GatewayLookupEventListener { private final AtomicBoolean hasRun = new AtomicBoolean(false); @EventListener(GatewayLookupCompleteEvent.class) - public void onApplicationEvent() throws CannotRegisterServiceException { + public void onApplicationEvent() { if (!hasRun.get()) { hasRun.set(true); - instanceInitializeService.retrieveAndRegisterAllInstancesWithCatalog(); + try { + instanceInitializeService.retrieveAndRegisterAllInstancesWithCatalog(); + } catch (Exception e) { + hasRun.set(false); + log.debug("Unexpected error occurred while initial retrieving of services: {}", e.getMessage()); + } } } } diff --git a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/ApiCatalogControllerContainerRetrievalTestContextConfiguration.java b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/ApiCatalogControllerContainerRetrievalTestContextConfiguration.java index abf4ba310b..bd9eb8cb12 100644 --- a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/ApiCatalogControllerContainerRetrievalTestContextConfiguration.java +++ b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/ApiCatalogControllerContainerRetrievalTestContextConfiguration.java @@ -10,7 +10,6 @@ package org.zowe.apiml.apicatalog.controllers.api; -import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Bean; import org.zowe.apiml.apicatalog.controllers.handlers.ApiCatalogControllerExceptionHandler; import org.zowe.apiml.apicatalog.services.cached.CachedProductFamilyService; @@ -21,11 +20,13 @@ class ApiCatalogControllerContainerRetrievalTestContextConfiguration { - @MockBean - private CachedProductFamilyService cachedProductFamilyService; + @Bean + public CachedProductFamilyService cachedProductFamilyService() { + return mock(CachedProductFamilyService.class); + } @Bean - public ApiCatalogController apiCatalogController() { + public ApiCatalogController apiCatalogController(CachedProductFamilyService cachedProductFamilyService) { when(cachedProductFamilyService.getAllContainers()) .thenThrow(new NullPointerException()); @@ -43,4 +44,5 @@ public MessageService messageService() { public ApiCatalogControllerExceptionHandler apiCatalogControllerExceptionHandler() { return new ApiCatalogControllerExceptionHandler(messageService()); } + } diff --git a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/CatalogApiDocControllerApiDocNotFoundTestContextConfiguration.java b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/CatalogApiDocControllerApiDocNotFoundTestContextConfiguration.java index 7f22f25d65..ac4ec2b36e 100644 --- a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/CatalogApiDocControllerApiDocNotFoundTestContextConfiguration.java +++ b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/CatalogApiDocControllerApiDocNotFoundTestContextConfiguration.java @@ -10,7 +10,6 @@ package org.zowe.apiml.apicatalog.controllers.api; -import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Bean; import org.zowe.apiml.apicatalog.controllers.handlers.CatalogApiDocControllerExceptionHandler; import org.zowe.apiml.apicatalog.services.status.APIServiceStatusService; @@ -22,11 +21,13 @@ class CatalogApiDocControllerApiDocNotFoundTestContextConfiguration { - @MockBean - private APIServiceStatusService apiServiceStatusService; + @Bean + public APIServiceStatusService apiServiceStatusService() { + return mock(APIServiceStatusService.class); + } @Bean - public CatalogApiDocController catalogApiDocController() { + public CatalogApiDocController catalogApiDocController(APIServiceStatusService apiServiceStatusService) { when(apiServiceStatusService.getServiceCachedApiDocInfo("service2", "v1")) .thenThrow(new ApiDocNotFoundException("Really bad stuff happened")); diff --git a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/CatalogApiDocControllerServiceNotFoundTestContextConfiguration.java b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/CatalogApiDocControllerServiceNotFoundTestContextConfiguration.java index 565fc1fdc8..65053eb455 100644 --- a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/CatalogApiDocControllerServiceNotFoundTestContextConfiguration.java +++ b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/CatalogApiDocControllerServiceNotFoundTestContextConfiguration.java @@ -10,7 +10,6 @@ package org.zowe.apiml.apicatalog.controllers.api; -import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Bean; import org.zowe.apiml.apicatalog.controllers.handlers.CatalogApiDocControllerExceptionHandler; import org.zowe.apiml.apicatalog.services.status.APIServiceStatusService; @@ -22,11 +21,13 @@ class CatalogApiDocControllerServiceNotFoundTestContextConfiguration { - @MockBean - private APIServiceStatusService apiServiceStatusService; + @Bean + public APIServiceStatusService apiServiceStatusService() { + return mock(APIServiceStatusService.class); + } @Bean - public CatalogApiDocController catalogApiDocController() { + public CatalogApiDocController catalogApiDocController(APIServiceStatusService apiServiceStatusService) { when(apiServiceStatusService.getServiceCachedApiDocInfo("service1", "v1")) .thenThrow(new ServiceNotFoundException("API Documentation not retrieved, The service is running.")); diff --git a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/MockControllerTest.java b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/MockControllerTest.java index a4377f0880..310774e38d 100644 --- a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/MockControllerTest.java +++ b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/controllers/api/MockControllerTest.java @@ -15,7 +15,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; -import org.springframework.boot.test.mock.mockito.SpyBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; @@ -27,6 +26,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; @@ -72,9 +72,13 @@ void whenPostRequest() throws Exception { @Configuration @Profile("test") - @SpyBean(ExampleService.class) static class Context { + @Bean + public ExampleService exampleService() { + return spy(new ExampleService()); + } + @Bean public MockController mockController(ExampleService exampleService) { return new MockController(exampleService); diff --git a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/standalone/StandaloneInitializerTest.java b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/standalone/StandaloneInitializerTest.java index 9d9153817e..9fd0f06b09 100644 --- a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/standalone/StandaloneInitializerTest.java +++ b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/standalone/StandaloneInitializerTest.java @@ -16,7 +16,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.context.event.ApplicationReadyEvent; -import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Bean; @@ -26,11 +25,7 @@ import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.junit.jupiter.SpringExtension; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoInteractions; -import static org.mockito.Mockito.when; +import static org.mockito.Mockito.*; @ExtendWith(SpringExtension.class) @ActiveProfiles("test") @@ -82,11 +77,13 @@ private ConfigurableApplicationContext mockContext(ApplicationReadyEvent event, @Profile("test") public static class TestConfiguration { - @MockBean - private StandaloneLoaderService standaloneLoaderService; + @Bean + public StandaloneLoaderService standaloneLoaderService() { + return mock(StandaloneLoaderService.class); + } @Bean - public StandaloneInitializer getStandaloneInitializer() { + public StandaloneInitializer getStandaloneInitializer(StandaloneLoaderService standaloneLoaderService) { return new StandaloneInitializer(standaloneLoaderService); } diff --git a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/staticapi/StaticAPIRefreshControllerTest.java b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/staticapi/StaticAPIRefreshControllerTest.java index 96571544ab..352f17bd69 100644 --- a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/staticapi/StaticAPIRefreshControllerTest.java +++ b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/staticapi/StaticAPIRefreshControllerTest.java @@ -10,6 +10,7 @@ package org.zowe.apiml.apicatalog.staticapi; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; @@ -25,6 +26,7 @@ import org.zowe.apiml.apicatalog.services.status.model.ServiceNotFoundException; import static org.hamcrest.Matchers.hasSize; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.when; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; @@ -46,6 +48,11 @@ class StaticAPIRefreshControllerTest { @Autowired private StaticAPIService staticAPIService; + @BeforeEach + void setUp() { + reset(staticAPIService); + } + @Test void givenServiceNotFoundException_whenCallRefreshAPI_thenResponseShouldBe503WithSpecificMessage() throws Exception { when(staticAPIService.refresh()).thenThrow( diff --git a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/staticapi/StaticApiContextConfiguration.java b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/staticapi/StaticApiContextConfiguration.java index 0559baa523..128e43bfc9 100644 --- a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/staticapi/StaticApiContextConfiguration.java +++ b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/staticapi/StaticApiContextConfiguration.java @@ -10,15 +10,18 @@ package org.zowe.apiml.apicatalog.staticapi; -import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Bean; import org.zowe.apiml.message.core.MessageService; import org.zowe.apiml.message.yaml.YamlMessageService; +import static org.mockito.Mockito.mock; + public class StaticApiContextConfiguration { - @MockBean - private StaticAPIService staticAPIService; + @Bean + public StaticAPIService staticAPIService() { + return mock(StaticAPIService.class); + } @Bean public MessageService messageService() { @@ -31,12 +34,14 @@ public StaticAPIRefreshControllerExceptionHandler staticAPIRefreshControllerExce } @Bean - public StaticAPIRefreshController apiCatalogController() { + public StaticAPIRefreshController apiCatalogController(StaticAPIService staticAPIService) { return new StaticAPIRefreshController(staticAPIService); } - @MockBean - private StaticDefinitionGenerator staticDefinitionGenerator; + @Bean + public StaticDefinitionGenerator staticDefinitionGenerator() { + return mock(StaticDefinitionGenerator.class); + } @Bean public StaticDefinitionControllerExceptionHandler staticDefinitionControllerExceptionHandler(MessageService messageService) { @@ -44,7 +49,7 @@ public StaticDefinitionControllerExceptionHandler staticDefinitionControllerExce } @Bean - public StaticDefinitionController staticAPIRefreshController() { + public StaticDefinitionController staticAPIRefreshController(StaticDefinitionGenerator staticDefinitionGenerator) { return new StaticDefinitionController(staticDefinitionGenerator); } } diff --git a/api-catalog-ui/frontend/cypress/e2e/detail-page/swagger-rendering.cy.js b/api-catalog-ui/frontend/cypress/e2e/detail-page/swagger-rendering.cy.js index 7214f781d0..4d20a9cba4 100644 --- a/api-catalog-ui/frontend/cypress/e2e/detail-page/swagger-rendering.cy.js +++ b/api-catalog-ui/frontend/cypress/e2e/detail-page/swagger-rendering.cy.js @@ -93,7 +93,7 @@ describe("Swagger rendering", () => { .should('exist'); cy.get('@swaggerContainer') - .get('div.information-container > section > div > div.info > .main') + .get('div.information-container > section div.info .main') .as('mainInfo'); cy.get('@mainInfo').should('exist'); diff --git a/api-catalog-ui/frontend/package-lock.json b/api-catalog-ui/frontend/package-lock.json index 1a1dafffb3..3d7b55fe6b 100644 --- a/api-catalog-ui/frontend/package-lock.json +++ b/api-catalog-ui/frontend/package-lock.json @@ -52,7 +52,7 @@ "redux-persist-transform-filter": "0.0.22", "redux-thunk": "3.1.0", "rxjs": "7.8.1", - "sass": "1.83.1", + "sass": "1.83.4", "stream": "0.0.3", "swagger-ui-react": "5.18.2", "url": "0.11.4", @@ -61,28 +61,28 @@ }, "devDependencies": { "@babel/core": "7.26.0", - "@babel/eslint-parser": "7.25.9", + "@babel/eslint-parser": "7.26.5", "@babel/plugin-proposal-private-property-in-object": "7.21.11", "@babel/preset-env": "7.26.0", "@babel/preset-react": "7.26.3", "@cfaester/enzyme-adapter-react-18": "0.8.0", - "@eslint/compat": "1.2.4", - "@eslint/js": "9.17.0", + "@eslint/compat": "1.2.5", + "@eslint/js": "9.18.0", "@testing-library/dom": "10.4.0", "@testing-library/jest-dom": "6.6.3", - "@testing-library/react": "16.1.0", - "@testing-library/user-event": "14.5.2", + "@testing-library/react": "16.2.0", + "@testing-library/user-event": "14.6.0", "ajv": "8.17.1", "ansi-regex": "6.1.0", "body-parser": "1.20.3", - "caniuse-lite": "1.0.30001690", + "caniuse-lite": "1.0.30001692", "concurrently": "9.1.2", "cors": "2.8.5", "cross-env": "7.0.3", "cypress": "13.17.0", "cypress-file-upload": "5.0.8", "enzyme": "3.11.0", - "eslint": "9.17.0", + "eslint": "9.18.0", "eslint-config-airbnb": "19.0.4", "eslint-config-prettier": "9.1.0", "eslint-plugin-cypress": "4.1.0", @@ -90,8 +90,8 @@ "eslint-plugin-header": "3.1.1", "eslint-plugin-import": "2.31.0", "eslint-plugin-jsx-a11y": "6.10.2", - "eslint-plugin-prettier": "5.2.1", - "eslint-plugin-react": "7.37.3", + "eslint-plugin-prettier": "5.2.2", + "eslint-plugin-react": "7.37.4", "express": "4.21.2", "globals": "15.14.0", "html-loader": "5.1.0", @@ -115,7 +115,7 @@ "redux-mock-store": "1.5.5", "rimraf": "6.0.1", "source-map-explorer": "2.5.3", - "start-server-and-test": "2.0.9", + "start-server-and-test": "2.0.10", "tmpl": "1.0.5", "undici": "6.19.8", "yaml": "2.7.0" @@ -223,9 +223,9 @@ } }, "node_modules/@babel/eslint-parser": { - "version": "7.25.9", - "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@babel/eslint-parser/-/eslint-parser-7.25.9.tgz", - "integrity": "sha512-5UXfgpK0j0Xr/xIdgdLEhOFxaDZ0bRPWJJchRpqOSur/3rZoPbqqki5mm0p4NE2cs28krBEiSM2MB7//afRSQQ==", + "version": "7.26.5", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@babel/eslint-parser/-/eslint-parser-7.26.5.tgz", + "integrity": "sha512-Kkm8C8uxI842AwQADxl0GbcG1rupELYLShazYEZO/2DYjhyWXJIOUVOE3tBYm6JXzUCNJOZEzqc4rCW/jsEQYQ==", "dev": true, "license": "MIT", "dependencies": { @@ -242,13 +242,13 @@ } }, "node_modules/@babel/generator": { - "version": "7.26.3", - "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@babel/generator/-/generator-7.26.3.tgz", - "integrity": "sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ==", + "version": "7.26.5", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@babel/generator/-/generator-7.26.5.tgz", + "integrity": "sha512-2caSP6fN9I7HOe6nqhtft7V4g7/V/gfDsC3Ag4W7kEzzvRGKqiv0pu0HogPiZ3KaVSoNDhUws6IJjDjpfmYIXw==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.26.3", - "@babel/types": "^7.26.3", + "@babel/parser": "^7.26.5", + "@babel/types": "^7.26.5", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" @@ -542,12 +542,12 @@ } }, "node_modules/@babel/parser": { - "version": "7.26.3", - "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@babel/parser/-/parser-7.26.3.tgz", - "integrity": "sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA==", + "version": "7.26.5", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@babel/parser/-/parser-7.26.5.tgz", + "integrity": "sha512-SRJ4jYmXRqV1/Xc+TIVG84WjHBXKlxO9sHQnA2Pf12QQEAp1LOh6kDzNHXcUnbH1QI0FDoPPVOt+vyUDucxpaw==", "license": "MIT", "dependencies": { - "@babel/types": "^7.26.3" + "@babel/types": "^7.26.5" }, "bin": { "parser": "bin/babel-parser.js" @@ -2207,16 +2207,16 @@ } }, "node_modules/@babel/traverse": { - "version": "7.26.4", - "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@babel/traverse/-/traverse-7.26.4.tgz", - "integrity": "sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w==", + "version": "7.26.5", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@babel/traverse/-/traverse-7.26.5.tgz", + "integrity": "sha512-rkOSPOw+AXbgtwUga3U4u8RpoK9FEFWBNAlTpcnkLFjL5CT+oyHNuUUC/xx6XefEJ16r38r8Bc/lfp6rYuHeJQ==", "license": "MIT", "dependencies": { "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.3", - "@babel/parser": "^7.26.3", + "@babel/generator": "^7.26.5", + "@babel/parser": "^7.26.5", "@babel/template": "^7.25.9", - "@babel/types": "^7.26.3", + "@babel/types": "^7.26.5", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -2234,9 +2234,9 @@ } }, "node_modules/@babel/types": { - "version": "7.26.3", - "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@babel/types/-/types-7.26.3.tgz", - "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==", + "version": "7.26.5", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@babel/types/-/types-7.26.5.tgz", + "integrity": "sha512-L6mZmwFDK6Cjh1nRCLXpa6no13ZIioJDz7mdkzHv399pThrTa/k0nUlNaenOeh2kWu/iaOQYElEpKPUswUa9Vg==", "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.25.9", @@ -2839,9 +2839,9 @@ } }, "node_modules/@eslint/compat": { - "version": "1.2.4", - "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@eslint/compat/-/compat-1.2.4.tgz", - "integrity": "sha512-S8ZdQj/N69YAtuqFt7653jwcvuUj131+6qGLUyDqfDg1OIoBQ66OCuXC473YQfO2AaxITTutiRQiDwoo7ZLYyg==", + "version": "1.2.5", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@eslint/compat/-/compat-1.2.5.tgz", + "integrity": "sha512-5iuG/StT+7OfvhoBHPlmxkPA9om6aDUFgmD4+mWKAGsYt4vCe8rypneG03AuseyRHBmcCLXQtIH5S26tIoggLg==", "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2871,9 +2871,9 @@ } }, "node_modules/@eslint/core": { - "version": "0.9.1", - "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@eslint/core/-/core-0.9.1.tgz", - "integrity": "sha512-GuUdqkyyzQI5RMIWkHhvTWLCyLo1jNK3vzkSyaExH5kHPDHcuL2VOpHjmMY+y3+NC69qAKToBqldTBgYeLSr9Q==", + "version": "0.10.0", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@eslint/core/-/core-0.10.0.tgz", + "integrity": "sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -2954,9 +2954,9 @@ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, "node_modules/@eslint/js": { - "version": "9.17.0", - "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@eslint/js/-/js-9.17.0.tgz", - "integrity": "sha512-Sxc4hqcs1kTu0iID3kcZDW3JHq2a77HO9P8CP6YEA/FpH3Ll8UXE2r/86Rz9YJLKme39S9vU5OWNjC6Xl0Cr3w==", + "version": "9.18.0", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@eslint/js/-/js-9.18.0.tgz", + "integrity": "sha512-fK6L7rxcq6/z+AaQMtiFTkvbHkBLNlwyRxHpKawP0x3u9+NC6MQTnFW+AdpwC6gfHTW0051cokQgtTN2FqlxQA==", "dev": true, "license": "MIT", "engines": { @@ -2964,12 +2964,12 @@ } }, "node_modules/@eslint/migrate-config": { - "version": "1.3.5", - "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@eslint/migrate-config/-/migrate-config-1.3.5.tgz", - "integrity": "sha512-0NpN+l8p5YE+xP7d0cVAPYFWyTa3msJ6Rkzfl95xmuGhlbiLwPblpaUtZOVmyo2In8KDtW1eoyDRRQ6X7ModsQ==", + "version": "1.3.6", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@eslint/migrate-config/-/migrate-config-1.3.6.tgz", + "integrity": "sha512-SahJ5U/EpBAHhnAPtdzokEral73bVe+/Bn8F5vqs5uBgXHsjhPjbYgsjstSR6qnS6Sl+Wbgi91Zp6UvG26mNpA==", "license": "Apache-2.0", "dependencies": { - "@eslint/compat": "^1.2.4", + "@eslint/compat": "^1.2.5", "@eslint/eslintrc": "^3.1.0", "camelcase": "^8.0.0", "recast": "^0.23.7" @@ -3004,12 +3004,13 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.2.4", - "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@eslint/plugin-kit/-/plugin-kit-0.2.4.tgz", - "integrity": "sha512-zSkKow6H5Kdm0ZUQUB2kV5JIXqoG0+uH5YADhaEHswm664N9Db8dXSi0nMJpacpMf+MyyglF1vnZohpEg5yUtg==", + "version": "0.2.5", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@eslint/plugin-kit/-/plugin-kit-0.2.5.tgz", + "integrity": "sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A==", "dev": true, "license": "Apache-2.0", "dependencies": { + "@eslint/core": "^0.10.0", "levn": "^0.4.1" }, "engines": { @@ -6446,9 +6447,9 @@ } }, "node_modules/@testing-library/react": { - "version": "16.1.0", - "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@testing-library/react/-/react-16.1.0.tgz", - "integrity": "sha512-Q2ToPvg0KsVL0ohND9A3zLJWcOXXcO8IDu3fj11KhNt0UlCWyFyvnCIBkd12tidB2lkiVRG8VFqdhcqhqnAQtg==", + "version": "16.2.0", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@testing-library/react/-/react-16.2.0.tgz", + "integrity": "sha512-2cSskAvA1QNtKc8Y9VJQRv0tm3hLVgxRGDB+KYhIaPQJ1I+RHbhIXcM+zClKXzMes/wshsMVzf4B9vS4IZpqDQ==", "dev": true, "license": "MIT", "dependencies": { @@ -6474,9 +6475,9 @@ } }, "node_modules/@testing-library/user-event": { - "version": "14.5.2", - "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@testing-library/user-event/-/user-event-14.5.2.tgz", - "integrity": "sha512-YAh82Wh4TIrxYLmfGcixwD18oIjyC1pFQC2Y01F2lzV2HTMiYrI0nze0FD0ocB//CKS/7jIUgae+adPqxK5yCQ==", + "version": "14.6.0", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@testing-library/user-event/-/user-event-14.6.0.tgz", + "integrity": "sha512-+jsfK7kVJbqnCYtLTln8Ja/NmVrZRwBJHmHR9IxIVccMWSOZ6Oy0FkDJNeyVu4QSpMNmRfy10Xb76ObRDlWWBQ==", "dev": true, "license": "MIT", "engines": { @@ -8933,9 +8934,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001690", - "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/caniuse-lite/-/caniuse-lite-1.0.30001690.tgz", - "integrity": "sha512-5ExiE3qQN6oF8Clf8ifIDcMRCRE/dMGcETG/XGMD8/XiXm6HXQgQTh1yZYLXXpSOsEUlJm1Xr7kGULZTuGtP/w==", + "version": "1.0.30001692", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/caniuse-lite/-/caniuse-lite-1.0.30001692.tgz", + "integrity": "sha512-A95VKan0kdtrsnMubMKxEKUKImOPSuCpYgxSQBo036P5YYgVIcOYJEgt/txJWqObiRQeISNCfef9nvlQ0vbV7A==", "funding": [ { "type": "opencollective", @@ -11570,19 +11571,19 @@ } }, "node_modules/eslint": { - "version": "9.17.0", - "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/eslint/-/eslint-9.17.0.tgz", - "integrity": "sha512-evtlNcpJg+cZLcnVKwsai8fExnqjGPicK7gnUtlNuzu+Fv9bI0aLpND5T44VLQtoMEnI57LoXO9XAkIXwohKrA==", + "version": "9.18.0", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/eslint/-/eslint-9.18.0.tgz", + "integrity": "sha512-+waTfRWQlSbpt3KWE+CjrPPYnbq9kfZIYUqapc0uBXyjTp8aYXZDsUH16m39Ryq3NjAVP4tjuF7KaukeqoCoaA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.19.0", - "@eslint/core": "^0.9.0", + "@eslint/core": "^0.10.0", "@eslint/eslintrc": "^3.2.0", - "@eslint/js": "9.17.0", - "@eslint/plugin-kit": "^0.2.3", + "@eslint/js": "9.18.0", + "@eslint/plugin-kit": "^0.2.5", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.1", @@ -11904,10 +11905,11 @@ } }, "node_modules/eslint-plugin-prettier": { - "version": "5.2.1", - "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.1.tgz", - "integrity": "sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw==", + "version": "5.2.2", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.2.tgz", + "integrity": "sha512-1yI3/hf35wmlq66C8yOyrujQnel+v5l1Vop5Cl2I6ylyNTT1JbuUUnV3/41PzwTzcyDp/oF0jWE3HXvcH5AQOQ==", "dev": true, + "license": "MIT", "dependencies": { "prettier-linter-helpers": "^1.0.0", "synckit": "^0.9.1" @@ -11934,9 +11936,9 @@ } }, "node_modules/eslint-plugin-react": { - "version": "7.37.3", - "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/eslint-plugin-react/-/eslint-plugin-react-7.37.3.tgz", - "integrity": "sha512-DomWuTQPFYZwF/7c9W2fkKkStqZmBd3uugfqBYLdkZ3Hii23WzZuOLUskGxB8qkSKqftxEeGL1TB2kMhrce0jA==", + "version": "7.37.4", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/eslint-plugin-react/-/eslint-plugin-react-7.37.4.tgz", + "integrity": "sha512-BGP0jRmfYyvOyvMoRX/uoUeW+GqNj9y16bPQzqAHf3AYII/tDs+jMN0dBVkl88/OZwNGwrVFxE7riHsXVfy/LQ==", "dev": true, "license": "MIT", "dependencies": { @@ -13979,9 +13981,10 @@ } }, "node_modules/graphql-ws": { - "version": "5.16.0", - "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/graphql-ws/-/graphql-ws-5.16.0.tgz", - "integrity": "sha512-Ju2RCU2dQMgSKtArPbEtsK5gNLnsQyTNIo/T7cZNp96niC1x0KdJNZV0TIoilceBPQwfb5itrGl8pkFeOUMl4A==", + "version": "5.16.2", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/graphql-ws/-/graphql-ws-5.16.2.tgz", + "integrity": "sha512-E1uccsZxt/96jH/OwmLPuXMACILs76pKF2i3W861LpKBCYtGIyPQGtWLuBLkND4ox1KHns70e83PS4te50nvPQ==", + "license": "MIT", "engines": { "node": ">=10" }, @@ -26729,7 +26732,7 @@ }, "node_modules/rollup": { "version": "2.79.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.2.tgz", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/rollup/-/rollup-2.79.2.tgz", "integrity": "sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==", "dev": true, "bin": { @@ -27205,9 +27208,9 @@ "dev": true }, "node_modules/sass": { - "version": "1.83.1", - "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/sass/-/sass-1.83.1.tgz", - "integrity": "sha512-EVJbDaEs4Rr3F0glJzFSOvtg2/oy2V/YrGFPqPY24UqcLDWcI9ZY5sN+qyO3c/QCZwzgfirvhXvINiJCE/OLcA==", + "version": "1.83.4", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/sass/-/sass-1.83.4.tgz", + "integrity": "sha512-B1bozCeNQiOgDcLd33e2Cs2U60wZwjUUXzh900ZyQF5qUasvMdDZYbQ566LJu7cqR+sAHlAfO6RMkaID5s6qpA==", "license": "MIT", "dependencies": { "chokidar": "^4.0.0", @@ -27284,12 +27287,12 @@ "license": "MIT" }, "node_modules/sass/node_modules/readdirp": { - "version": "4.0.2", - "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/readdirp/-/readdirp-4.0.2.tgz", - "integrity": "sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==", + "version": "4.1.1", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/readdirp/-/readdirp-4.1.1.tgz", + "integrity": "sha512-h80JrZu/MHUZCyHu5ciuoI0+WxsCxzxJTILn6Fs8rxSnFPh+UVHYfeIxK1nVGugMqkfC4vJcBOYbkfkwYK0+gw==", "license": "MIT", "engines": { - "node": ">= 14.16.0" + "node": ">= 14.18.0" }, "funding": { "type": "individual", @@ -28364,9 +28367,9 @@ "dev": true }, "node_modules/start-server-and-test": { - "version": "2.0.9", - "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/start-server-and-test/-/start-server-and-test-2.0.9.tgz", - "integrity": "sha512-DDceIvc4wdpr+z3Aqkot2QMho8TcUBh5qH0wEHDpEexBTzlheOcmh53d3dExABY4J5C7qS2UbSXqRWLtxpbWIQ==", + "version": "2.0.10", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/start-server-and-test/-/start-server-and-test-2.0.10.tgz", + "integrity": "sha512-nZphcfcqGqwk74lbZkqSwClkYz+M5ZPGOMgWxNVJrdztPKN96qe6HooRu6L3TpwITn0lKJJdKACqHbJtqythOQ==", "dev": true, "license": "MIT", "dependencies": { @@ -28377,7 +28380,7 @@ "execa": "5.1.1", "lazy-ass": "1.6.0", "ps-tree": "1.2.0", - "wait-on": "8.0.1" + "wait-on": "8.0.2" }, "bin": { "server-test": "src/bin/start.js", @@ -29825,10 +29828,11 @@ } }, "node_modules/tough-cookie": { - "version": "5.0.0", - "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/tough-cookie/-/tough-cookie-5.0.0.tgz", - "integrity": "sha512-FRKsF7cz96xIIeMZ82ehjC3xW2E+O2+v11udrDYewUbszngYhsGa8z6YUMMzO9QJZzzyd0nGGXnML/TReX6W8Q==", + "version": "5.1.0", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/tough-cookie/-/tough-cookie-5.1.0.tgz", + "integrity": "sha512-rvZUv+7MoBYTiDmFPBrhL7Ujx9Sk+q9wwm22x8c8T5IJaR+Wsyc7TNxbVxo84kZoRJZZMazowFLqpankBEQrGg==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "tldts": "^6.1.32" }, @@ -30644,12 +30648,13 @@ } }, "node_modules/wait-on": { - "version": "8.0.1", - "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/wait-on/-/wait-on-8.0.1.tgz", - "integrity": "sha512-1wWQOyR2LVVtaqrcIL2+OM+x7bkpmzVROa0Nf6FryXkS+er5Sa1kzFGjzZRqLnHa3n1rACFLeTwUqE1ETL9Mig==", + "version": "8.0.2", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/wait-on/-/wait-on-8.0.2.tgz", + "integrity": "sha512-qHlU6AawrgAIHlueGQHQ+ETcPLAauXbnoTKl3RKq20W0T8x0DKVAo5xWIYjHSyvHxQlcYbFdR0jp4T9bDVITFA==", "dev": true, + "license": "MIT", "dependencies": { - "axios": "^1.7.7", + "axios": "^1.7.9", "joi": "^17.13.3", "lodash": "^4.17.21", "minimist": "^1.2.8", diff --git a/api-catalog-ui/frontend/package.json b/api-catalog-ui/frontend/package.json index 014eda5a45..581d7a16d3 100644 --- a/api-catalog-ui/frontend/package.json +++ b/api-catalog-ui/frontend/package.json @@ -48,7 +48,7 @@ "redux-persist-transform-filter": "0.0.22", "redux-thunk": "3.1.0", "rxjs": "7.8.1", - "sass": "1.83.1", + "sass": "1.83.4", "stream": "0.0.3", "swagger-ui-react": "5.18.2", "url": "0.11.4", @@ -81,28 +81,28 @@ }, "devDependencies": { "@babel/core": "7.26.0", - "@babel/eslint-parser": "7.25.9", + "@babel/eslint-parser": "7.26.5", "@babel/plugin-proposal-private-property-in-object": "7.21.11", "@babel/preset-env": "7.26.0", "@babel/preset-react": "7.26.3", "@cfaester/enzyme-adapter-react-18": "0.8.0", - "@eslint/compat": "1.2.4", - "@eslint/js": "9.17.0", + "@eslint/compat": "1.2.5", + "@eslint/js": "9.18.0", "@testing-library/dom": "10.4.0", "@testing-library/jest-dom": "6.6.3", - "@testing-library/react": "16.1.0", - "@testing-library/user-event": "14.5.2", + "@testing-library/react": "16.2.0", + "@testing-library/user-event": "14.6.0", "ajv": "8.17.1", "ansi-regex": "6.1.0", "body-parser": "1.20.3", - "caniuse-lite": "1.0.30001690", + "caniuse-lite": "1.0.30001692", "concurrently": "9.1.2", "cors": "2.8.5", "cross-env": "7.0.3", "cypress": "13.17.0", "cypress-file-upload": "5.0.8", "enzyme": "3.11.0", - "eslint": "9.17.0", + "eslint": "9.18.0", "eslint-config-airbnb": "19.0.4", "eslint-config-prettier": "9.1.0", "eslint-plugin-cypress": "4.1.0", @@ -110,8 +110,8 @@ "eslint-plugin-header": "3.1.1", "eslint-plugin-import": "2.31.0", "eslint-plugin-jsx-a11y": "6.10.2", - "eslint-plugin-prettier": "5.2.1", - "eslint-plugin-react": "7.37.3", + "eslint-plugin-prettier": "5.2.2", + "eslint-plugin-react": "7.37.4", "express": "4.21.2", "globals": "15.14.0", "html-loader": "5.1.0", @@ -135,7 +135,7 @@ "redux-mock-store": "1.5.5", "rimraf": "6.0.1", "source-map-explorer": "2.5.3", - "start-server-and-test": "2.0.9", + "start-server-and-test": "2.0.10", "tmpl": "1.0.5", "undici": "6.19.8", "yaml": "2.7.0" @@ -145,12 +145,12 @@ "jsdom": "16.7.0", "got": "14.4.5", "react-error-overlay": "6.0.11", - "tough-cookie": "5.0.0", + "tough-cookie": "5.1.0", "@braintree/sanitize-url": "7.1.1", "resolve-url-loader": "5.0.0", "lodash": "4.17.21", "semver": "7.6.3", - "@babel/traverse": "7.26.4", + "@babel/traverse": "7.26.5", "axios": "1.7.9" }, "engines": { diff --git a/apiml-common/src/main/java/org/zowe/apiml/product/web/DiscoveryRestTemplateConfig.java b/apiml-common/src/main/java/org/zowe/apiml/product/web/DiscoveryRestTemplateConfig.java index 8991ced61f..888c7fb61e 100644 --- a/apiml-common/src/main/java/org/zowe/apiml/product/web/DiscoveryRestTemplateConfig.java +++ b/apiml-common/src/main/java/org/zowe/apiml/product/web/DiscoveryRestTemplateConfig.java @@ -12,12 +12,13 @@ import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; -import org.springframework.cloud.netflix.eureka.RestTemplateTimeoutProperties; +import org.springframework.cloud.netflix.eureka.RestClientTimeoutProperties; import org.springframework.cloud.netflix.eureka.http.DefaultEurekaClientHttpRequestFactorySupplier; -import org.springframework.cloud.netflix.eureka.http.RestTemplateDiscoveryClientOptionalArgs; -import org.springframework.cloud.netflix.eureka.http.RestTemplateTransportClientFactories; +import org.springframework.cloud.netflix.eureka.http.RestClientDiscoveryClientOptionalArgs; +import org.springframework.cloud.netflix.eureka.http.RestClientTransportClientFactories; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestClient; import org.zowe.apiml.message.log.ApimlLogger; import org.zowe.apiml.message.yaml.YamlMessageServiceInstance; @@ -30,15 +31,16 @@ public class DiscoveryRestTemplateConfig { private static final ApimlLogger apimlLog = ApimlLogger.of(DiscoveryRestTemplateConfig.class, YamlMessageServiceInstance.getInstance()); @Bean - public RestTemplateTransportClientFactories restTemplateTransportClientFactories(RestTemplateDiscoveryClientOptionalArgs restTemplateDiscoveryClientOptionalArgs) { - return new RestTemplateTransportClientFactories(restTemplateDiscoveryClientOptionalArgs); + public RestClientTransportClientFactories restTemplateTransportClientFactories(RestClientDiscoveryClientOptionalArgs restClientDiscoveryClientOptionalArgs) { + return new RestClientTransportClientFactories(restClientDiscoveryClientOptionalArgs); } @Bean - public RestTemplateDiscoveryClientOptionalArgs defaultArgs(@Value("${eureka.client.serviceUrl.defaultZone}") String eurekaServerUrl, - @Qualifier("secureSslContext") SSLContext secureSslContext, - HostnameVerifier secureHostnameVerifier) { - RestTemplateDiscoveryClientOptionalArgs clientArgs = new RestTemplateDiscoveryClientOptionalArgs(getDefaultEurekaClientHttpRequestFactorySupplier()); + public RestClientDiscoveryClientOptionalArgs defaultArgs(@Value("${eureka.client.serviceUrl.defaultZone}") String eurekaServerUrl, + @Qualifier("secureSslContext") SSLContext secureSslContext, + HostnameVerifier secureHostnameVerifier + ) { + RestClientDiscoveryClientOptionalArgs clientArgs = new RestClientDiscoveryClientOptionalArgs(getDefaultEurekaClientHttpRequestFactorySupplier(), RestClient::builder); if (eurekaServerUrl.startsWith("http://")) { apimlLog.log("org.zowe.apiml.common.insecureHttpWarning"); @@ -51,7 +53,7 @@ public RestTemplateDiscoveryClientOptionalArgs defaultArgs(@Value("${eureka.clie } private static DefaultEurekaClientHttpRequestFactorySupplier getDefaultEurekaClientHttpRequestFactorySupplier() { - RestTemplateTimeoutProperties properties = new RestTemplateTimeoutProperties(); + RestClientTimeoutProperties properties = new RestClientTimeoutProperties(); properties.setConnectTimeout(180000); properties.setConnectRequestTimeout(180000); properties.setSocketTimeout(180000); diff --git a/apiml-common/src/test/java/org/zowe/apiml/product/gateway/GatewayInstanceInitializerTest.java b/apiml-common/src/test/java/org/zowe/apiml/product/gateway/GatewayInstanceInitializerTest.java index d772f87ebd..e1a62fe473 100644 --- a/apiml-common/src/test/java/org/zowe/apiml/product/gateway/GatewayInstanceInitializerTest.java +++ b/apiml-common/src/test/java/org/zowe/apiml/product/gateway/GatewayInstanceInitializerTest.java @@ -18,7 +18,6 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.TestConfiguration; -import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.annotation.Bean; import org.springframework.test.context.junit.jupiter.SpringExtension; @@ -29,9 +28,8 @@ import java.util.Collections; import static java.time.Duration.ofMillis; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTimeout; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @ExtendWith(SpringExtension.class) @@ -79,8 +77,10 @@ void testInit() { @TestConfiguration static class TestConfig { - @MockBean - private EurekaClient eurekaClient; + @Bean + public EurekaClient eurekaClient() { + return mock(EurekaClient.class); + } @Bean public GatewayClient gatewayClient() { @@ -88,16 +88,16 @@ public GatewayClient gatewayClient() { } @Bean - public InstanceLookupExecutor instanceLookupExecutor() { + public InstanceLookupExecutor instanceLookupExecutor(EurekaClient eurekaClient) { return new InstanceLookupExecutor( eurekaClient ); } @Bean - public GatewayInstanceInitializer gatewayInstanceInitializer(ApplicationEventPublisher applicationEventPublisher) { + public GatewayInstanceInitializer gatewayInstanceInitializer(ApplicationEventPublisher applicationEventPublisher, EurekaClient eurekaClient) { return new GatewayInstanceInitializer( - instanceLookupExecutor(), + instanceLookupExecutor(eurekaClient), applicationEventPublisher, gatewayClient() ); diff --git a/apiml-extension-loader/src/test/java/org/zowe/apiml/extension/ExtensionsLoaderTest.java b/apiml-extension-loader/src/test/java/org/zowe/apiml/extension/ExtensionsLoaderTest.java index d69a6c88bf..5e363c55ad 100644 --- a/apiml-extension-loader/src/test/java/org/zowe/apiml/extension/ExtensionsLoaderTest.java +++ b/apiml-extension-loader/src/test/java/org/zowe/apiml/extension/ExtensionsLoaderTest.java @@ -18,7 +18,6 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.context.event.ApplicationContextInitializedEvent; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; @@ -27,6 +26,7 @@ import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.TestContextManager; +import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.test.util.ReflectionTestUtils; import org.zowe.CustomBean; @@ -43,10 +43,10 @@ class ExtensionsLoaderTest { @Autowired private ApplicationEventPublisher publisher; - @MockBean + @MockitoBean private ExtensionsLoader extensionsLoader; - @MockBean + @MockitoBean private ExtensionConfigReader reader; @AfterEach diff --git a/apiml-security-common/src/main/java/org/zowe/apiml/security/common/filter/CategorizeCertsFilter.java b/apiml-security-common/src/main/java/org/zowe/apiml/security/common/filter/CategorizeCertsFilter.java index 21e51e540f..50ab77085b 100644 --- a/apiml-security-common/src/main/java/org/zowe/apiml/security/common/filter/CategorizeCertsFilter.java +++ b/apiml-security-common/src/main/java/org/zowe/apiml/security/common/filter/CategorizeCertsFilter.java @@ -22,11 +22,9 @@ import lombok.Setter; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; -import org.springframework.security.authentication.InsufficientAuthenticationException; import org.springframework.web.filter.OncePerRequestFilter; import org.zowe.apiml.message.log.ApimlLogger; import org.zowe.apiml.product.logging.annotations.InjectApimlLogger; -import org.zowe.apiml.security.common.error.AuthExceptionHandler; import org.zowe.apiml.security.common.verify.CertificateValidator; import java.io.ByteArrayInputStream; @@ -55,17 +53,8 @@ public class CategorizeCertsFilter extends OncePerRequestFilter { @Getter private final Set publicKeyCertificatesBase64; - //TODO:cleanup private final CertificateValidator certificateValidator; - final AuthExceptionHandler authExceptionHandler; - - private final boolean rejectIfZoweCertificateMissing; - - public CategorizeCertsFilter(Set publicKeyCertificatesBase64, CertificateValidator certificateValidator, AuthExceptionHandler authExceptionHandler) { - this(publicKeyCertificatesBase64, certificateValidator, authExceptionHandler, true); - } - /** * Get certificates from request (if exists), separate them (to use only APIML certificate to request sign and * other for authentication) and store again into request. @@ -75,7 +64,6 @@ public CategorizeCertsFilter(Set publicKeyCertificatesBase64, Certificat * in the default attribute. * * @param request Request to filter certificates - * @throws InsufficientAuthenticationException Optionally, if the Zowe server certificate is missing and rejectIfZoweCertificateMissing is set to true */ private void categorizeCerts(ServletRequest request) { X509Certificate[] certs = (X509Certificate[]) request.getAttribute(ATTRNAME_JAKARTA_SERVLET_REQUEST_X509_CERTIFICATE); @@ -93,15 +81,10 @@ private void categorizeCerts(ServletRequest request) { } log.debug(LOG_FORMAT_FILTERING_CERTIFICATES, ATTRNAME_CLIENT_AUTH_X509_CERTIFICATE, request.getAttribute(ATTRNAME_CLIENT_AUTH_X509_CERTIFICATE)); - -// } else if (rejectIfZoweCertificateMissing) { -// log.debug("No X509 certificate found in request attribute {}", ATTRNAME_JAKARTA_SERVLET_REQUEST_X509_CERTIFICATE); -// throw new InsufficientAuthenticationException("No Zowe Server X509 certificate found in request"); } - } - Optional getClientCertFromHeader(HttpServletRequest request) { + private Optional getClientCertFromHeader(HttpServletRequest request) { String certFromHeader = request.getHeader(CLIENT_CERT_HEADER); if (StringUtils.isNotEmpty(certFromHeader)) { @@ -124,7 +107,7 @@ Optional getClientCertFromHeader(HttpServletRequest request) { * @param originalRequest incoming original http request object * @return wrapped http request object with overridden functions */ - HttpServletRequest mutate(HttpServletRequest originalRequest) { + private HttpServletRequest mutate(HttpServletRequest originalRequest) { return new HttpServletRequestWrapper(originalRequest) { @Override @@ -155,12 +138,7 @@ public Enumeration getHeaders(String name) { **/ @Override protected void doFilterInternal(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull FilterChain filterChain) throws ServletException, IOException { - try { - categorizeCerts(request); - } catch (InsufficientAuthenticationException ex) { - authExceptionHandler.handleException(request, response, ex); - return; - } + categorizeCerts(request); filterChain.doFilter(mutate(request), response); } diff --git a/apiml-security-common/src/main/java/org/zowe/apiml/security/common/filter/X509ClientRejectIfMissingFilter.java b/apiml-security-common/src/main/java/org/zowe/apiml/security/common/filter/X509ClientRejectIfMissingFilter.java deleted file mode 100644 index 67057be665..0000000000 --- a/apiml-security-common/src/main/java/org/zowe/apiml/security/common/filter/X509ClientRejectIfMissingFilter.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * This program and the accompanying materials are made available under the terms of the - * Eclipse Public License v2.0 which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-v20.html - * - * SPDX-License-Identifier: EPL-2.0 - * - * Copyright Contributors to the Zowe Project. - */ - -package org.zowe.apiml.security.common.filter; - -import jakarta.servlet.FilterChain; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import lombok.extern.slf4j.Slf4j; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; -import org.zowe.apiml.security.common.error.AuthExceptionHandler; -import org.zowe.apiml.security.common.error.InvalidCertificateException; -import org.zowe.apiml.security.common.verify.CertificateValidator; - -import java.io.IOException; -import java.security.cert.X509Certificate; -import java.util.HashSet; - -/** - * Checks the client certificate is present in the request. No further validation is done, the client certificate - * was already validated by Gateway. Servers as a counterpart to {@link CategorizeCertsFilter} for simple scenarios - * when client certificate is required to be present but is not used for authorization. - */ - -@Slf4j -public class X509ClientRejectIfMissingFilter extends CategorizeCertsFilter { - - public X509ClientRejectIfMissingFilter(CertificateValidator certificateValidator, AuthExceptionHandler authExceptionHandler) { - super(new HashSet<>(), certificateValidator, authExceptionHandler, true); - } - - @Override - protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { - var certOpt = getClientCertFromHeader(request); - if (certOpt.isEmpty()) { - log.debug("No X509 client certificate found in request."); - authExceptionHandler.handleException(request, response, new InvalidCertificateException("X509 client certificate required but missing.")); - return; - } - - var cert = (X509Certificate) certOpt.get(); - var auth = new PreAuthenticatedAuthenticationToken(cert.getSubjectX500Principal(), cert); - auth.setAuthenticated(true); - SecurityContextHolder.getContext().setAuthentication(auth); - - filterChain.doFilter(mutate(request), response); - } -} diff --git a/apiml-security-common/src/main/java/org/zowe/apiml/security/common/verify/CertificateValidator.java b/apiml-security-common/src/main/java/org/zowe/apiml/security/common/verify/CertificateValidator.java index c37a189468..db2131035c 100644 --- a/apiml-security-common/src/main/java/org/zowe/apiml/security/common/verify/CertificateValidator.java +++ b/apiml-security-common/src/main/java/org/zowe/apiml/security/common/verify/CertificateValidator.java @@ -62,9 +62,9 @@ public boolean hasGatewayChain(X509Certificate[] certs) { return false; } List trustedCerts = Arrays.stream(proxyCertificatesEndpoints) - .map(trustedCertificatesProvider::getTrustedCerts) - .flatMap(List::stream) - .toList(); + .map(trustedCertificatesProvider::getTrustedCerts) + .flatMap(List::stream) + .toList(); for (X509Certificate cert : certs) { if (!trustedCerts.contains(cert)) { log.debug("Certificate is not trusted by endpoint {}. Untrusted certificate is {}", proxyCertificatesEndpoints, cert); diff --git a/apiml-security-common/src/test/java/org/zowe/apiml/security/common/filter/CategorizeCertsFilterTest.java b/apiml-security-common/src/test/java/org/zowe/apiml/security/common/filter/CategorizeCertsFilterTest.java index 38fb36c9e9..dff641bfbc 100644 --- a/apiml-security-common/src/test/java/org/zowe/apiml/security/common/filter/CategorizeCertsFilterTest.java +++ b/apiml-security-common/src/test/java/org/zowe/apiml/security/common/filter/CategorizeCertsFilterTest.java @@ -19,7 +19,6 @@ import org.springframework.mock.web.MockFilterChain; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; -import org.zowe.apiml.security.common.error.AuthExceptionHandler; import org.zowe.apiml.security.common.utils.X509Utils; import org.zowe.apiml.security.common.verify.CertificateValidator; @@ -72,7 +71,6 @@ class CategorizeCertsFilterTest { private X509Certificate[] clientCerts; private CertificateValidator certificateValidator; - private AuthExceptionHandler authExceptionHandler; @BeforeAll public static void init() throws CertificateException { @@ -82,15 +80,13 @@ public static void init() throws CertificateException { } @BeforeEach - public void setUp() throws ServletException { + public void setUp() { request = new MockHttpServletRequest(); response = new MockHttpServletResponse(); chain = new MockFilterChain(); certificateValidator = mock(CertificateValidator.class); - authExceptionHandler = mock(AuthExceptionHandler.class); when(certificateValidator.isForwardingEnabled()).thenReturn(false); when(certificateValidator.hasGatewayChain(any())).thenReturn(false); - doNothing().when(authExceptionHandler).handleException(any(), any(), any()); } @Nested @@ -98,7 +94,7 @@ class GivenNoPublicKeysInFilter { @BeforeEach void setUp() { - filter = new CategorizeCertsFilter(new HashSet<>(), certificateValidator, authExceptionHandler, false); + filter = new CategorizeCertsFilter(new HashSet<>(), certificateValidator); } @Nested @@ -313,7 +309,6 @@ class WhenOtherHeadersInRequest { private static final String COMMON_HEADER = "User-Agent"; private static final String COMMON_HEADER_VALUE = "dummy"; - @BeforeEach void setUp() { request.addHeader(COMMON_HEADER, COMMON_HEADER_VALUE); @@ -341,7 +336,7 @@ void setUp() { X509Utils.correctBase64("apimlCert1"), X509Utils.correctBase64("apimlCertCA") )); - filter = new CategorizeCertsFilter(serverCertChain, certificateValidator, authExceptionHandler); + filter = new CategorizeCertsFilter(serverCertChain, certificateValidator); } @Nested @@ -491,32 +486,4 @@ void thenClientCertHeaderIgnored() throws ServletException, IOException { } } } - - //TODO: validate if needed -// @Nested -// class WhenNoZoweServerCertificateProvided { -// -// private MockHttpServletRequest requestWithNoCertificates; -// -// @BeforeEach -// void setup() { -// requestWithNoCertificates = new MockHttpServletRequest(); -// } -// -// @Test -// void givenRejectOnMissingZoweServerCertificateEnabled_thenThrowException() throws ServletException, IOException { -// var rejectOnFilter = new CategorizeCertsFilter(new HashSet<>(), certificateValidator, authExceptionHandler); -// ArgumentCaptor argument = ArgumentCaptor.forClass(RuntimeException.class); -// rejectOnFilter.doFilter(requestWithNoCertificates, response, chain); -// verify(authExceptionHandler, times(1)).handleException(any(), any(), argument.capture()); -// assertInstanceOf(InsufficientAuthenticationException.class, argument.getValue()); -// } -// -// @Test -// void givenRejectOnMissingZoweServerCertificateDisabled_thenDoNotThrowException() throws ServletException, IOException { -// var rejectOffFilter = new CategorizeCertsFilter(new HashSet<>(), certificateValidator, authExceptionHandler, false); -// rejectOffFilter.doFilter(request, response, chain); -// verify(authExceptionHandler, never()).handleException(any(), any(), any()); -// } -// } } diff --git a/apiml-security-common/src/test/java/org/zowe/apiml/security/common/filter/X509ClientRejectIfMissingFilterTest.java b/apiml-security-common/src/test/java/org/zowe/apiml/security/common/filter/X509ClientRejectIfMissingFilterTest.java deleted file mode 100644 index d37a57cf6f..0000000000 --- a/apiml-security-common/src/test/java/org/zowe/apiml/security/common/filter/X509ClientRejectIfMissingFilterTest.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * This program and the accompanying materials are made available under the terms of the - * Eclipse Public License v2.0 which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-v20.html - * - * SPDX-License-Identifier: EPL-2.0 - * - * Copyright Contributors to the Zowe Project. - */ - -package org.zowe.apiml.security.common.filter; - -import jakarta.servlet.Filter; -import jakarta.servlet.Servlet; -import jakarta.servlet.ServletException; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.Mockito; -import org.springframework.mock.web.MockFilterChain; -import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.mock.web.MockHttpServletResponse; -import org.springframework.security.core.context.SecurityContextHolder; -import org.zowe.apiml.security.common.error.AuthExceptionHandler; -import org.zowe.apiml.security.common.error.InvalidCertificateException; -import org.zowe.apiml.security.common.verify.CertificateValidator; - -import java.io.IOException; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.*; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.*; - -class X509ClientRejectIfMissingFilterTest { - - private static final String CLIENT_CERT_HEADER = "Client-Cert"; - private static final String CLIENT_CERT_HEADER_VALUE = - "MIIEFTCCAv2gAwIBAgIEKWdbVTANBgkqhkiG9w0BAQsFADCBjDELMAkGA1UEBhMC" + - "Q1oxDTALBgNVBAgTBEJybm8xDTALBgNVBAcTBEJybm8xFDASBgNVBAoTC1pvd2Ug" + - "U2FtcGxlMRwwGgYDVQQLExNBUEkgTWVkaWF0aW9uIExheWVyMSswKQYDVQQDEyJa" + - "b3dlIFNlbGYtU2lnbmVkIFVudHJ1c3RlZCBTZXJ2aWNlMB4XDTE4MTIwNzIwMDc1" + - "MloXDTI4MTIwNDIwMDc1MlowgYwxCzAJBgNVBAYTAkNaMQ0wCwYDVQQIEwRCcm5v" + - "MQ0wCwYDVQQHEwRCcm5vMRQwEgYDVQQKEwtab3dlIFNhbXBsZTEcMBoGA1UECxMT" + - "QVBJIE1lZGlhdGlvbiBMYXllcjErMCkGA1UEAxMiWm93ZSBTZWxmLVNpZ25lZCBV" + - "bnRydXN0ZWQgU2VydmljZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB" + - "AJti8p4nr8ztRSbemrAv1ytVLQMbXozhLe3lNaiVADGTFPZYeJ2lDt7oAl238HOY" + - "ScpOz+JjTeUkL0jsjNYgMhi4J07II/3sJL0SBfVqvvgjUL4BvcpdBl0crSuI/3D4" + - "OaPue+ZmPFijwdCcw5JbazMoOka/zUwpYYdbwxPUH2BbKfwtmmygX88nkJcRSoQO" + - "KBdNsUs+QRuUiokZ/FJi7uiOsNZ8eEfQv6qJ7mOJ7l1IrMcNm3jHgodoQi/4jXO1" + - "np/hZaz/ZDni9kBwcyd64AViB2v7VrrBmjdESt1mtCIMvKMlwAZAqrDO75Q9pepO" + - "Y7zbN4s9s7IUfyb9431xg2MCAwEAAaN9MHswHQYDVR0lBBYwFAYIKwYBBQUHAwIG" + - "CCsGAQUFBwMBMA4GA1UdDwEB/wQEAwIE8DArBgNVHREEJDAighVsb2NhbGhvc3Qu" + - "bG9jYWxkb21haW6CCWxvY2FsaG9zdDAdBgNVHQ4EFgQUIeSN7aNtwH2MnBAGDLre" + - "TtcSaZ4wDQYJKoZIhvcNAQELBQADggEBAELPbHlG60nO164yrBjZcpQJ/2e5ThOR" + - "8efXUWExuy/NpwVx0vJg4tb8s9NI3X4pRh3WyD0uGPGkO9w+CAvgUaECePLYjkov" + - "KIS6Cvlcav9nWqdZau1fywltmOLu8Sq5i42Yvb7ZcPOEwDShpuq0ql7LR7j7P4XH" + - "+JkA0k9Zi6RfYJAyOOpbD2R4JoMbxBKrxUVs7cEajl2ltckjyRWoB6FBud1IthRR" + - "mZoPMtlCleKlsKp7yJiE13hpX+qIGnzEQE2gNgQ94dSl4m2xO6pnyDRMAEncmd33" + - "oehy77omRxNsLzkWe6mjaC8ShMGzG9jYR02iN2h4083/PVXvTZIqwhg="; - - private MockHttpServletRequest request; - private MockHttpServletResponse response; - private MockFilterChain chain; - private AuthExceptionHandler authExceptionHandler; - private Filter nextFilter; - private CertificateValidator certificateValidator; - - @BeforeEach - void setUp() { - authExceptionHandler = Mockito.mock(AuthExceptionHandler.class); - certificateValidator = Mockito.mock(CertificateValidator.class); - request = new MockHttpServletRequest(); - response = new MockHttpServletResponse(); - nextFilter = Mockito.mock(Filter.class); - chain = new MockFilterChain(Mockito.mock(Servlet.class), new X509ClientRejectIfMissingFilter(certificateValidator, authExceptionHandler), nextFilter); - SecurityContextHolder.clearContext(); - } - - @Test - void whenClientCertMissing_thenRejectAndStop() throws ServletException, IOException { - chain.doFilter(request, response); - verify(authExceptionHandler, times(1)) - .handleException(any(), any(), any(InvalidCertificateException.class)); - verify(nextFilter, never()).doFilter(any(), any(), eq(chain)); - assertThat(SecurityContextHolder.getContext().getAuthentication(), is(nullValue())); - } - - @Test - void whenClientCertPresent_thenAllowAndContinue() throws ServletException, IOException { - request.addHeader(CLIENT_CERT_HEADER, CLIENT_CERT_HEADER_VALUE); - chain.doFilter(request, response); - verify(authExceptionHandler, never()).handleException(any(), any(), any()); - verify(nextFilter, times(1)).doFilter(any(), any(), eq(chain)); - assertThat(SecurityContextHolder.getContext().getAuthentication().getCredentials(), is(notNullValue())); - } -} diff --git a/apiml-security-common/src/test/java/org/zowe/apiml/security/common/verify/CertificateValidatorTest.java b/apiml-security-common/src/test/java/org/zowe/apiml/security/common/verify/CertificateValidatorTest.java index 7b297fc619..cface17c63 100644 --- a/apiml-security-common/src/test/java/org/zowe/apiml/security/common/verify/CertificateValidatorTest.java +++ b/apiml-security-common/src/test/java/org/zowe/apiml/security/common/verify/CertificateValidatorTest.java @@ -15,9 +15,9 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Import; import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.util.ReflectionTestUtils; import org.zowe.apiml.security.common.utils.X509Utils; @@ -129,9 +129,11 @@ void whenAllCertificatesFoundThenTheyAreTrusted() { @Nested @Import(CertificateValidator.class) - @MockBean(TrustedCertificatesProvider.class) class Configuration { + @MockitoBean + private TrustedCertificatesProvider trustedCertificatesProvider; + @Nested @TestPropertySource(properties = { "apiml.security.x509.certificatesUrl=url1" diff --git a/build.gradle b/build.gradle index a22cfd66c9..40bea34ef7 100644 --- a/build.gradle +++ b/build.gradle @@ -98,9 +98,6 @@ allprojects { configurations.all { resolutionStrategy.dependencySubstitution { substitute(module('javax.servlet:servlet-api')).using(module('javax.servlet:javax.servlet-api:4.0.1')) - // netty reactor contains a bug: https://github.com/reactor/reactor-netty/issues/3559 > https://github.com/reactor/reactor-netty/pull/3581 - substitute(module('io.projectreactor.netty:reactor-netty-core')).using(module('io.projectreactor.netty:reactor-netty-core:1.1.23')) - substitute(module('io.projectreactor.netty:reactor-netty-http')).using(module('io.projectreactor.netty:reactor-netty-http:1.1.23')) } } @@ -112,7 +109,8 @@ configure(subprojects.findAll { it.name in [ 'caching-service', 'discovery-service', 'gateway-service', - 'zaas-service' + 'zaas-service', + 'integration-tests' ]}) { configurations.all { diff --git a/common-service-core/src/main/java/org/zowe/apiml/util/CorsUtils.java b/common-service-core/src/main/java/org/zowe/apiml/util/CorsUtils.java index c38b594312..97e0f83184 100644 --- a/common-service-core/src/main/java/org/zowe/apiml/util/CorsUtils.java +++ b/common-service-core/src/main/java/org/zowe/apiml/util/CorsUtils.java @@ -27,7 +27,7 @@ public class CorsUtils { private static final List allowedCorsHttpMethods; private final boolean corsEnabled; private final List allowedOrigins; - private static final Pattern gatewayRoutesPattern = Pattern.compile("apiml\\.routes.*.gateway\\S*"); + private static final Pattern gatewayRoutesPattern = Pattern.compile("apiml\\.routes\\.[^.]*\\.gateway\\S*"); private static final List CORS_ENABLED_ENDPOINTS = Arrays.asList("/*/*/gateway/**", "/gateway/*/*/**", "/gateway/version"); diff --git a/discoverable-client/src/test/java/org/zowe/apiml/client/api/ApiMediationClientTestControllerTest.java b/discoverable-client/src/test/java/org/zowe/apiml/client/api/ApiMediationClientTestControllerTest.java index f3955d0a1a..5ef213e374 100644 --- a/discoverable-client/src/test/java/org/zowe/apiml/client/api/ApiMediationClientTestControllerTest.java +++ b/discoverable-client/src/test/java/org/zowe/apiml/client/api/ApiMediationClientTestControllerTest.java @@ -10,13 +10,12 @@ package org.zowe.apiml.client.api; -import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.Test; - +import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; -import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Import; +import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.web.servlet.MockMvc; import org.zowe.apiml.client.configuration.SecurityConfiguration; @@ -37,7 +36,7 @@ class ApiMediationClientTestControllerTest { @Autowired private MockMvc mockMvc; - @MockBean + @MockitoBean private ApiMediationClientService apiMediationClientService; @Test diff --git a/discoverable-client/src/test/java/org/zowe/apiml/client/api/PetControllerDeleteTest.java b/discoverable-client/src/test/java/org/zowe/apiml/client/api/PetControllerDeleteTest.java index 730f440ac1..fcb9895766 100644 --- a/discoverable-client/src/test/java/org/zowe/apiml/client/api/PetControllerDeleteTest.java +++ b/discoverable-client/src/test/java/org/zowe/apiml/client/api/PetControllerDeleteTest.java @@ -14,8 +14,8 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; -import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Import; +import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.web.servlet.MockMvc; import org.zowe.apiml.client.configuration.ApplicationConfiguration; @@ -34,7 +34,7 @@ class PetControllerDeleteTest { @Autowired private MockMvc mockMvc; - @MockBean + @MockitoBean private PetService petService; @Test diff --git a/discoverable-client/src/test/java/org/zowe/apiml/client/api/PetControllerGetAllTest.java b/discoverable-client/src/test/java/org/zowe/apiml/client/api/PetControllerGetAllTest.java index 2a6d661a8a..410df69a42 100644 --- a/discoverable-client/src/test/java/org/zowe/apiml/client/api/PetControllerGetAllTest.java +++ b/discoverable-client/src/test/java/org/zowe/apiml/client/api/PetControllerGetAllTest.java @@ -14,8 +14,8 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; -import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Import; +import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.web.servlet.MockMvc; import org.zowe.apiml.client.configuration.ApplicationConfiguration; @@ -41,7 +41,7 @@ class PetControllerGetAllTest { @Autowired private MockMvc mockMvc; - @MockBean + @MockitoBean private PetService petService; @Test diff --git a/discoverable-client/src/test/java/org/zowe/apiml/client/api/PetControllerGetOneTest.java b/discoverable-client/src/test/java/org/zowe/apiml/client/api/PetControllerGetOneTest.java index 847482ee5d..b92863fc1c 100644 --- a/discoverable-client/src/test/java/org/zowe/apiml/client/api/PetControllerGetOneTest.java +++ b/discoverable-client/src/test/java/org/zowe/apiml/client/api/PetControllerGetOneTest.java @@ -14,8 +14,8 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; -import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Import; +import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.web.servlet.MockMvc; import org.zowe.apiml.client.configuration.ApplicationConfiguration; @@ -38,7 +38,7 @@ class PetControllerGetOneTest { @Autowired private MockMvc mockMvc; - @MockBean + @MockitoBean private PetService petService; @Test diff --git a/discoverable-client/src/test/java/org/zowe/apiml/client/api/PetControllerPostPetTest.java b/discoverable-client/src/test/java/org/zowe/apiml/client/api/PetControllerPostPetTest.java index 515a8524cc..3496165585 100644 --- a/discoverable-client/src/test/java/org/zowe/apiml/client/api/PetControllerPostPetTest.java +++ b/discoverable-client/src/test/java/org/zowe/apiml/client/api/PetControllerPostPetTest.java @@ -10,27 +10,24 @@ package org.zowe.apiml.client.api; -import org.zowe.apiml.client.configuration.ApplicationConfiguration; -import org.zowe.apiml.client.configuration.SecurityConfiguration; -import org.zowe.apiml.client.configuration.SpringComponentsConfiguration; -import org.zowe.apiml.client.model.Pet; -import org.zowe.apiml.client.service.PetService; import com.fasterxml.jackson.databind.ObjectMapper; -import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; -import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Import; import org.springframework.http.MediaType; +import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.web.servlet.MockMvc; +import org.zowe.apiml.client.configuration.ApplicationConfiguration; +import org.zowe.apiml.client.configuration.SecurityConfiguration; +import org.zowe.apiml.client.configuration.SpringComponentsConfiguration; +import org.zowe.apiml.client.model.Pet; +import org.zowe.apiml.client.service.PetService; import static org.hamcrest.core.Is.is; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; +import static org.mockito.Mockito.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -42,7 +39,7 @@ class PetControllerPostPetTest { @Autowired private MockMvc mockMvc; - @MockBean + @MockitoBean private PetService petService; private final ObjectMapper mapper = new ObjectMapper(); diff --git a/discoverable-client/src/test/java/org/zowe/apiml/client/api/PetControllerPutTest.java b/discoverable-client/src/test/java/org/zowe/apiml/client/api/PetControllerPutTest.java index 06b3da3098..bfad386364 100644 --- a/discoverable-client/src/test/java/org/zowe/apiml/client/api/PetControllerPutTest.java +++ b/discoverable-client/src/test/java/org/zowe/apiml/client/api/PetControllerPutTest.java @@ -10,30 +10,26 @@ package org.zowe.apiml.client.api; -import org.zowe.apiml.client.configuration.ApplicationConfiguration; -import org.zowe.apiml.client.configuration.SecurityConfiguration; -import org.zowe.apiml.client.configuration.SpringComponentsConfiguration; -import org.zowe.apiml.client.model.Pet; -import org.zowe.apiml.client.service.PetService; import com.fasterxml.jackson.databind.ObjectMapper; import org.json.JSONObject; -import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; -import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Import; import org.springframework.http.MediaType; +import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.web.servlet.MockMvc; +import org.zowe.apiml.client.configuration.ApplicationConfiguration; +import org.zowe.apiml.client.configuration.SecurityConfiguration; +import org.zowe.apiml.client.configuration.SpringComponentsConfiguration; +import org.zowe.apiml.client.model.Pet; +import org.zowe.apiml.client.service.PetService; import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.core.Is.is; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; +import static org.mockito.Mockito.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -45,7 +41,7 @@ class PetControllerPutTest { @Autowired private MockMvc mockMvc; - @MockBean + @MockitoBean private PetService petService; private final ObjectMapper mapper = new ObjectMapper(); diff --git a/discoverable-client/src/test/java/org/zowe/apiml/client/api/ZaasClientTestControllerTest.java b/discoverable-client/src/test/java/org/zowe/apiml/client/api/ZaasClientTestControllerTest.java index 0459e955de..94aa917d0f 100644 --- a/discoverable-client/src/test/java/org/zowe/apiml/client/api/ZaasClientTestControllerTest.java +++ b/discoverable-client/src/test/java/org/zowe/apiml/client/api/ZaasClientTestControllerTest.java @@ -11,12 +11,13 @@ package org.zowe.apiml.client.api; import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.servlet.http.Cookie; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; -import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Import; import org.springframework.http.MediaType; +import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.test.context.support.AnnotationConfigContextLoader; import org.springframework.test.web.servlet.MockMvc; import org.zowe.apiml.client.configuration.ApplicationConfiguration; @@ -26,8 +27,6 @@ import org.zowe.apiml.zaasclient.exception.ZaasClientException; import org.zowe.apiml.zaasclient.service.ZaasClient; -import jakarta.servlet.http.Cookie; - import static org.mockito.Mockito.when; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; @@ -41,7 +40,7 @@ class ZaasClientTestControllerTest { private final ObjectMapper mapper = new ObjectMapper(); - @MockBean + @MockitoBean private ZaasClient zaasClient; private static final String TOKEN_PREFIX = "apimlAuthenticationToken"; diff --git a/gateway-service/src/main/java/org/zowe/apiml/gateway/config/AuthEndpointConfig.java b/gateway-service/src/main/java/org/zowe/apiml/gateway/config/AuthEndpointConfig.java index 0a00696007..d27530195c 100644 --- a/gateway-service/src/main/java/org/zowe/apiml/gateway/config/AuthEndpointConfig.java +++ b/gateway-service/src/main/java/org/zowe/apiml/gateway/config/AuthEndpointConfig.java @@ -77,9 +77,7 @@ private WebClient createLoadBalanced(WebClient webClient, ReactiveLoadBalancer.F private WebClient.RequestBodySpec getWebclient(ServerRequest serverRequest, String path) { var sslInfo = serverRequest.exchange().getRequest().getSslInfo(); - //TODO: remove after checking integration tests var client = sslInfo == null ? this.webClient : this.webClientClientCert; - //var client = this.webClientClientCert; var request = client .method(serverRequest.method()) diff --git a/gateway-service/src/main/java/org/zowe/apiml/gateway/config/ConnectionsConfig.java b/gateway-service/src/main/java/org/zowe/apiml/gateway/config/ConnectionsConfig.java index 9bd6978b7f..2f4795057f 100644 --- a/gateway-service/src/main/java/org/zowe/apiml/gateway/config/ConnectionsConfig.java +++ b/gateway-service/src/main/java/org/zowe/apiml/gateway/config/ConnectionsConfig.java @@ -36,27 +36,27 @@ import org.springframework.cloud.circuitbreaker.resilience4j.Resilience4JConfigBuilder; import org.springframework.cloud.client.circuitbreaker.Customizer; import org.springframework.cloud.context.config.annotation.RefreshScope; -import org.springframework.cloud.gateway.config.*; +import org.springframework.cloud.gateway.config.HttpClientCustomizer; +import org.springframework.cloud.gateway.config.HttpClientFactory; +import org.springframework.cloud.gateway.config.HttpClientProperties; +import org.springframework.cloud.gateway.config.HttpClientSslConfigurer; import org.springframework.cloud.gateway.filter.headers.HttpHeadersFilter; -import org.springframework.cloud.gateway.handler.RoutePredicateHandlerMapping; import org.springframework.cloud.netflix.eureka.CloudEurekaClient; import org.springframework.cloud.netflix.eureka.EurekaClientConfigBean; -import org.springframework.cloud.netflix.eureka.RestTemplateTimeoutProperties; +import org.springframework.cloud.netflix.eureka.RestClientTimeoutProperties; import org.springframework.cloud.netflix.eureka.http.DefaultEurekaClientHttpRequestFactorySupplier; -import org.springframework.cloud.netflix.eureka.http.RestTemplateDiscoveryClientOptionalArgs; -import org.springframework.cloud.netflix.eureka.http.RestTemplateTransportClientFactories; +import org.springframework.cloud.netflix.eureka.http.RestClientDiscoveryClientOptionalArgs; +import org.springframework.cloud.netflix.eureka.http.RestClientTransportClientFactories; import org.springframework.cloud.util.ProxyUtils; import org.springframework.context.ApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Conditional; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Primary; +import org.springframework.context.annotation.*; import org.springframework.http.client.reactive.ReactorClientHttpConnector; import org.springframework.util.CollectionUtils; -import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource; +import org.springframework.web.client.RestClient; +import org.springframework.web.cors.reactive.CorsWebFilter; import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.server.WebFilter; import org.springframework.web.util.UriComponentsBuilder; -import org.springframework.web.util.pattern.PathPatternParser; import org.zowe.apiml.config.AdditionalRegistration; import org.zowe.apiml.config.AdditionalRegistrationCondition; import org.zowe.apiml.config.AdditionalRegistrationParser; @@ -270,23 +270,23 @@ public CloudEurekaClient primaryEurekaClient(ApplicationInfoManager manager, Eur } else { appManager = manager; } - RestTemplateDiscoveryClientOptionalArgs args1 = defaultArgs(getDefaultEurekaClientHttpRequestFactorySupplier()); - RestTemplateTransportClientFactories factories = new RestTemplateTransportClientFactories(args1); + RestClientDiscoveryClientOptionalArgs args1 = defaultArgs(getDefaultEurekaClientHttpRequestFactorySupplier()); + RestClientTransportClientFactories factories = new RestClientTransportClientFactories(args1); final CloudEurekaClient cloudEurekaClient = new CloudEurekaClient(appManager, config, factories, args1, this.context); cloudEurekaClient.registerHealthCheck(healthCheckHandler); return cloudEurekaClient; } private static DefaultEurekaClientHttpRequestFactorySupplier getDefaultEurekaClientHttpRequestFactorySupplier() { - RestTemplateTimeoutProperties properties = new RestTemplateTimeoutProperties(); + RestClientTimeoutProperties properties = new RestClientTimeoutProperties(); properties.setConnectTimeout(180000); properties.setConnectRequestTimeout(180000); properties.setSocketTimeout(180000); return new DefaultEurekaClientHttpRequestFactorySupplier(properties); } - public RestTemplateDiscoveryClientOptionalArgs defaultArgs(DefaultEurekaClientHttpRequestFactorySupplier factorySupplier) { - RestTemplateDiscoveryClientOptionalArgs clientArgs = new RestTemplateDiscoveryClientOptionalArgs(factorySupplier); + public RestClientDiscoveryClientOptionalArgs defaultArgs(DefaultEurekaClientHttpRequestFactorySupplier factorySupplier) { + RestClientDiscoveryClientOptionalArgs clientArgs = new RestClientDiscoveryClientOptionalArgs(factorySupplier, RestClient::builder); if (eurekaServerUrl.startsWith("http://")) { apimlLog.log("org.zowe.apiml.common.insecureHttpWarning"); @@ -299,6 +299,7 @@ public RestTemplateDiscoveryClientOptionalArgs defaultArgs(DefaultEurekaClientHt } @Bean + @DependsOn("discoveryClient") public List additionalRegistration() { List additionalRegistrations = new AdditionalRegistrationParser().extractAdditionalRegistrations(System.getenv()); log.debug("Parsed {} additional registration: {}", additionalRegistrations.size(), additionalRegistrations); @@ -337,8 +338,8 @@ private CloudEurekaClient registerInTheApimlInstance(EurekaClientConfig config, updateMetadata(newInfo, apimlRegistration); - RestTemplateDiscoveryClientOptionalArgs args1 = defaultArgs(getDefaultEurekaClientHttpRequestFactorySupplier()); - RestTemplateTransportClientFactories factories = new RestTemplateTransportClientFactories(args1); + RestClientDiscoveryClientOptionalArgs args1 = defaultArgs(getDefaultEurekaClientHttpRequestFactorySupplier()); + RestClientTransportClientFactories factories = new RestClientTransportClientFactories(args1); return eurekaFactory.createCloudEurekaClient(new AdditionalEurekaConfiguration(eurekaInstanceConfig, newInfo), newInfo, configBean, context, factories, args1); } @@ -408,17 +409,13 @@ public WebClient webClientClientCert(HttpClient httpClient) { } @Bean - public UrlBasedCorsConfigurationSource corsConfigurationSource(RoutePredicateHandlerMapping handlerMapping, GlobalCorsProperties globalCorsProperties, CorsUtils corsUtils) { - UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser()); - source.setCorsConfigurations(globalCorsProperties.getCorsConfigurations()); - corsUtils.registerDefaultCorsConfiguration(source::registerCorsConfiguration); - handlerMapping.setCorsConfigurationSource(source); - return source; + public CorsUtils corsUtils() { + return new CorsUtils(corsEnabled, null); } @Bean - public CorsUtils corsUtils() { - return new CorsUtils(corsEnabled, null); + public WebFilter corsWebFilter(ServiceCorsUpdater serviceCorsUpdater) { + return new CorsWebFilter(serviceCorsUpdater.getUrlBasedCorsConfigurationSource()); } public InstanceInfo create(EurekaInstanceConfig config) { diff --git a/gateway-service/src/main/java/org/zowe/apiml/gateway/config/DiscoveryClientOrderProcessorBean.java b/gateway-service/src/main/java/org/zowe/apiml/gateway/config/DiscoveryClientOrderProcessorBean.java new file mode 100644 index 0000000000..df09e145d8 --- /dev/null +++ b/gateway-service/src/main/java/org/zowe/apiml/gateway/config/DiscoveryClientOrderProcessorBean.java @@ -0,0 +1,37 @@ +/* + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright Contributors to the Zowe Project. + */ + +package org.zowe.apiml.gateway.config; + +import com.netflix.discovery.EurekaClient; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.cloud.client.discovery.DiscoveryClient; +import org.springframework.cloud.netflix.eureka.CloudEurekaClient; +import org.springframework.stereotype.Component; + +import java.util.Arrays; +import java.util.stream.Stream; + +@Component +public class DiscoveryClientOrderProcessorBean implements BeanFactoryPostProcessor { + + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { + Stream.of(DiscoveryClient.class, EurekaClient.class, CloudEurekaClient.class) + .map(beanFactory::getBeanNamesForType) + .flatMap(Arrays::stream) + .distinct() + .map(beanFactory::getBeanDefinition) + .forEach(bd -> bd.setDependsOn("gatewayLoadBalancerClientFilter", "eurekaAutoServiceRegistration")); + } + +} diff --git a/gateway-service/src/main/java/org/zowe/apiml/gateway/config/EurekaFactory.java b/gateway-service/src/main/java/org/zowe/apiml/gateway/config/EurekaFactory.java index bc90787fcf..6edb4fd00a 100644 --- a/gateway-service/src/main/java/org/zowe/apiml/gateway/config/EurekaFactory.java +++ b/gateway-service/src/main/java/org/zowe/apiml/gateway/config/EurekaFactory.java @@ -16,8 +16,8 @@ import org.springframework.cloud.netflix.eureka.CloudEurekaClient; import org.springframework.cloud.netflix.eureka.EurekaClientConfigBean; import org.springframework.cloud.netflix.eureka.InstanceInfoFactory; -import org.springframework.cloud.netflix.eureka.http.RestTemplateDiscoveryClientOptionalArgs; -import org.springframework.cloud.netflix.eureka.http.RestTemplateTransportClientFactories; +import org.springframework.cloud.netflix.eureka.http.RestClientDiscoveryClientOptionalArgs; +import org.springframework.cloud.netflix.eureka.http.RestClientTransportClientFactories; import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Component; @@ -36,7 +36,7 @@ InstanceInfo createInstanceInfo(EurekaInstanceConfig instanceConfig) { return new InstanceInfoFactory().create(instanceConfig); } - public CloudEurekaClient createCloudEurekaClient(EurekaInstanceConfig eurekaInstanceConfig, InstanceInfo newInfo, EurekaClientConfigBean configBean, ApplicationContext context, RestTemplateTransportClientFactories factories, RestTemplateDiscoveryClientOptionalArgs args1) { + public CloudEurekaClient createCloudEurekaClient(EurekaInstanceConfig eurekaInstanceConfig, InstanceInfo newInfo, EurekaClientConfigBean configBean, ApplicationContext context, RestClientTransportClientFactories factories, RestClientDiscoveryClientOptionalArgs args1) { ApplicationInfoManager perClientAppManager = new ApplicationInfoManager(eurekaInstanceConfig, newInfo, null); return new CloudEurekaClient(perClientAppManager, configBean, factories, args1, context); } diff --git a/gateway-service/src/main/java/org/zowe/apiml/gateway/config/ServiceCorsUpdater.java b/gateway-service/src/main/java/org/zowe/apiml/gateway/config/ServiceCorsUpdater.java new file mode 100644 index 0000000000..b452aa7943 --- /dev/null +++ b/gateway-service/src/main/java/org/zowe/apiml/gateway/config/ServiceCorsUpdater.java @@ -0,0 +1,67 @@ +/* + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright Contributors to the Zowe Project. + */ + +package org.zowe.apiml.gateway.config; + +import jakarta.annotation.PostConstruct; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.springframework.cloud.client.discovery.ReactiveDiscoveryClient; +import org.springframework.cloud.gateway.config.GlobalCorsProperties; +import org.springframework.cloud.gateway.event.RefreshRoutesEvent; +import org.springframework.cloud.gateway.handler.RoutePredicateHandlerMapping; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Component; +import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource; +import org.springframework.web.util.pattern.PathPatternParser; +import org.zowe.apiml.util.CorsUtils; +import reactor.core.publisher.Flux; + +import static org.zowe.apiml.constants.EurekaMetadataDefinition.APIML_ID; + +@Component +@RequiredArgsConstructor +public class ServiceCorsUpdater { + + private final CorsUtils corsUtils; + private final ReactiveDiscoveryClient discoveryClient; + private final RoutePredicateHandlerMapping handlerMapping; + private final GlobalCorsProperties globalCorsProperties; + + @Getter + private UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource; + + @PostConstruct + void initCorsConfigurationSource() { + urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource(new PathPatternParser()); + urlBasedCorsConfigurationSource.setCorsConfigurations(globalCorsProperties.getCorsConfigurations()); + corsUtils.registerDefaultCorsConfiguration(urlBasedCorsConfigurationSource::registerCorsConfiguration); + handlerMapping.setCorsConfigurationSource(urlBasedCorsConfigurationSource); + } + + @EventListener(RefreshRoutesEvent.class) + public void onRefreshRoutesEvent(RefreshRoutesEvent event) { + discoveryClient.getServices() + .flatMap(service -> discoveryClient.getInstances(service).collectList()) + .flatMap(Flux::fromIterable) + .toIterable() + .forEach(serviceInstance -> + corsUtils.setCorsConfiguration( + serviceInstance.getServiceId().toLowerCase(), + serviceInstance.getMetadata(), + (prefix, serviceId, config) -> { + serviceId = serviceInstance.getMetadata().getOrDefault(APIML_ID, serviceInstance.getServiceId().toLowerCase()); + urlBasedCorsConfigurationSource.registerCorsConfiguration("/" + serviceId + "/**", config); + } + ) + ); + } + +} diff --git a/gateway-service/src/main/java/org/zowe/apiml/gateway/service/RouteLocator.java b/gateway-service/src/main/java/org/zowe/apiml/gateway/service/RouteLocator.java index 102083dc49..bdaab76932 100644 --- a/gateway-service/src/main/java/org/zowe/apiml/gateway/service/RouteLocator.java +++ b/gateway-service/src/main/java/org/zowe/apiml/gateway/service/RouteLocator.java @@ -10,25 +10,23 @@ package org.zowe.apiml.gateway.service; -import lombok.AccessLevel; -import lombok.Getter; +import jakarta.annotation.PostConstruct; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.ReactiveDiscoveryClient; import org.springframework.cloud.gateway.filter.FilterDefinition; import org.springframework.cloud.gateway.route.RouteDefinition; import org.springframework.cloud.gateway.route.RouteDefinitionLocator; -import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Service; import org.springframework.util.PatternMatchUtils; -import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource; import org.zowe.apiml.auth.Authentication; import org.zowe.apiml.auth.AuthenticationScheme; import org.zowe.apiml.eurekaservice.client.util.EurekaMetadataParser; import org.zowe.apiml.gateway.service.routing.RouteDefinitionProducer; import org.zowe.apiml.gateway.service.scheme.SchemeHandler; import org.zowe.apiml.product.routing.RoutedService; -import org.zowe.apiml.util.CorsUtils; import org.zowe.apiml.util.StringUtils; import reactor.core.publisher.Flux; @@ -39,6 +37,8 @@ import static org.zowe.apiml.constants.EurekaMetadataDefinition.*; @Service +@Slf4j +@RequiredArgsConstructor public class RouteLocator implements RouteDefinitionLocator { private static final EurekaMetadataParser metadataParser = new EurekaMetadataParser(); @@ -52,32 +52,15 @@ public class RouteLocator implements RouteDefinitionLocator { @Value("${apiml.gateway.servicesToLimitRequestRate:-}") List servicesToLimitRequestRate; - private final ApplicationContext context; - - private final CorsUtils corsUtils; private final ReactiveDiscoveryClient discoveryClient; private final List commonFilters; private final List routeDefinitionProducers; + private final List schemeHandlersList; private final Map schemeHandlers = new EnumMap<>(AuthenticationScheme.class); - @Getter(lazy = true, value = AccessLevel.PRIVATE) - private final UrlBasedCorsConfigurationSource corsConfigurationSource = context.getBean(UrlBasedCorsConfigurationSource.class); - - public RouteLocator( - ApplicationContext context, - CorsUtils corsUtils, - ReactiveDiscoveryClient discoveryClient, - List commonFilters, - List schemeHandlersList, - List routeDefinitionProducers - ) { - this.context = context; - this.corsUtils = corsUtils; - this.discoveryClient = discoveryClient; - this.commonFilters = commonFilters; - this.routeDefinitionProducers = routeDefinitionProducers; - + @PostConstruct + void afterPropertiesSet() { for (SchemeHandler schemeHandler : schemeHandlersList) { schemeHandlers.put(schemeHandler.getAuthenticationScheme(), schemeHandler); } @@ -99,15 +82,7 @@ void setAuth(ServiceInstance serviceInstance, RouteDefinition routeDefinition, A } } - void setCors(ServiceInstance serviceInstance) { - corsUtils.setCorsConfiguration( - serviceInstance.getServiceId().toLowerCase(), - serviceInstance.getMetadata(), - (prefix, serviceId, config) -> { - serviceId = serviceInstance.getMetadata().getOrDefault(APIML_ID, serviceInstance.getServiceId().toLowerCase()); - getCorsConfigurationSource().registerCorsConfiguration("/" + serviceId + "/**", config); - }); - } + Stream getRoutedService(ServiceInstance serviceInstance) { return metadataParser.parseToListRoute(serviceInstance.getMetadata()).stream() @@ -205,17 +180,15 @@ public Flux getRouteDefinitions() { // counter of generated route definition to prevent clashing by the order AtomicInteger order = new AtomicInteger(); // iterate over services - return getServiceInstances().flatMap(Flux::fromIterable).map(serviceInstance -> { - // configure CORS for the service (if necessary) - setCors(serviceInstance); - + return getServiceInstances().flatMap(Flux::fromIterable).map(serviceInstance -> // generate route definition per services and its routing rules - return getAuthFilterPerRoute(order, serviceInstance, getPostRoutingFilters(serviceInstance)); - }) + getAuthFilterPerRoute(order, serviceInstance, getPostRoutingFilters(serviceInstance)) + ) .flatMapIterable(list -> list); } private boolean filterIgnored(String serviceId) { return !PatternMatchUtils.simpleMatch(ignoredServices, serviceId); } + } diff --git a/gateway-service/src/main/resources/application.yml b/gateway-service/src/main/resources/application.yml index 0a291eda25..d11a930888 100644 --- a/gateway-service/src/main/resources/application.yml +++ b/gateway-service/src/main/resources/application.yml @@ -70,6 +70,7 @@ spring: okta: redirectUri: "{baseUrl}/gateway/{action}/oauth2/code/{registrationId}" main: + allow-circular-references: true banner-mode: ${apiml.banner:"off"} web-application-type: reactive @@ -127,10 +128,6 @@ server: trustStore: keystore/localhost/localhost.truststore.p12 trustStorePassword: password trustStoreType: PKCS12 - application: - name: gateway - main: - allow-circular-references: true logbackServiceName: ZWEAGW1 diff --git a/gateway-service/src/test/java/org/zowe/apiml/gateway/acceptance/config/DiscoveryClientTestConfig.java b/gateway-service/src/test/java/org/zowe/apiml/gateway/acceptance/config/DiscoveryClientTestConfig.java index dc21ab9715..4b5ae012b0 100644 --- a/gateway-service/src/test/java/org/zowe/apiml/gateway/acceptance/config/DiscoveryClientTestConfig.java +++ b/gateway-service/src/test/java/org/zowe/apiml/gateway/acceptance/config/DiscoveryClientTestConfig.java @@ -21,14 +21,15 @@ import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.ReactiveDiscoveryClient; import org.springframework.cloud.context.config.annotation.RefreshScope; -import org.springframework.cloud.netflix.eureka.RestTemplateTimeoutProperties; +import org.springframework.cloud.netflix.eureka.RestClientTimeoutProperties; import org.springframework.cloud.netflix.eureka.http.DefaultEurekaClientHttpRequestFactorySupplier; -import org.springframework.cloud.netflix.eureka.http.RestTemplateDiscoveryClientOptionalArgs; -import org.springframework.cloud.netflix.eureka.http.RestTemplateTransportClientFactories; +import org.springframework.cloud.netflix.eureka.http.RestClientDiscoveryClientOptionalArgs; +import org.springframework.cloud.netflix.eureka.http.RestClientTransportClientFactories; import org.springframework.cloud.util.ProxyUtils; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Primary; +import org.springframework.web.client.RestClient; import org.zowe.apiml.gateway.acceptance.netflix.ApimlDiscoveryClientStub; import org.zowe.apiml.gateway.acceptance.netflix.ApplicationRegistry; import reactor.core.publisher.Flux; @@ -91,9 +92,9 @@ public ApimlDiscoveryClientStub eurekaClient(ApplicationInfoManager manager, } - var factorySupplier = new DefaultEurekaClientHttpRequestFactorySupplier(new RestTemplateTimeoutProperties()); - var args1 = new RestTemplateDiscoveryClientOptionalArgs(factorySupplier); - var factories = new RestTemplateTransportClientFactories(args1); + var factorySupplier = new DefaultEurekaClientHttpRequestFactorySupplier(new RestClientTimeoutProperties()); + var args1 = new RestClientDiscoveryClientOptionalArgs(factorySupplier, RestClient::builder); + var factories = new RestClientTransportClientFactories(args1); final var discoveryClient = new ApimlDiscoveryClientStub(appManager, config, this.context, applicationRegistry, factories, args1); discoveryClient.registerHealthCheck(healthCheckHandler); diff --git a/gateway-service/src/test/java/org/zowe/apiml/gateway/acceptance/netflix/ApimlDiscoveryClientStub.java b/gateway-service/src/test/java/org/zowe/apiml/gateway/acceptance/netflix/ApimlDiscoveryClientStub.java index e36b39bf83..2512c96cb4 100644 --- a/gateway-service/src/test/java/org/zowe/apiml/gateway/acceptance/netflix/ApimlDiscoveryClientStub.java +++ b/gateway-service/src/test/java/org/zowe/apiml/gateway/acceptance/netflix/ApimlDiscoveryClientStub.java @@ -16,8 +16,8 @@ import com.netflix.discovery.shared.Applications; import org.apache.commons.lang3.StringUtils; import org.springframework.cloud.netflix.eureka.CloudEurekaClient; -import org.springframework.cloud.netflix.eureka.http.RestTemplateDiscoveryClientOptionalArgs; -import org.springframework.cloud.netflix.eureka.http.RestTemplateTransportClientFactories; +import org.springframework.cloud.netflix.eureka.http.RestClientDiscoveryClientOptionalArgs; +import org.springframework.cloud.netflix.eureka.http.RestClientTransportClientFactories; import org.springframework.context.ApplicationEventPublisher; import java.util.List; @@ -25,7 +25,7 @@ public class ApimlDiscoveryClientStub extends CloudEurekaClient { private ApplicationRegistry applicationRegistry; - public ApimlDiscoveryClientStub(ApplicationInfoManager applicationInfoManager, EurekaClientConfig config, ApplicationEventPublisher publisher, ApplicationRegistry applicationRegistry, RestTemplateTransportClientFactories factories, RestTemplateDiscoveryClientOptionalArgs args1) { + public ApimlDiscoveryClientStub(ApplicationInfoManager applicationInfoManager, EurekaClientConfig config, ApplicationEventPublisher publisher, ApplicationRegistry applicationRegistry, RestClientTransportClientFactories factories, RestClientDiscoveryClientOptionalArgs args1) { super(applicationInfoManager, config, factories, args1, publisher); diff --git a/gateway-service/src/test/java/org/zowe/apiml/gateway/config/ServiceCorsUpdaterTest.java b/gateway-service/src/test/java/org/zowe/apiml/gateway/config/ServiceCorsUpdaterTest.java new file mode 100644 index 0000000000..6732cd3bd9 --- /dev/null +++ b/gateway-service/src/test/java/org/zowe/apiml/gateway/config/ServiceCorsUpdaterTest.java @@ -0,0 +1,104 @@ +/* + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright Contributors to the Zowe Project. + */ + +package org.zowe.apiml.gateway.config; + +import org.apache.logging.log4j.util.TriConsumer; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.cloud.client.discovery.ReactiveDiscoveryClient; +import org.springframework.cloud.gateway.config.GlobalCorsProperties; +import org.springframework.cloud.gateway.event.RefreshRoutesEvent; +import org.springframework.cloud.gateway.handler.RoutePredicateHandlerMapping; +import org.springframework.test.util.ReflectionTestUtils; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource; +import org.zowe.apiml.constants.EurekaMetadataDefinition; +import org.zowe.apiml.util.CorsUtils; +import reactor.core.publisher.Flux; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Consumer; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.*; + +class ServiceCorsUpdaterTest { + + private static final String SERVICE_ID = "myserviceid"; + private static final String APIML_ID = "apimlid"; + + private CorsUtils corsUtils = spy(new CorsUtils(true, Collections.emptyList())); + private ReactiveDiscoveryClient discoveryClient = mock(ReactiveDiscoveryClient.class); + + private ServiceCorsUpdater serviceCorsUpdater; + + private UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource; + + @BeforeEach + void setUp() { + serviceCorsUpdater = new ServiceCorsUpdater(corsUtils, discoveryClient, mock(RoutePredicateHandlerMapping.class), mock(GlobalCorsProperties.class)); + serviceCorsUpdater.initCorsConfigurationSource(); + urlBasedCorsConfigurationSource = spy((UrlBasedCorsConfigurationSource) ReflectionTestUtils.getField(serviceCorsUpdater, "urlBasedCorsConfigurationSource")); + ReflectionTestUtils.setField(serviceCorsUpdater, "urlBasedCorsConfigurationSource", urlBasedCorsConfigurationSource); + } + + private ServiceInstance createServiceInstance(String serviceId) { + ServiceInstance serviceInstance = mock(ServiceInstance.class); + doReturn(serviceId).when(serviceInstance).getServiceId(); + doReturn(Flux.just(serviceInstance)).when(discoveryClient).getInstances(serviceId); + + Map metadata = new HashMap<>(); + + metadata.put("apiml.routes.api-v1.gatewayUrl", "api/v1"); + metadata.put("apiml.routes.api-v1.serviceUrl", "api/v1"); + + doReturn(metadata).when(serviceInstance).getMetadata(); + + return serviceInstance; + } + + private TriConsumer getCorsLambda(Consumer> metadataProcessor) { + ServiceInstance serviceInstance = createServiceInstance(SERVICE_ID); + metadataProcessor.accept(serviceInstance.getMetadata()); + + doReturn(Flux.just(SERVICE_ID)).when(discoveryClient).getServices(); + + serviceCorsUpdater.onRefreshRoutesEvent(new RefreshRoutesEvent(this)); + ArgumentCaptor> lambdaCaptor = ArgumentCaptor.forClass(TriConsumer.class); + verify(corsUtils).setCorsConfiguration(anyString(), any(), lambdaCaptor.capture()); + + return lambdaCaptor.getValue(); + } + + @Test + void givenApimlId_whenSetCors_thenServiceIdIsReplacedWithApimlId() { + TriConsumer corsLambda = getCorsLambda(md -> md.put(EurekaMetadataDefinition.APIML_ID, APIML_ID)); + + corsLambda.accept(null, SERVICE_ID, null); + + verify(serviceCorsUpdater.getUrlBasedCorsConfigurationSource()).registerCorsConfiguration("/" + APIML_ID + "/**", null); + } + + @Test + void givenNoApimlId_whenSetCors_thenServiceIdIsUsed() { + TriConsumer corsLambda = getCorsLambda(md -> {}); + + corsLambda.accept(null, SERVICE_ID, null); + + verify(serviceCorsUpdater.getUrlBasedCorsConfigurationSource()).registerCorsConfiguration("/" + SERVICE_ID + "/**", null); + } + +} diff --git a/gateway-service/src/test/java/org/zowe/apiml/gateway/controllers/CertificatesRestControllerTest.java b/gateway-service/src/test/java/org/zowe/apiml/gateway/controllers/CertificatesRestControllerTest.java index ba1172a7e9..e8379144a4 100644 --- a/gateway-service/src/test/java/org/zowe/apiml/gateway/controllers/CertificatesRestControllerTest.java +++ b/gateway-service/src/test/java/org/zowe/apiml/gateway/controllers/CertificatesRestControllerTest.java @@ -16,7 +16,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration; import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest; -import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.test.web.reactive.server.WebTestClient; import org.zowe.apiml.gateway.service.CertificateChainService; import org.zowe.apiml.message.core.MessageService; @@ -83,10 +83,10 @@ class CertificatesRestControllerTest { @Autowired WebTestClient webTestClient; - @MockBean + @MockitoBean private CertificateChainService mockCertificateChainService; - @MockBean + @MockitoBean private MessageService messageService; @Nested diff --git a/gateway-service/src/test/java/org/zowe/apiml/gateway/controllers/VersionControllerTest.java b/gateway-service/src/test/java/org/zowe/apiml/gateway/controllers/VersionControllerTest.java index f841d3868f..648e2fccff 100644 --- a/gateway-service/src/test/java/org/zowe/apiml/gateway/controllers/VersionControllerTest.java +++ b/gateway-service/src/test/java/org/zowe/apiml/gateway/controllers/VersionControllerTest.java @@ -14,7 +14,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration; import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest; -import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.test.web.reactive.server.WebTestClient; import org.zowe.apiml.message.core.MessageService; import org.zowe.apiml.product.version.VersionInfo; @@ -24,12 +24,14 @@ import static org.mockito.Mockito.when; @WebFluxTest(controllers = VersionController.class, excludeAutoConfiguration = { ReactiveSecurityAutoConfiguration.class }) -@MockBean(MessageService.class) class VersionControllerTest { - @MockBean + @MockitoBean private VersionService versionService; + @MockitoBean + private MessageService messageService; + @Autowired private WebTestClient webTestClient; diff --git a/gateway-service/src/test/java/org/zowe/apiml/gateway/service/RouteLocatorTest.java b/gateway-service/src/test/java/org/zowe/apiml/gateway/service/RouteLocatorTest.java index e4b45ef9aa..5a313cb703 100644 --- a/gateway-service/src/test/java/org/zowe/apiml/gateway/service/RouteLocatorTest.java +++ b/gateway-service/src/test/java/org/zowe/apiml/gateway/service/RouteLocatorTest.java @@ -10,29 +10,22 @@ package org.zowe.apiml.gateway.service; -import org.apache.logging.log4j.util.TriConsumer; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -import org.mockito.ArgumentCaptor; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.ReactiveDiscoveryClient; import org.springframework.cloud.gateway.filter.FilterDefinition; import org.springframework.cloud.gateway.route.RouteDefinition; -import org.springframework.context.ApplicationContext; import org.springframework.test.util.ReflectionTestUtils; -import org.springframework.web.cors.CorsConfiguration; -import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource; import org.zowe.apiml.auth.Authentication; import org.zowe.apiml.auth.AuthenticationScheme; import org.zowe.apiml.gateway.service.routing.RouteDefinitionProducer; import org.zowe.apiml.gateway.service.scheme.SchemeHandler; import org.zowe.apiml.product.routing.RoutedService; -import org.zowe.apiml.util.CorsUtils; import reactor.core.publisher.Flux; import java.util.*; -import java.util.function.Consumer; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; @@ -54,25 +47,19 @@ class RouteLocatorTest { createRouteDefinitionProducer(10, "id10") }; - private UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = mock(UrlBasedCorsConfigurationSource.class); - private CorsUtils corsUtils = mock(CorsUtils.class); private ReactiveDiscoveryClient discoveryClient = mock(ReactiveDiscoveryClient.class); private RouteLocator routeLocator; @BeforeEach void init() { - ApplicationContext context = mock(ApplicationContext.class); - doReturn(urlBasedCorsConfigurationSource).when(context).getBean(UrlBasedCorsConfigurationSource.class); - routeLocator = spy(new RouteLocator( - context, - corsUtils, discoveryClient, Arrays.asList(COMMON_FILTERS), - Arrays.asList(SCHEME_HANDLER_FILTERS), - Arrays.asList(PRODUCERS) + Arrays.asList(PRODUCERS), + Arrays.asList(SCHEME_HANDLER_FILTERS) )); + routeLocator.afterPropertiesSet(); } private ServiceInstance createServiceInstance(String serviceId, String...routes) { @@ -172,35 +159,6 @@ void givenExistingAuthenticationScheme_whenSetAuth_thenCallApply() { verify(SCHEME_HANDLER_FILTERS[0]).apply(MOCK_SERVICE, routeDefinition, authentication); } - private TriConsumer getCorsLambda(Consumer> metadataProcessor) { - ServiceInstance serviceInstance = createServiceInstance("myservice", "api/v1"); - metadataProcessor.accept(serviceInstance.getMetadata()); - - routeLocator.setCors(serviceInstance); - ArgumentCaptor> lambdaCaptor = ArgumentCaptor.forClass(TriConsumer.class); - verify(corsUtils).setCorsConfiguration(anyString(), any(), lambdaCaptor.capture()); - - return lambdaCaptor.getValue(); - } - - @Test - void givenApimlId_whenSetCors_thenServiceIdIsReplacedWithApimlId() { - TriConsumer corsLambda = getCorsLambda(md -> md.put(APIML_ID, "apimlid")); - - corsLambda.accept(null, "myservice", null); - - verify(urlBasedCorsConfigurationSource).registerCorsConfiguration("/apimlid/**", null); - } - - @Test - void givenNoApimlId_whenSetCors_thenServiceIdIsUsed() { - TriConsumer corsLambda = getCorsLambda(md -> {}); - - corsLambda.accept(null, "myservice", null); - - verify(urlBasedCorsConfigurationSource).registerCorsConfiguration("/myservice/**", null); - } - @Test void givenNonGatewayService_whenGetRoutedService_thenReturnRoutingFromMetadata() { ServiceInstance s = createServiceInstance("myservice", "api/v1", "ui/v1"); @@ -245,8 +203,6 @@ void givenRouteLocator_whenGetRouteDefinitions_thenGenerateAll() { int index = 0; for (String serviceId : new String[] {"service1", "service2"}) { - verify(corsUtils).setCorsConfiguration(eq(serviceId), any(), any()); - for (String gatewayUrl : new String[] {"a/b", ""}) { for (String producerId : new String[] {"id0", "id5", "id10"}) { assertEquals(index, rds[index].getOrder()); @@ -263,7 +219,7 @@ void givenRouteLocator_whenGetRouteDefinitions_thenGenerateAll() { class PostRoutingFilterDefinition { private final List COMMON_FILTERS = Collections.singletonList(mock(FilterDefinition.class)); - private final RouteLocator routeLocator = new RouteLocator(null, null, null, COMMON_FILTERS, Collections.emptyList(), null); + private final RouteLocator routeLocator = new RouteLocator(null, COMMON_FILTERS, Collections.emptyList(), null); private ServiceInstance createServiceInstance(Boolean forwardingEnabled, Boolean encodedCharactersEnabled, Boolean rateLimiterEnabled) { Map metadata = new HashMap<>(); diff --git a/gradle/versions.gradle b/gradle/versions.gradle index deb6f6a823..43edbc239d 100644 --- a/gradle/versions.gradle +++ b/gradle/versions.gradle @@ -5,20 +5,20 @@ dependencyResolutionManagement { version('projectNode', '20.14.0') version('projectNpm', '10.7.0') - version('springBoot', '3.3.7') - version('springBootGraphQl', '3.3.7') - version('springCloudNetflix', '4.1.4') - version('springCloudCommons', '4.1.5') - version('springCloudCB', '3.1.3') - version('springCloudGateway', '4.1.6') - version('springFramework', '6.1.16') + version('springBoot', '3.4.1') + version('springBootGraphQl', '3.4.1') + version('springCloudNetflix', '4.2.0') + version('springCloudCommons', '4.2.0') + version('springCloudCB', '3.2.0') + version('springCloudGateway', '4.2.0') + version('springFramework', '6.2.2') version('springRetry', '2.0.11') version('glassfishHk2', '3.1.1') version('zosUtils', '2.0.5') version('aws', '1.12.780') version('awaitility', '4.2.2') - version('bouncyCastle', '1.79') + version('bouncyCastle', '1.80') version('caffeine', '3.1.8') version('checkerQual', '3.48.4') version('commonsLang3', '3.17.0') @@ -67,21 +67,18 @@ dependencyResolutionManagement { // force version in build.gradle file - compatibility with Slf4j version('log4j', '2.24.3') version('lombok', '1.18.36') - version('netty', '4.1.116.Final') + version('netty', '4.1.117.Final') // netty reactor contains a bug: https://github.com/reactor/reactor-netty/issues/3559 > https://github.com/reactor/reactor-netty/pull/3581 - version('nettyReactor') { - strictly '[1.1.0,1.1.24[' - prefer '1.1.23' - } + version('nettyReactor', '1.2.2') version('nimbusJoseJwt', '9.48') version('openApiDiff', '2.0.1') version('picocli', '4.7.6') - version('reactor', '3.7.1') + version('reactor', '3.7.2') version('restAssured', '5.5.0') version('rhino', '1.8.0') - version('springDoc', '2.6.0') - version('swaggerCore', '2.2.27') + version('springDoc', '2.8.3') + version('swaggerCore', '2.2.28') version('swaggerInflector', '2.0.12') version('swagger2Parser', '1.0.72') version('swagger3Parser', '2.1.24') @@ -98,8 +95,8 @@ dependencyResolutionManagement { version('gradleTestLogger', '4.0.0') version('testLogger', '4.0.0') version('micronautPlatform', '4.6.1') - version('micronaut', '4.7.10') - version('micronautPlugin', '4.4.4') + version('micronaut', '4.7.12') + version('micronautPlugin', '4.4.5') version('shadow', '8.1.1') version('checkstyle', '10.17.0') version('jacoco', '0.8.11') diff --git a/onboarding-enabler-nodejs-sample-app/package.json b/onboarding-enabler-nodejs-sample-app/package.json index 586b763a9d..141b99feb0 100755 --- a/onboarding-enabler-nodejs-sample-app/package.json +++ b/onboarding-enabler-nodejs-sample-app/package.json @@ -19,7 +19,7 @@ "express": "4.21.2" }, "overrides": { - "tough-cookie": "5.0.0" + "tough-cookie": "5.1.0" }, "engines": { "npm": "=10.9.2", diff --git a/onboarding-enabler-nodejs/package-lock.json b/onboarding-enabler-nodejs/package-lock.json index d399d275d5..efcee0d822 100644 --- a/onboarding-enabler-nodejs/package-lock.json +++ b/onboarding-enabler-nodejs/package-lock.json @@ -3742,7 +3742,7 @@ }, "node_modules/gulp-babel": { "version": "7.0.1", - "resolved": "https://registry.npmjs.org/gulp-babel/-/gulp-babel-7.0.1.tgz", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/gulp-babel/-/gulp-babel-7.0.1.tgz", "integrity": "sha512-UqHS3AdxZyJCRxqnAX603Dj3k/Wx6hzcgmav3QcxvsIFq3Y8ZkU7iXd0O+JwD5ivqCc6o0r1S7tCB/xxLnuSNw==", "dev": true, "license": "MIT", diff --git a/onboarding-enabler-spring/src/test/java/org/zowe/apiml/enable/config/EnableApiDiscoveryConfigTest.java b/onboarding-enabler-spring/src/test/java/org/zowe/apiml/enable/config/EnableApiDiscoveryConfigTest.java index 4b0882fcf4..9f3ac01448 100644 --- a/onboarding-enabler-spring/src/test/java/org/zowe/apiml/enable/config/EnableApiDiscoveryConfigTest.java +++ b/onboarding-enabler-spring/src/test/java/org/zowe/apiml/enable/config/EnableApiDiscoveryConfigTest.java @@ -16,8 +16,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.ConfigDataApplicationContextInitializer; -import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.zowe.apiml.enable.EnableApiDiscovery; import org.zowe.apiml.enable.register.RegisterToApiLayer; @@ -40,7 +40,7 @@ class EnableApiDiscoveryConfigTest { @Autowired private ApiMediationServiceConfig apiMediationServiceConfig; - @MockBean + @MockitoBean private ApiMediationClient apiMediationClient; @Test diff --git a/onboarding-enabler-spring/src/test/java/org/zowe/apiml/enable/register/ApimlDisabledRegisterToApiLayerTest.java b/onboarding-enabler-spring/src/test/java/org/zowe/apiml/enable/register/ApimlDisabledRegisterToApiLayerTest.java index 88fa29d394..840c8b3fec 100644 --- a/onboarding-enabler-spring/src/test/java/org/zowe/apiml/enable/register/ApimlDisabledRegisterToApiLayerTest.java +++ b/onboarding-enabler-spring/src/test/java/org/zowe/apiml/enable/register/ApimlDisabledRegisterToApiLayerTest.java @@ -15,9 +15,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.ConfigDataApplicationContextInitializer; -import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.zowe.apiml.enable.EnableApiDiscovery; import org.zowe.apiml.enable.config.EnableApiDiscoveryConfig; @@ -40,7 +40,7 @@ class ApimlDisabledRegisterToApiLayerTest { @Autowired private ApiMediationServiceConfig apiMediationServiceConfig; - @MockBean + @MockitoBean private ApiMediationClient apiMediationClient; @Test diff --git a/onboarding-enabler-spring/src/test/java/org/zowe/apiml/enable/register/ApimlEnabledRegisterToApiLayerTest.java b/onboarding-enabler-spring/src/test/java/org/zowe/apiml/enable/register/ApimlEnabledRegisterToApiLayerTest.java index 472d693f66..230091c4a7 100644 --- a/onboarding-enabler-spring/src/test/java/org/zowe/apiml/enable/register/ApimlEnabledRegisterToApiLayerTest.java +++ b/onboarding-enabler-spring/src/test/java/org/zowe/apiml/enable/register/ApimlEnabledRegisterToApiLayerTest.java @@ -15,9 +15,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.ConfigDataApplicationContextInitializer; -import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.zowe.apiml.enable.EnableApiDiscovery; import org.zowe.apiml.enable.config.EnableApiDiscoveryConfig; @@ -41,7 +41,7 @@ class ApimlEnabledRegisterToApiLayerTest { @Autowired private ApiMediationServiceConfig apiMediationServiceConfig; - @MockBean + @MockitoBean private ApiMediationClient apiMediationClient; @Test diff --git a/scripts/docs/package.json b/scripts/docs/package.json index b58b4bd4a8..3a4c36aa22 100644 --- a/scripts/docs/package.json +++ b/scripts/docs/package.json @@ -10,6 +10,6 @@ "author": "", "license": "EPL-2.0", "dependencies": { - "octokit": "4.0.3" + "octokit": "4.1.0" } } diff --git a/scripts/release_components/package.json b/scripts/release_components/package.json index bb9d1a05d8..c9ee5fe06a 100644 --- a/scripts/release_components/package.json +++ b/scripts/release_components/package.json @@ -10,6 +10,6 @@ "author": "", "license": "EPL-2.0", "dependencies": { - "octokit": "4.0.3" + "octokit": "4.1.0" } } diff --git a/scripts/release_docs/package.json b/scripts/release_docs/package.json index cc107eb1ca..40c16fffcb 100644 --- a/scripts/release_docs/package.json +++ b/scripts/release_docs/package.json @@ -10,6 +10,6 @@ "author": "", "license": "EPL-2.0", "dependencies": { - "octokit": "4.0.3" + "octokit": "4.1.0" } } diff --git a/zaas-service/src/main/java/org/zowe/apiml/zaas/ZaasApplication.java b/zaas-service/src/main/java/org/zowe/apiml/zaas/ZaasApplication.java index 5afdd24d4f..8280e32cb3 100644 --- a/zaas-service/src/main/java/org/zowe/apiml/zaas/ZaasApplication.java +++ b/zaas-service/src/main/java/org/zowe/apiml/zaas/ZaasApplication.java @@ -24,7 +24,7 @@ import static org.zowe.apiml.extension.ZoweRuntimeEnvironment.defaultEnv; -@EnableWebSecurity(debug = true) +@EnableWebSecurity @SpringBootApplication @EnableDiscoveryClient @ComponentScan( diff --git a/zaas-service/src/main/java/org/zowe/apiml/zaas/config/DiscoveryClientOrderProcessorBean.java b/zaas-service/src/main/java/org/zowe/apiml/zaas/config/DiscoveryClientOrderProcessorBean.java index 1f3efa59c1..6f858879fb 100644 --- a/zaas-service/src/main/java/org/zowe/apiml/zaas/config/DiscoveryClientOrderProcessorBean.java +++ b/zaas-service/src/main/java/org/zowe/apiml/zaas/config/DiscoveryClientOrderProcessorBean.java @@ -30,7 +30,7 @@ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) .flatMap(Arrays::stream) .distinct() .map(beanFactory::getBeanDefinition) - .forEach(bd -> bd.setDependsOn("scopedTarget.zosmfService")); + .forEach(bd -> bd.setDependsOn("scopedTarget.zosmfService", "eurekaAutoServiceRegistration")); } } diff --git a/zaas-service/src/main/java/org/zowe/apiml/zaas/security/config/NewSecurityConfiguration.java b/zaas-service/src/main/java/org/zowe/apiml/zaas/security/config/NewSecurityConfiguration.java index 8c014c43b8..9068b8cb72 100644 --- a/zaas-service/src/main/java/org/zowe/apiml/zaas/security/config/NewSecurityConfiguration.java +++ b/zaas-service/src/main/java/org/zowe/apiml/zaas/security/config/NewSecurityConfiguration.java @@ -158,7 +158,7 @@ private class CustomSecurityFilters extends AbstractHttpConfigurer { @Override public void configure(HttpSecurity http) { - http.addFilterAfter(new CategorizeCertsFilter(publicKeyCertificatesBase64, certificateValidator, authExceptionHandler), org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter.class) + http.addFilterAfter(new CategorizeCertsFilter(publicKeyCertificatesBase64, certificateValidator), org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter.class) .addFilterAfter(x509ForwardingAwareAuthenticationFilter(), CategorizeCertsFilter.class) .addFilterAfter(loginFilter(http), X509AuthAwareFilter.class); } @@ -301,7 +301,7 @@ public SecurityFilterChain authZaasEndpointsFilterChain(HttpSecurity http) throw .authorizeHttpRequests(requests -> requests .anyRequest().authenticated()) .x509(x509 -> x509.userDetailsService(x509UserDetailsService())) - .addFilterAfter(new CategorizeCertsFilter(publicKeyCertificatesBase64, certificateValidator, authExceptionHandler), org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter.class) + .addFilterAfter(new CategorizeCertsFilter(publicKeyCertificatesBase64, certificateValidator), org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter.class) .addFilterAfter(new ExtractAuthSourceFilter(authSourceService, authExceptionHandler), org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter.class) .addFilterAfter(new ZaasAuthenticationFilter(authSourceService, authExceptionHandler), CategorizeCertsFilter.class); @@ -373,11 +373,9 @@ public SecurityFilterChain ticketFilterChain(HttpSecurity http) throws Exception ))).authorizeHttpRequests(requests -> requests.anyRequest().authenticated()) .authenticationProvider(tokenAuthenticationProvider) .logout(AbstractHttpConfigurer::disable) // logout filter in this chain not needed - //Todo: validate .x509(x509 -> x509 //default x509 filter, authenticates trusted cert, ticketFilter(..) depends on this .userDetailsService(new SimpleUserDetailService()) - ) - .with(new CustomSecurityFilters(), withDefaults()) + ).with(new CustomSecurityFilters(), withDefaults()) .build(); } @@ -385,10 +383,7 @@ private class CustomSecurityFilters extends AbstractHttpConfigurer requests .anyRequest().authenticated()) .authenticationProvider(tokenAuthenticationProvider) - .logout(AbstractHttpConfigurer::disable) // logout filter in this chain not needed + .logout(logout -> logout.disable()) // logout filter in this chain not needed .x509(x509 -> x509 //default x509 filter, authenticates trusted cert, refreshFilter(..) depends on this .userDetailsService(new SimpleUserDetailService())) .with(new CustomSecurityFilters(), Customizer.withDefaults()); @@ -500,10 +495,6 @@ public SecurityFilterChain certificateOrAuthEndpointsFilterChain(HttpSecurity ht http.securityMatchers(matchers -> matchers .requestMatchers("/application/**") .requestMatchers(HttpMethod.POST, SafResourceAccessController.FULL_CONTEXT_PATH) - .requestMatchers("/gateway/conformance/**") - .requestMatchers("/gateway/api/v1/conformance/**") - .requestMatchers("/gateway/validate") - .requestMatchers("/gateway/api/v1/validate") ) ).authorizeHttpRequests(requests -> requests .anyRequest() @@ -516,7 +507,7 @@ public SecurityFilterChain certificateOrAuthEndpointsFilterChain(HttpSecurity ht // filter out API ML certificate .addFilterBefore(reversedCategorizeCertFilter(), org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter.class); } else { - http.addFilterAfter(new CategorizeCertsFilter(publicKeyCertificatesBase64, certificateValidator, authExceptionHandler), org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter.class); + http.addFilterAfter(new CategorizeCertsFilter(publicKeyCertificatesBase64, certificateValidator), org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter.class); } return http.authenticationProvider(compoundAuthProvider) // for authenticating credentials @@ -573,7 +564,7 @@ private BearerContentFilter bearerContentFilter(AuthenticationManager authentica } private CategorizeCertsFilter reversedCategorizeCertFilter() { - CategorizeCertsFilter out = new CategorizeCertsFilter(publicKeyCertificatesBase64, certificateValidator, authExceptionHandler); + CategorizeCertsFilter out = new CategorizeCertsFilter(publicKeyCertificatesBase64, certificateValidator); out.setCertificateForClientAuth(crt -> out.getPublicKeyCertificatesBase64().contains(CategorizeCertsFilter.base64EncodePublicKey(crt))); out.setApimlCertificate(crt -> !out.getPublicKeyCertificatesBase64().contains(CategorizeCertsFilter.base64EncodePublicKey(crt))); return out; @@ -642,4 +633,5 @@ protected HttpSecurity baseConfigure(HttpSecurity http) throws Exception { private UserDetailsService x509UserDetailsService() { return username -> new User(username, "", Collections.emptyList()); } + } diff --git a/zaas-service/src/test/java/org/zowe/apiml/acceptance/RefreshEndpointTest.java b/zaas-service/src/test/java/org/zowe/apiml/acceptance/RefreshEndpointTest.java index 1f0328a4b7..01dea77569 100644 --- a/zaas-service/src/test/java/org/zowe/apiml/acceptance/RefreshEndpointTest.java +++ b/zaas-service/src/test/java/org/zowe/apiml/acceptance/RefreshEndpointTest.java @@ -18,8 +18,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.zowe.apiml.product.web.HttpConfig; import org.zowe.apiml.security.common.login.LoginRequest; import org.zowe.apiml.zaas.ZaasApplication; @@ -34,7 +34,6 @@ import static org.hamcrest.core.Is.is; @SpringBootTest(classes = ZaasApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) -@MockBean(name = "x509Mapper", classes = AuthenticationMapper.class) public class RefreshEndpointTest { private final static String USERNAME = "USER"; @@ -46,6 +45,9 @@ public class RefreshEndpointTest { @Value("${apiml.service.hostname:localhost}") private String hostname; + @MockitoBean(name = "x509Mapper") + private AuthenticationMapper x509Mapper; + @Autowired HttpConfig httpConfig; diff --git a/zaas-service/src/test/java/org/zowe/apiml/acceptance/ZaasTest.java b/zaas-service/src/test/java/org/zowe/apiml/acceptance/ZaasTest.java index 68f15cf4a4..5df166a7ea 100644 --- a/zaas-service/src/test/java/org/zowe/apiml/acceptance/ZaasTest.java +++ b/zaas-service/src/test/java/org/zowe/apiml/acceptance/ZaasTest.java @@ -16,9 +16,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.zowe.apiml.product.web.HttpConfig; import org.zowe.apiml.zaas.ZaasApplication; import org.zowe.apiml.zaas.security.mapping.AuthenticationMapper; @@ -33,11 +33,13 @@ @TestPropertySource(properties = { "apiml.security.auth.provider=dummy" // To simulate SAF auth provider that does not run outside of mainframe }) -@MockBean(name = "x509Mapper", classes = AuthenticationMapper.class) class ZaasTest { private static final String COOKIE = "apimlAuthenticationToken"; + @MockitoBean(name = "x509Mapper") + private AuthenticationMapper x509Mapper; + @Autowired private HttpConfig httpConfig; diff --git a/zaas-service/src/test/java/org/zowe/apiml/zaas/config/CacheConfigTest.java b/zaas-service/src/test/java/org/zowe/apiml/zaas/config/CacheConfigTest.java index 83ec8b4edd..3a5c14cebe 100644 --- a/zaas-service/src/test/java/org/zowe/apiml/zaas/config/CacheConfigTest.java +++ b/zaas-service/src/test/java/org/zowe/apiml/zaas/config/CacheConfigTest.java @@ -14,11 +14,11 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.cache.CacheManager; import org.springframework.cache.jcache.JCacheCacheManager; import org.springframework.cache.support.NoOpCacheManager; import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.web.client.RestTemplate; import org.zowe.apiml.product.gateway.GatewayClient; @@ -31,10 +31,14 @@ class CacheConfigTest { @Nested @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = CacheConfig.class) @ActiveProfiles("test") - @MockBean(GatewayClient.class) - @MockBean(name = "restTemplateWithKeystore", value = RestTemplate.class) class EnabledCache { + @MockitoBean + private GatewayClient gatewayClient; + + @MockitoBean(name = "restTemplateWithKeystore") + private RestTemplate restTemplateWithKeystore; + @Autowired private CacheManager cacheManager; @@ -51,10 +55,14 @@ void testCacheManagerIsRealImplementation() { "apiml.caching.enabled=false" }) @ActiveProfiles("test") - @MockBean(GatewayClient.class) - @MockBean(name = "restTemplateWithKeystore", value = RestTemplate.class) class DisabledCache { + @MockitoBean + private GatewayClient gatewayClient; + + @MockitoBean(name = "restTemplateWithKeystore") + private RestTemplate restTemplateWithKeystore; + @Autowired private CacheManager cacheManager; diff --git a/zaas-service/src/test/java/org/zowe/apiml/zaas/security/config/AttlsConfigTest.java b/zaas-service/src/test/java/org/zowe/apiml/zaas/security/config/AttlsConfigTest.java index d085f5fc26..d1f1e19a06 100644 --- a/zaas-service/src/test/java/org/zowe/apiml/zaas/security/config/AttlsConfigTest.java +++ b/zaas-service/src/test/java/org/zowe/apiml/zaas/security/config/AttlsConfigTest.java @@ -18,10 +18,10 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.zowe.apiml.zaas.security.mapping.AuthenticationMapper; import javax.net.ssl.SSLException; @@ -46,9 +46,11 @@ } ) @TestInstance(Lifecycle.PER_CLASS) -@MockBean(name = "x509Mapper", classes = AuthenticationMapper.class) public class AttlsConfigTest { + @MockitoBean(name = "x509Mapper") + private AuthenticationMapper x509Mapper; + @Autowired HttpSecurity http; diff --git a/zaas-service/src/test/java/org/zowe/apiml/zaas/security/service/AuthenticationServiceTest.java b/zaas-service/src/test/java/org/zowe/apiml/zaas/security/service/AuthenticationServiceTest.java index b8ce1b8a11..4793c5400e 100644 --- a/zaas-service/src/test/java/org/zowe/apiml/zaas/security/service/AuthenticationServiceTest.java +++ b/zaas-service/src/test/java/org/zowe/apiml/zaas/security/service/AuthenticationServiceTest.java @@ -29,13 +29,13 @@ import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.cache.CacheManager; import org.springframework.context.ApplicationContext; import org.springframework.http.HttpHeaders; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.util.ReflectionTestUtils; import org.springframework.web.client.RestTemplate; @@ -538,10 +538,23 @@ void stubJWTSecurityForSign() { @Nested @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = { CacheConfig.class, AuthenticationService.class, AuthConfigurationProperties.class }) - @MockBean({ JwtSecurity.class, ZosmfService.class, EurekaClient.class, GatewayClient.class }) - @MockBean(name = "restTemplateWithKeystore", value = RestTemplate.class) class GivenCacheJWTTest { + @MockitoBean + private JwtSecurity jwtSecurity; + + @MockitoBean + private ZosmfService zosmfService; + + @MockitoBean + private EurekaClient eurekaClient; + + @MockitoBean + private GatewayClient gatewayClient; + + @MockitoBean(name = "restTemplateWithKeystore") + private RestTemplate restTemplateWithKeystore; + @Autowired private AuthenticationService authService; diff --git a/zaas-service/src/test/java/org/zowe/apiml/zaas/zaas/ZaasAuthenticationFilterTest.java b/zaas-service/src/test/java/org/zowe/apiml/zaas/zaas/ZaasAuthenticationFilterTest.java index c51f187dc4..bf2b0cac30 100644 --- a/zaas-service/src/test/java/org/zowe/apiml/zaas/zaas/ZaasAuthenticationFilterTest.java +++ b/zaas-service/src/test/java/org/zowe/apiml/zaas/zaas/ZaasAuthenticationFilterTest.java @@ -112,10 +112,6 @@ private void mockAuthSource(boolean isValid) { AuthSource authSource = new JwtAuthSource("token"); request.setAttribute(AUTH_SOURCE_ATTR, authSource); when(authSourceService.isValid(authSource)).thenReturn(isValid); - //TODO - //if (isValid) { - // when(authSourceService.parse(authSource)).thenReturn(new ParsedTokenAuthSource("user", null, null, null)); - //} } } diff --git a/zaas-service/src/test/java/org/zowe/apiml/zaas/zaas/ZaasExceptionHandlerTest.java b/zaas-service/src/test/java/org/zowe/apiml/zaas/zaas/ZaasExceptionHandlerTest.java index 36c521db4f..c44cd0cfa8 100644 --- a/zaas-service/src/test/java/org/zowe/apiml/zaas/zaas/ZaasExceptionHandlerTest.java +++ b/zaas-service/src/test/java/org/zowe/apiml/zaas/zaas/ZaasExceptionHandlerTest.java @@ -15,14 +15,12 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import org.zowe.apiml.product.web.HttpConfig; import org.zowe.apiml.security.common.auth.saf.EndpointImproperlyConfigureException; import org.zowe.apiml.security.common.auth.saf.UnsupportedResourceClassException; @@ -43,18 +41,11 @@ class ZaasExceptionHandlerTest { @LocalServerPort private int port; - @Autowired - private HttpConfig httpConfig; - @BeforeEach void init() { RestAssured.baseURI = "https://localhost"; RestAssured.port = port; RestAssured.useRelaxedHTTPSValidation(); -// TODO: validate -// RestAssured.config = RestAssured.config.sslConfig(new SSLConfig().sslSocketFactory( -// new SSLSocketFactory(httpConfig.getSecureSslContext(), SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER) -// )); } @Test @@ -63,7 +54,7 @@ void givenUnknownEndpoint_whenCallZaas_thenReturns404WithMessage() { .get("/unknown/endpoint") .then() .statusCode(404) - .body("messages[0].messageKey", is("org.zowe.apiml.common.endPointNotFound")); + .body("messages[0].messageKey", is("org.zowe.apiml.common.notFound")); } @ParameterizedTest @@ -79,21 +70,6 @@ void givenNoCredentials_whenCallZaas_thenReturns401WithMessage(String url) { .body("messages[0].messageKey", is("org.zowe.apiml.security.authRequired")); } - //TODO: validate -// @ParameterizedTest -// @ValueSource(strings = { -// "/zaas/scheme/ticket", -// "/application/health" -// }) -// void givenNoCertificateAndNoCredentials_whenCallZaas_thenReturns401WithMessage(String url) { -// given().config(RestAssuredConfig.newConfig()).relaxedHTTPSValidation() -// .when() -// .get(url) -// .then() -// .statusCode(401) -// .body("messages[0].messageKey", is("org.zowe.apiml.security.authRequired")); -// } - @ParameterizedTest @ValueSource(strings = { "/zaas/api/v1/auth/login" diff --git a/zowe-cli-id-federation-plugin/package-lock.json b/zowe-cli-id-federation-plugin/package-lock.json index 1df9235ef6..724fdd30e8 100644 --- a/zowe-cli-id-federation-plugin/package-lock.json +++ b/zowe-cli-id-federation-plugin/package-lock.json @@ -12,18 +12,18 @@ "csv-parse": "5.6.0" }, "devDependencies": { - "@eslint/js": "9.17.0", + "@eslint/js": "9.18.0", "@types/jest": "29.5.14", - "@types/node": "20.17.12", - "@typescript-eslint/eslint-plugin": "8.19.1", - "@typescript-eslint/parser": "8.19.1", - "@zowe/cli": "8.10.3", - "@zowe/cli-test-utils": "8.10.3", - "@zowe/imperative": "8.10.3", + "@types/node": "20.17.14", + "@typescript-eslint/eslint-plugin": "8.20.0", + "@typescript-eslint/parser": "8.20.0", + "@zowe/cli": "8.11.0", + "@zowe/cli-test-utils": "8.11.0", + "@zowe/imperative": "8.11.0", "copyfiles": "2.4.1", "env-cmd": "10.1.0", - "eslint": "9.17.0", - "eslint-plugin-jest": "28.10.0", + "eslint": "9.18.0", + "eslint-plugin-jest": "28.11.0", "eslint-plugin-license-header": "0.6.1", "eslint-plugin-unused-imports": "4.1.4", "globals": "15.14.0", @@ -39,14 +39,14 @@ "ts-jest": "29.2.5", "ts-node": "10.9.2", "typedoc": "0.27.6", - "typescript": "5.7.2" + "typescript": "5.7.3" }, "engines": { "node": "=20.18.1", "npm": "=10.9.2" }, "peerDependencies": { - "@zowe/imperative": "8.10.3" + "@zowe/imperative": "8.11.0" } }, "node_modules/@ampproject/remapping": { @@ -130,14 +130,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.26.3", - "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@babel/generator/-/generator-7.26.3.tgz", - "integrity": "sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ==", + "version": "7.26.5", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@babel/generator/-/generator-7.26.5.tgz", + "integrity": "sha512-2caSP6fN9I7HOe6nqhtft7V4g7/V/gfDsC3Ag4W7kEzzvRGKqiv0pu0HogPiZ3KaVSoNDhUws6IJjDjpfmYIXw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.26.3", - "@babel/types": "^7.26.3", + "@babel/parser": "^7.26.5", + "@babel/types": "^7.26.5", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" @@ -274,13 +274,13 @@ } }, "node_modules/@babel/parser": { - "version": "7.26.3", - "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@babel/parser/-/parser-7.26.3.tgz", - "integrity": "sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA==", + "version": "7.26.5", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@babel/parser/-/parser-7.26.5.tgz", + "integrity": "sha512-SRJ4jYmXRqV1/Xc+TIVG84WjHBXKlxO9sHQnA2Pf12QQEAp1LOh6kDzNHXcUnbH1QI0FDoPPVOt+vyUDucxpaw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.26.3" + "@babel/types": "^7.26.5" }, "bin": { "parser": "bin/babel-parser.js" @@ -531,17 +531,17 @@ } }, "node_modules/@babel/traverse": { - "version": "7.26.4", - "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@babel/traverse/-/traverse-7.26.4.tgz", - "integrity": "sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w==", + "version": "7.26.5", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@babel/traverse/-/traverse-7.26.5.tgz", + "integrity": "sha512-rkOSPOw+AXbgtwUga3U4u8RpoK9FEFWBNAlTpcnkLFjL5CT+oyHNuUUC/xx6XefEJ16r38r8Bc/lfp6rYuHeJQ==", "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.3", - "@babel/parser": "^7.26.3", + "@babel/generator": "^7.26.5", + "@babel/parser": "^7.26.5", "@babel/template": "^7.25.9", - "@babel/types": "^7.26.3", + "@babel/types": "^7.26.5", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -560,9 +560,9 @@ } }, "node_modules/@babel/types": { - "version": "7.26.3", - "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@babel/types/-/types-7.26.3.tgz", - "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==", + "version": "7.26.5", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@babel/types/-/types-7.26.5.tgz", + "integrity": "sha512-L6mZmwFDK6Cjh1nRCLXpa6no13ZIioJDz7mdkzHv399pThrTa/k0nUlNaenOeh2kWu/iaOQYElEpKPUswUa9Vg==", "dev": true, "license": "MIT", "dependencies": { @@ -665,9 +665,9 @@ } }, "node_modules/@eslint/core": { - "version": "0.9.1", - "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@eslint/core/-/core-0.9.1.tgz", - "integrity": "sha512-GuUdqkyyzQI5RMIWkHhvTWLCyLo1jNK3vzkSyaExH5kHPDHcuL2VOpHjmMY+y3+NC69qAKToBqldTBgYeLSr9Q==", + "version": "0.10.0", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@eslint/core/-/core-0.10.0.tgz", + "integrity": "sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -714,9 +714,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.17.0", - "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@eslint/js/-/js-9.17.0.tgz", - "integrity": "sha512-Sxc4hqcs1kTu0iID3kcZDW3JHq2a77HO9P8CP6YEA/FpH3Ll8UXE2r/86Rz9YJLKme39S9vU5OWNjC6Xl0Cr3w==", + "version": "9.18.0", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@eslint/js/-/js-9.18.0.tgz", + "integrity": "sha512-fK6L7rxcq6/z+AaQMtiFTkvbHkBLNlwyRxHpKawP0x3u9+NC6MQTnFW+AdpwC6gfHTW0051cokQgtTN2FqlxQA==", "dev": true, "license": "MIT", "engines": { @@ -734,12 +734,13 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.2.4", - "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@eslint/plugin-kit/-/plugin-kit-0.2.4.tgz", - "integrity": "sha512-zSkKow6H5Kdm0ZUQUB2kV5JIXqoG0+uH5YADhaEHswm664N9Db8dXSi0nMJpacpMf+MyyglF1vnZohpEg5yUtg==", + "version": "0.2.5", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@eslint/plugin-kit/-/plugin-kit-0.2.5.tgz", + "integrity": "sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A==", "dev": true, "license": "Apache-2.0", "dependencies": { + "@eslint/core": "^0.10.0", "levn": "^0.4.1" }, "engines": { @@ -2020,9 +2021,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.17.12", - "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@types/node/-/node-20.17.12.tgz", - "integrity": "sha512-vo/wmBgMIiEA23A/knMfn/cf37VnuF52nZh5ZoW0GWt4e4sxNquibrMRJ7UQsA06+MBx9r/H1jsI9grYjQCQlw==", + "version": "20.17.14", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@types/node/-/node-20.17.14.tgz", + "integrity": "sha512-w6qdYetNL5KRBiSClK/KWai+2IMEJuAj+EujKCumalFOwXtvOXaEan9AuwcRID2IcOIAWSIfR495hBtgKlx2zg==", "dev": true, "license": "MIT", "dependencies": { @@ -2057,17 +2058,17 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.19.1", - "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.19.1.tgz", - "integrity": "sha512-tJzcVyvvb9h/PB96g30MpxACd9IrunT7GF9wfA9/0TJ1LxGOJx1TdPzSbBBnNED7K9Ka8ybJsnEpiXPktolTLg==", + "version": "8.20.0", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.20.0.tgz", + "integrity": "sha512-naduuphVw5StFfqp4Gq4WhIBE2gN1GEmMUExpJYknZJdRnc+2gDzB8Z3+5+/Kv33hPQRDGzQO/0opHE72lZZ6A==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.19.1", - "@typescript-eslint/type-utils": "8.19.1", - "@typescript-eslint/utils": "8.19.1", - "@typescript-eslint/visitor-keys": "8.19.1", + "@typescript-eslint/scope-manager": "8.20.0", + "@typescript-eslint/type-utils": "8.20.0", + "@typescript-eslint/utils": "8.20.0", + "@typescript-eslint/visitor-keys": "8.20.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -2100,16 +2101,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.19.1", - "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@typescript-eslint/parser/-/parser-8.19.1.tgz", - "integrity": "sha512-67gbfv8rAwawjYx3fYArwldTQKoYfezNUT4D5ioWetr/xCrxXxvleo3uuiFuKfejipvq+og7mjz3b0G2bVyUCw==", + "version": "8.20.0", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@typescript-eslint/parser/-/parser-8.20.0.tgz", + "integrity": "sha512-gKXG7A5HMyjDIedBi6bUrDcun8GIjnI8qOwVLiY3rx6T/sHP/19XLJOnIq/FgQvWLHja5JN/LSE7eklNBr612g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.19.1", - "@typescript-eslint/types": "8.19.1", - "@typescript-eslint/typescript-estree": "8.19.1", - "@typescript-eslint/visitor-keys": "8.19.1", + "@typescript-eslint/scope-manager": "8.20.0", + "@typescript-eslint/types": "8.20.0", + "@typescript-eslint/typescript-estree": "8.20.0", + "@typescript-eslint/visitor-keys": "8.20.0", "debug": "^4.3.4" }, "engines": { @@ -2125,14 +2126,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.19.1", - "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@typescript-eslint/scope-manager/-/scope-manager-8.19.1.tgz", - "integrity": "sha512-60L9KIuN/xgmsINzonOcMDSB8p82h95hoBfSBtXuO4jlR1R9L1xSkmVZKgCPVfavDlXihh4ARNjXhh1gGnLC7Q==", + "version": "8.20.0", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@typescript-eslint/scope-manager/-/scope-manager-8.20.0.tgz", + "integrity": "sha512-J7+VkpeGzhOt3FeG1+SzhiMj9NzGD/M6KoGn9f4dbz3YzK9hvbhVTmLj/HiTp9DazIzJ8B4XcM80LrR9Dm1rJw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.19.1", - "@typescript-eslint/visitor-keys": "8.19.1" + "@typescript-eslint/types": "8.20.0", + "@typescript-eslint/visitor-keys": "8.20.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2143,14 +2144,14 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.19.1", - "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@typescript-eslint/type-utils/-/type-utils-8.19.1.tgz", - "integrity": "sha512-Rp7k9lhDKBMRJB/nM9Ksp1zs4796wVNyihG9/TU9R6KCJDNkQbc2EOKjrBtLYh3396ZdpXLtr/MkaSEmNMtykw==", + "version": "8.20.0", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@typescript-eslint/type-utils/-/type-utils-8.20.0.tgz", + "integrity": "sha512-bPC+j71GGvA7rVNAHAtOjbVXbLN5PkwqMvy1cwGeaxUoRQXVuKCebRoLzm+IPW/NtFFpstn1ummSIasD5t60GA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.19.1", - "@typescript-eslint/utils": "8.19.1", + "@typescript-eslint/typescript-estree": "8.20.0", + "@typescript-eslint/utils": "8.20.0", "debug": "^4.3.4", "ts-api-utils": "^2.0.0" }, @@ -2180,9 +2181,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.19.1", - "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@typescript-eslint/types/-/types-8.19.1.tgz", - "integrity": "sha512-JBVHMLj7B1K1v1051ZaMMgLW4Q/jre5qGK0Ew6UgXz1Rqh+/xPzV1aW581OM00X6iOfyr1be+QyW8LOUf19BbA==", + "version": "8.20.0", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@typescript-eslint/types/-/types-8.20.0.tgz", + "integrity": "sha512-cqaMiY72CkP+2xZRrFt3ExRBu0WmVitN/rYPZErA80mHjHx/Svgp8yfbzkJmDoQ/whcytOPO9/IZXnOc+wigRA==", "dev": true, "license": "MIT", "engines": { @@ -2194,14 +2195,14 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.19.1", - "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@typescript-eslint/typescript-estree/-/typescript-estree-8.19.1.tgz", - "integrity": "sha512-jk/TZwSMJlxlNnqhy0Eod1PNEvCkpY6MXOXE/WLlblZ6ibb32i2We4uByoKPv1d0OD2xebDv4hbs3fm11SMw8Q==", + "version": "8.20.0", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@typescript-eslint/typescript-estree/-/typescript-estree-8.20.0.tgz", + "integrity": "sha512-Y7ncuy78bJqHI35NwzWol8E0X7XkRVS4K4P4TCyzWkOJih5NDvtoRDW4Ba9YJJoB2igm9yXDdYI/+fkiiAxPzA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.19.1", - "@typescript-eslint/visitor-keys": "8.19.1", + "@typescript-eslint/types": "8.20.0", + "@typescript-eslint/visitor-keys": "8.20.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -2260,16 +2261,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.19.1", - "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@typescript-eslint/utils/-/utils-8.19.1.tgz", - "integrity": "sha512-IxG5gLO0Ne+KaUc8iW1A+XuKLd63o4wlbI1Zp692n1xojCl/THvgIKXJXBZixTh5dd5+yTJ/VXH7GJaaw21qXA==", + "version": "8.20.0", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@typescript-eslint/utils/-/utils-8.20.0.tgz", + "integrity": "sha512-dq70RUw6UK9ei7vxc4KQtBRk7qkHZv447OUZ6RPQMQl71I3NZxQJX/f32Smr+iqWrB02pHKn2yAdHBb0KNrRMA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.19.1", - "@typescript-eslint/types": "8.19.1", - "@typescript-eslint/typescript-estree": "8.19.1" + "@typescript-eslint/scope-manager": "8.20.0", + "@typescript-eslint/types": "8.20.0", + "@typescript-eslint/typescript-estree": "8.20.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2284,13 +2285,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.19.1", - "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@typescript-eslint/visitor-keys/-/visitor-keys-8.19.1.tgz", - "integrity": "sha512-fzmjU8CHK853V/avYZAvuVut3ZTfwN5YtMaoi+X9Y9MA9keaWNHC3zEQ9zvyX/7Hj+5JkNyK1l7TOR2hevHB6Q==", + "version": "8.20.0", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@typescript-eslint/visitor-keys/-/visitor-keys-8.20.0.tgz", + "integrity": "sha512-v/BpkeeYAsPkKCkR8BDwcno0llhzWVqPOamQrAEMdpZav2Y9OVjd9dwJyBLJWwf335B5DmlifECIkZRJCaGaHA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.19.1", + "@typescript-eslint/types": "8.20.0", "eslint-visitor-keys": "^4.2.0" }, "engines": { @@ -2371,25 +2372,25 @@ "dev": true }, "node_modules/@zowe/cli": { - "version": "8.10.3", - "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@zowe/cli/-/cli-8.10.3.tgz", - "integrity": "sha512-2eX7cHU6Ua/YM86bXuzO5OTzFyHIKjEKyXq+NPSHiQcMnss97RN0Kj2ey00YvA2TymVJGtVS4Q1oHo/Oh0bKXg==", + "version": "8.11.0", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@zowe/cli/-/cli-8.11.0.tgz", + "integrity": "sha512-DgFqizIICBsV8aJXYKUftjNNNoJLLNtiEeX+HMFNzvBLx6nk2ihglUf+7+Bw/BO1zEHNS8MbT8WSyUir/9JDxg==", "dev": true, "hasInstallScript": true, "hasShrinkwrap": true, "license": "EPL-2.0", "dependencies": { - "@zowe/core-for-zowe-sdk": "8.10.3", - "@zowe/imperative": "8.10.3", - "@zowe/provisioning-for-zowe-sdk": "8.10.3", - "@zowe/zos-console-for-zowe-sdk": "8.10.3", - "@zowe/zos-files-for-zowe-sdk": "8.10.3", - "@zowe/zos-jobs-for-zowe-sdk": "8.10.3", - "@zowe/zos-logs-for-zowe-sdk": "8.10.3", - "@zowe/zos-tso-for-zowe-sdk": "8.10.3", - "@zowe/zos-uss-for-zowe-sdk": "8.10.3", - "@zowe/zos-workflows-for-zowe-sdk": "8.10.3", - "@zowe/zosmf-for-zowe-sdk": "8.10.3", + "@zowe/core-for-zowe-sdk": "8.11.0", + "@zowe/imperative": "8.11.0", + "@zowe/provisioning-for-zowe-sdk": "8.11.0", + "@zowe/zos-console-for-zowe-sdk": "8.11.0", + "@zowe/zos-files-for-zowe-sdk": "8.11.0", + "@zowe/zos-jobs-for-zowe-sdk": "8.11.0", + "@zowe/zos-logs-for-zowe-sdk": "8.11.0", + "@zowe/zos-tso-for-zowe-sdk": "8.11.0", + "@zowe/zos-uss-for-zowe-sdk": "8.11.0", + "@zowe/zos-workflows-for-zowe-sdk": "8.11.0", + "@zowe/zosmf-for-zowe-sdk": "8.11.0", "find-process": "1.4.7", "lodash": "4.17.21", "minimatch": "9.0.5", @@ -2402,13 +2403,13 @@ "node": ">=18.12.0" }, "optionalDependencies": { - "@zowe/secrets-for-zowe-sdk": "8.1.2" + "@zowe/secrets-for-zowe-sdk": "8.10.4" } }, "node_modules/@zowe/cli-test-utils": { - "version": "8.10.3", - "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@zowe/cli-test-utils/-/cli-test-utils-8.10.3.tgz", - "integrity": "sha512-mX8ZX/648PVUfrqzOJP6784BNu5vj1GfMGIF9PLUFSPVuqNP1ArKWc4enG1RCUBBA0idM7touVIjmIAjERPaRQ==", + "version": "8.11.0", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@zowe/cli-test-utils/-/cli-test-utils-8.11.0.tgz", + "integrity": "sha512-0Mav0JoE0djVbj8B4p7C9zuuSe92X3OzIMDnFDYdONQqGibU/7fOZVDgQZzMHxG8moQPxCZ3z7e7Q9RmS7s8TA==", "dev": true, "license": "EPL-2.0", "dependencies": { @@ -2446,7 +2447,7 @@ }, "node_modules/@zowe/cli/node_modules/@isaacs/cliui": { "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@isaacs/cliui/-/cliui-8.0.2.tgz", "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", "dev": true, "license": "ISC", @@ -2464,7 +2465,7 @@ }, "node_modules/@zowe/cli/node_modules/@isaacs/cliui/node_modules/ansi-regex": { "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/ansi-regex/-/ansi-regex-6.1.0.tgz", "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "dev": true, "license": "MIT", @@ -2477,7 +2478,7 @@ }, "node_modules/@zowe/cli/node_modules/@isaacs/cliui/node_modules/ansi-styles": { "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/ansi-styles/-/ansi-styles-6.2.1.tgz", "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", "dev": true, "license": "MIT", @@ -2490,14 +2491,14 @@ }, "node_modules/@zowe/cli/node_modules/@isaacs/cliui/node_modules/emoji-regex": { "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/emoji-regex/-/emoji-regex-9.2.2.tgz", "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true, "license": "MIT" }, "node_modules/@zowe/cli/node_modules/@isaacs/cliui/node_modules/string-width": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/string-width/-/string-width-5.1.2.tgz", "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "dev": true, "license": "MIT", @@ -2515,7 +2516,7 @@ }, "node_modules/@zowe/cli/node_modules/@isaacs/cliui/node_modules/strip-ansi": { "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/strip-ansi/-/strip-ansi-7.1.0.tgz", "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, "license": "MIT", @@ -2531,7 +2532,7 @@ }, "node_modules/@zowe/cli/node_modules/@isaacs/cliui/node_modules/wrap-ansi": { "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", "dev": true, "license": "MIT", @@ -2722,7 +2723,7 @@ }, "node_modules/@zowe/cli/node_modules/@pkgjs/parseargs": { "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", "dev": true, "license": "MIT", @@ -2969,9 +2970,9 @@ "license": "MIT" }, "node_modules/@zowe/cli/node_modules/@zowe/core-for-zowe-sdk": { - "version": "8.10.3", - "resolved": "https://registry.npmjs.org/@zowe/core-for-zowe-sdk/-/core-for-zowe-sdk-8.10.3.tgz", - "integrity": "sha512-D/6fSb8ThYTq7s7k6fbhjJnKasG8Wh2RJV5sJWIu3D6Mi8mrChGv6kWwW0/D8468ibgBlMjhYv9sZElVVqCnYw==", + "version": "8.11.0", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@zowe/core-for-zowe-sdk/-/core-for-zowe-sdk-8.11.0.tgz", + "integrity": "sha512-/JDaGlNL73P/HsFPnqh6052hMSsoONuHPCHk1ZFPcCoSO7Ca7yRKx2HTvHuBl8VLne/6ekK7J8FgRbdmn70pZw==", "dev": true, "dependencies": { "comment-json": "~4.2.3", @@ -2985,9 +2986,9 @@ } }, "node_modules/@zowe/cli/node_modules/@zowe/imperative": { - "version": "8.10.3", - "resolved": "https://registry.npmjs.org/@zowe/imperative/-/imperative-8.10.3.tgz", - "integrity": "sha512-nW1zIfiVFmLHfEyMnnihpokYhajotqM78bTC8FbRlZbSNNMmqRwleXDqSACLr6EZutXo9LvOX+USEtfemMt5iQ==", + "version": "8.11.0", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@zowe/imperative/-/imperative-8.11.0.tgz", + "integrity": "sha512-fC6xYibqzJiJQPLw99SRuoRH6gZRMZOtNEN76PlbCyUNEFmxaHaY75qOqRaVzp3svVdMpGB5DixQZtSUQ3PJMQ==", "dev": true, "dependencies": { "@types/yargs": "^17.0.32", @@ -3033,7 +3034,7 @@ }, "node_modules/@zowe/cli/node_modules/@zowe/imperative/node_modules/agent-base": { "version": "7.1.3", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/agent-base/-/agent-base-7.1.3.tgz", "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", "dev": true, "engines": { @@ -3042,7 +3043,7 @@ }, "node_modules/@zowe/cli/node_modules/@zowe/imperative/node_modules/diff": { "version": "5.2.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/diff/-/diff-5.2.0.tgz", "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", "dev": true, "engines": { @@ -3051,7 +3052,7 @@ }, "node_modules/@zowe/cli/node_modules/@zowe/imperative/node_modules/diff2html": { "version": "3.4.20-usewontache.1.60e7a2e", - "resolved": "https://registry.npmjs.org/diff2html/-/diff2html-3.4.20-usewontache.1.60e7a2e.tgz", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/diff2html/-/diff2html-3.4.20-usewontache.1.60e7a2e.tgz", "integrity": "sha512-0ge1jQpRv9Eg6USdIgnDIzAnuhhlgFPmhglCUBNhSVU772biWWbSu/palu0uK+PbgidjkjkajztZGVAZnD56pw==", "dev": true, "dependencies": { @@ -3067,7 +3068,7 @@ }, "node_modules/@zowe/cli/node_modules/@zowe/imperative/node_modules/diff2html/node_modules/diff": { "version": "5.1.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/diff/-/diff-5.1.0.tgz", "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==", "dev": true, "engines": { @@ -3076,7 +3077,7 @@ }, "node_modules/@zowe/cli/node_modules/@zowe/imperative/node_modules/highlight.js": { "version": "11.6.0", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.6.0.tgz", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/highlight.js/-/highlight.js-11.6.0.tgz", "integrity": "sha512-ig1eqDzJaB0pqEvlPVIpSSyMaO92bH1N2rJpLMN/nX396wTpDA4Eq0uK+7I/2XG17pFaaKE0kjV/XPeGt7Evjw==", "dev": true, "optional": true, @@ -3086,7 +3087,7 @@ }, "node_modules/@zowe/cli/node_modules/@zowe/imperative/node_modules/http-proxy-agent": { "version": "7.0.2", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", "dev": true, "dependencies": { @@ -3099,7 +3100,7 @@ }, "node_modules/@zowe/cli/node_modules/@zowe/imperative/node_modules/https-proxy-agent": { "version": "7.0.6", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", "dev": true, "dependencies": { @@ -3111,9 +3112,9 @@ } }, "node_modules/@zowe/cli/node_modules/@zowe/provisioning-for-zowe-sdk": { - "version": "8.10.3", - "resolved": "https://registry.npmjs.org/@zowe/provisioning-for-zowe-sdk/-/provisioning-for-zowe-sdk-8.10.3.tgz", - "integrity": "sha512-m99bnmr6bM6+XOTPe+6cAD8Bj/EjmL4v8KfLgc6zUYku46ifZKLoAftfEylTAkNyBTnDykc5ypLLwf/bYPOc+w==", + "version": "8.11.0", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@zowe/provisioning-for-zowe-sdk/-/provisioning-for-zowe-sdk-8.11.0.tgz", + "integrity": "sha512-5Yt4NbSiIF4OHq5JKJwf90M8eBTnL0fOXpvZgg4LqsuIhkFV9XxEfaeYyD+mPOaBzNpdWlQREqLhgbHPMY+4mw==", "dev": true, "dependencies": { "js-yaml": "^4.1.0" @@ -3127,9 +3128,9 @@ } }, "node_modules/@zowe/cli/node_modules/@zowe/secrets-for-zowe-sdk": { - "version": "8.1.2", - "resolved": "https://registry.npmjs.org/@zowe/secrets-for-zowe-sdk/-/secrets-for-zowe-sdk-8.1.2.tgz", - "integrity": "sha512-cE8rlBADL48wmiQr+fdQdxczW4wLsmv5BQa03QepKiydBBE7TLrG2Anx/F4uZ+sVIZQuN95SHvwkt8VZaTzuyw==", + "version": "8.10.4", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@zowe/secrets-for-zowe-sdk/-/secrets-for-zowe-sdk-8.10.4.tgz", + "integrity": "sha512-dsaWVGkeaYcvDd72fOuxi4c3zYoP9AYdQyaqAUu/9NyCVLZJ99B+DsUmTnTCBbr7iUJArPlGU2ydsbjf0wOR1A==", "dev": true, "hasInstallScript": true, "optional": true, @@ -3138,9 +3139,9 @@ } }, "node_modules/@zowe/cli/node_modules/@zowe/zos-console-for-zowe-sdk": { - "version": "8.10.3", - "resolved": "https://registry.npmjs.org/@zowe/zos-console-for-zowe-sdk/-/zos-console-for-zowe-sdk-8.10.3.tgz", - "integrity": "sha512-xiBycBQV6It8gYJfcORWpFITurZgxSqA+x+aOF+aoKqVlZLri47RHqos1plbu9SV5jMXnMeZ7dMHynhhjUcQmA==", + "version": "8.11.0", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@zowe/zos-console-for-zowe-sdk/-/zos-console-for-zowe-sdk-8.11.0.tgz", + "integrity": "sha512-AHFpKzQf+dxr659Wuw2NOS8KcJn2m5WDmQJUrrMV9dOP9jj4pjF5TlBZHFoxWjPZACefuS23Ybm3dvBb6lEp2A==", "dev": true, "engines": { "node": ">=18.12.0" @@ -3151,9 +3152,9 @@ } }, "node_modules/@zowe/cli/node_modules/@zowe/zos-files-for-zowe-sdk": { - "version": "8.10.3", - "resolved": "https://registry.npmjs.org/@zowe/zos-files-for-zowe-sdk/-/zos-files-for-zowe-sdk-8.10.3.tgz", - "integrity": "sha512-mikyA7hPk1PCyTHnYdCsjV30o4vXQEEn5HtWNlYCwt+LkdPEnOvNa2rHCgXYCuUd3K6/qgUHKvnV+Zr1PNNCBg==", + "version": "8.11.0", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@zowe/zos-files-for-zowe-sdk/-/zos-files-for-zowe-sdk-8.11.0.tgz", + "integrity": "sha512-CGvYtPhG16vOIIcWF+PNW9IrArMpFmUVD516m3Ic0CA2smTKWGBf2paIPeZUjZNNfNudQYGCXXpMtHri5/LIeg==", "dev": true, "dependencies": { "lodash": "^4.17.21", @@ -3168,12 +3169,12 @@ } }, "node_modules/@zowe/cli/node_modules/@zowe/zos-jobs-for-zowe-sdk": { - "version": "8.10.3", - "resolved": "https://registry.npmjs.org/@zowe/zos-jobs-for-zowe-sdk/-/zos-jobs-for-zowe-sdk-8.10.3.tgz", - "integrity": "sha512-B/A5mASv6sVos+wl6CKHMkbqUpeO2BggCLhgT+g1NwTDd0TCmXLbntaPDCAdJ67PpvgniHOyaKJLtVGT4C6vyQ==", + "version": "8.11.0", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@zowe/zos-jobs-for-zowe-sdk/-/zos-jobs-for-zowe-sdk-8.11.0.tgz", + "integrity": "sha512-S7x8GY/QZY++rlA9k0a+usJ75OIO4r3edTHkVXVnjgi/vuQptUYXNQCXYpSwjCVh5R/PkSkLSSWA0gnnVTfKdw==", "dev": true, "dependencies": { - "@zowe/zos-files-for-zowe-sdk": "8.10.3" + "@zowe/zos-files-for-zowe-sdk": "8.11.0" }, "engines": { "node": ">=18.12.0" @@ -3184,9 +3185,9 @@ } }, "node_modules/@zowe/cli/node_modules/@zowe/zos-logs-for-zowe-sdk": { - "version": "8.10.3", - "resolved": "https://registry.npmjs.org/@zowe/zos-logs-for-zowe-sdk/-/zos-logs-for-zowe-sdk-8.10.3.tgz", - "integrity": "sha512-zXntyCVV+lBh1WRAOWdJgQsUO8eb4G86tfbo1BYbkXGje7CtaJY1ng4Qa4o0GH3i2RkUGjlR4AhqV2NMVYeGqw==", + "version": "8.11.0", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@zowe/zos-logs-for-zowe-sdk/-/zos-logs-for-zowe-sdk-8.11.0.tgz", + "integrity": "sha512-EQ2kvuBWkL0zZxfFB6JgmkT7cytoz3WzfVv7WfbTy2XrB4x+cMNK2lGVj8jii+4whLgwaoVitjHq8R/7nIqGzA==", "dev": true, "engines": { "node": ">=18.12.0" @@ -3197,12 +3198,12 @@ } }, "node_modules/@zowe/cli/node_modules/@zowe/zos-tso-for-zowe-sdk": { - "version": "8.10.3", - "resolved": "https://registry.npmjs.org/@zowe/zos-tso-for-zowe-sdk/-/zos-tso-for-zowe-sdk-8.10.3.tgz", - "integrity": "sha512-fD/7N0w9PWWq85CnHvcieL5ot3UI3N2s8RSdGUhkfA1VGRJ4uLvrkCECf1QxRbbKUSaWBP538ZkgmU4wRiVWMg==", + "version": "8.11.0", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@zowe/zos-tso-for-zowe-sdk/-/zos-tso-for-zowe-sdk-8.11.0.tgz", + "integrity": "sha512-R/AUt0sNcw75c+4/hmUG0x9dKLdpWg62Q67TzEKS2nIWCD2sAE9oS5m/wTd6xrEKFFavVBAqmet2KzX6RS9hvw==", "dev": true, "dependencies": { - "@zowe/zosmf-for-zowe-sdk": "8.10.3" + "@zowe/zosmf-for-zowe-sdk": "8.11.0" }, "engines": { "node": ">=18.12.0" @@ -3213,9 +3214,9 @@ } }, "node_modules/@zowe/cli/node_modules/@zowe/zos-uss-for-zowe-sdk": { - "version": "8.10.3", - "resolved": "https://registry.npmjs.org/@zowe/zos-uss-for-zowe-sdk/-/zos-uss-for-zowe-sdk-8.10.3.tgz", - "integrity": "sha512-ivkVPtIiiNSHP3WlnGNqBx+uLa9pFEQJ8Qa5/fTpw1oHPDaP6Wu5oj1rAk0U9F+1AM80BYOpM/ioD4EVd5ufyw==", + "version": "8.11.0", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@zowe/zos-uss-for-zowe-sdk/-/zos-uss-for-zowe-sdk-8.11.0.tgz", + "integrity": "sha512-wMjKCtAZaX9E6DyVE1zHhJW+Lr79v8UJTl7p5wvCgfLmQyw7yrJOD83yYvTo/maT2E0pTe6jIuFtCnOW5Ic9Gg==", "dev": true, "dependencies": { "ssh2": "^1.15.0" @@ -3228,12 +3229,12 @@ } }, "node_modules/@zowe/cli/node_modules/@zowe/zos-workflows-for-zowe-sdk": { - "version": "8.10.3", - "resolved": "https://registry.npmjs.org/@zowe/zos-workflows-for-zowe-sdk/-/zos-workflows-for-zowe-sdk-8.10.3.tgz", - "integrity": "sha512-AoSIjJbiAq9AsLJh2+99FogrnepRuVMX8sdFpbMtZdDBEN4B5O55MeeKplTZ4w4V9ANzhPigky6C79TuHLDhCQ==", + "version": "8.11.0", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@zowe/zos-workflows-for-zowe-sdk/-/zos-workflows-for-zowe-sdk-8.11.0.tgz", + "integrity": "sha512-LJm5S6FyJ/0rgj1kaaVRo5LbZRHqymQrTLe0U7rVj5Ay5JaLMC37PpmZRjqYll6KporMQe6c/dj7cIkVZWGIJw==", "dev": true, "dependencies": { - "@zowe/zos-files-for-zowe-sdk": "8.10.3" + "@zowe/zos-files-for-zowe-sdk": "8.11.0" }, "engines": { "node": ">=18.12.0" @@ -3244,9 +3245,9 @@ } }, "node_modules/@zowe/cli/node_modules/@zowe/zosmf-for-zowe-sdk": { - "version": "8.10.3", - "resolved": "https://registry.npmjs.org/@zowe/zosmf-for-zowe-sdk/-/zosmf-for-zowe-sdk-8.10.3.tgz", - "integrity": "sha512-MdpBFXI8ntfl9clDD6YMuT4QNJJzssbEsFfzOQ2d/H8Q5REuyuq5zD/apJlJj+8pS/H88Uyrzlov73yQ8glIew==", + "version": "8.11.0", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@zowe/zosmf-for-zowe-sdk/-/zosmf-for-zowe-sdk-8.11.0.tgz", + "integrity": "sha512-CDC58MdD8vockE7y9Jnj7HcLx9kfWpak+NcuFAFOOzMQJxKGtCCXtj/2YpUQuhB9Rs2xy8YSdQMQWhCd4vQRfw==", "dev": true, "engines": { "node": ">=18.12.0" @@ -3270,7 +3271,7 @@ }, "node_modules/@zowe/cli/node_modules/ansi-regex": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "license": "MIT", @@ -3446,7 +3447,7 @@ }, "node_modules/@zowe/cli/node_modules/cross-spawn": { "version": "7.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.5.tgz", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/cross-spawn/-/cross-spawn-7.0.5.tgz", "integrity": "sha512-ZVJrKKYunU38/76t0RMOulHOnUcbU9GbpWKAOZ0mhjr7CX6FVrH+4FrAapSOekrgFQ3f/8gwMEuIft0aKq6Hug==", "dev": true, "license": "MIT", @@ -3461,13 +3462,13 @@ }, "node_modules/@zowe/cli/node_modules/cross-spawn/node_modules/isexe": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, "node_modules/@zowe/cli/node_modules/cross-spawn/node_modules/which": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, "dependencies": { @@ -3530,7 +3531,7 @@ }, "node_modules/@zowe/cli/node_modules/eastasianwidth": { "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "dev": true, "license": "MIT" @@ -3745,7 +3746,7 @@ }, "node_modules/@zowe/cli/node_modules/glob": { "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/glob/-/glob-10.4.5.tgz", "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", "dev": true, "license": "ISC", @@ -3777,7 +3778,7 @@ }, "node_modules/@zowe/cli/node_modules/glob/node_modules/minipass": { "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/minipass/-/minipass-7.1.2.tgz", "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "dev": true, "license": "ISC", @@ -3908,7 +3909,7 @@ }, "node_modules/@zowe/cli/node_modules/isexe": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/isexe/-/isexe-3.1.1.tgz", "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", "dev": true, "engines": { @@ -3917,7 +3918,7 @@ }, "node_modules/@zowe/cli/node_modules/jackspeak": { "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/jackspeak/-/jackspeak-3.4.3.tgz", "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", "dev": true, "license": "BlueOak-1.0.0", @@ -4089,7 +4090,7 @@ }, "node_modules/@zowe/cli/node_modules/micromatch": { "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/micromatch/-/micromatch-4.0.8.tgz", "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, "dependencies": { @@ -4102,7 +4103,7 @@ }, "node_modules/@zowe/cli/node_modules/minimatch": { "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/minimatch/-/minimatch-9.0.5.tgz", "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, "dependencies": { @@ -4117,7 +4118,7 @@ }, "node_modules/@zowe/cli/node_modules/minimatch/node_modules/brace-expansion": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, "dependencies": { @@ -5232,7 +5233,7 @@ "node_modules/@zowe/cli/node_modules/string-width-cjs": { "name": "string-width", "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "license": "MIT", @@ -5259,7 +5260,7 @@ "node_modules/@zowe/cli/node_modules/strip-ansi-cjs": { "name": "strip-ansi", "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "license": "MIT", @@ -5517,7 +5518,7 @@ }, "node_modules/@zowe/cli/node_modules/which": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/which/-/which-4.0.0.tgz", "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", "dev": true, "dependencies": { @@ -5557,7 +5558,7 @@ "node_modules/@zowe/cli/node_modules/wrap-ansi-cjs": { "name": "wrap-ansi", "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "license": "MIT", @@ -5631,9 +5632,9 @@ } }, "node_modules/@zowe/imperative": { - "version": "8.10.3", - "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@zowe/imperative/-/imperative-8.10.3.tgz", - "integrity": "sha512-nW1zIfiVFmLHfEyMnnihpokYhajotqM78bTC8FbRlZbSNNMmqRwleXDqSACLr6EZutXo9LvOX+USEtfemMt5iQ==", + "version": "8.11.0", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/@zowe/imperative/-/imperative-8.11.0.tgz", + "integrity": "sha512-fC6xYibqzJiJQPLw99SRuoRH6gZRMZOtNEN76PlbCyUNEFmxaHaY75qOqRaVzp3svVdMpGB5DixQZtSUQ3PJMQ==", "dev": true, "license": "EPL-2.0", "dependencies": { @@ -7173,19 +7174,19 @@ } }, "node_modules/eslint": { - "version": "9.17.0", - "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/eslint/-/eslint-9.17.0.tgz", - "integrity": "sha512-evtlNcpJg+cZLcnVKwsai8fExnqjGPicK7gnUtlNuzu+Fv9bI0aLpND5T44VLQtoMEnI57LoXO9XAkIXwohKrA==", + "version": "9.18.0", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/eslint/-/eslint-9.18.0.tgz", + "integrity": "sha512-+waTfRWQlSbpt3KWE+CjrPPYnbq9kfZIYUqapc0uBXyjTp8aYXZDsUH16m39Ryq3NjAVP4tjuF7KaukeqoCoaA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.19.0", - "@eslint/core": "^0.9.0", + "@eslint/core": "^0.10.0", "@eslint/eslintrc": "^3.2.0", - "@eslint/js": "9.17.0", - "@eslint/plugin-kit": "^0.2.3", + "@eslint/js": "9.18.0", + "@eslint/plugin-kit": "^0.2.5", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.1", @@ -7233,9 +7234,9 @@ } }, "node_modules/eslint-plugin-jest": { - "version": "28.10.0", - "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/eslint-plugin-jest/-/eslint-plugin-jest-28.10.0.tgz", - "integrity": "sha512-hyMWUxkBH99HpXT3p8hc7REbEZK3D+nk8vHXGgpB+XXsi0gO4PxMSP+pjfUzb67GnV9yawV9a53eUmcde1CCZA==", + "version": "28.11.0", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/eslint-plugin-jest/-/eslint-plugin-jest-28.11.0.tgz", + "integrity": "sha512-QAfipLcNCWLVocVbZW8GimKn5p5iiMcgGbRzz8z/P5q7xw+cNEpYqyzFMtIF/ZgF2HLOyy+dYBut+DoYolvqig==", "dev": true, "license": "MIT", "dependencies": { @@ -12038,9 +12039,9 @@ } }, "node_modules/typescript": { - "version": "5.7.2", - "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/typescript/-/typescript-5.7.2.tgz", - "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", + "version": "5.7.3", + "resolved": "https://zowe.jfrog.io/artifactory/api/npm/npm-org/typescript/-/typescript-5.7.3.tgz", + "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", "dev": true, "license": "Apache-2.0", "bin": { diff --git a/zowe-cli-id-federation-plugin/package.json b/zowe-cli-id-federation-plugin/package.json index 768338811a..1b4a7cef9b 100644 --- a/zowe-cli-id-federation-plugin/package.json +++ b/zowe-cli-id-federation-plugin/package.json @@ -49,18 +49,18 @@ "csv-parse": "5.6.0" }, "devDependencies": { - "@eslint/js": "9.17.0", + "@eslint/js": "9.18.0", "@types/jest": "29.5.14", - "@types/node": "20.17.12", - "@typescript-eslint/eslint-plugin": "8.19.1", - "@typescript-eslint/parser": "8.19.1", - "@zowe/cli": "8.10.3", - "@zowe/cli-test-utils": "8.10.3", - "@zowe/imperative": "8.10.3", + "@types/node": "20.17.14", + "@typescript-eslint/eslint-plugin": "8.20.0", + "@typescript-eslint/parser": "8.20.0", + "@zowe/cli": "8.11.0", + "@zowe/cli-test-utils": "8.11.0", + "@zowe/imperative": "8.11.0", "copyfiles": "2.4.1", "env-cmd": "10.1.0", - "eslint": "9.17.0", - "eslint-plugin-jest": "28.10.0", + "eslint": "9.18.0", + "eslint-plugin-jest": "28.11.0", "eslint-plugin-license-header": "0.6.1", "eslint-plugin-unused-imports": "4.1.4", "globals": "15.14.0", @@ -76,13 +76,13 @@ "ts-jest": "29.2.5", "ts-node": "10.9.2", "typedoc": "0.27.6", - "typescript": "5.7.2" + "typescript": "5.7.3" }, "overrides": { - "@babel/traverse": "7.26.4" + "@babel/traverse": "7.26.5" }, "peerDependencies": { - "@zowe/imperative": "8.10.3" + "@zowe/imperative": "8.11.0" }, "engines": { "npm": "=10.9.2", From 9bd52eecdbb5bb6bde112dc78ed665d71d58486d Mon Sep 17 00:00:00 2001 From: Richard Salac Date: Wed, 5 Feb 2025 10:06:44 +0100 Subject: [PATCH 06/11] chore: code cleanup Signed-off-by: Richard Salac --- .../org/zowe/apiml/zaas/zaas/ZaasAuthenticationFilter.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/zaas-service/src/main/java/org/zowe/apiml/zaas/zaas/ZaasAuthenticationFilter.java b/zaas-service/src/main/java/org/zowe/apiml/zaas/zaas/ZaasAuthenticationFilter.java index 17380d71ab..292caab8bc 100644 --- a/zaas-service/src/main/java/org/zowe/apiml/zaas/zaas/ZaasAuthenticationFilter.java +++ b/zaas-service/src/main/java/org/zowe/apiml/zaas/zaas/ZaasAuthenticationFilter.java @@ -39,11 +39,6 @@ protected void doFilterInternal(@NonNull HttpServletRequest request, @NonNull Ht Optional authSource = Optional.ofNullable((AuthSource) request.getAttribute(AUTH_SOURCE_ATTR)); if (authSource.isEmpty() || !authSourceService.isValid(authSource.get())) { throw new InsufficientAuthenticationException("Authentication failed."); -// } else { -// var auth = new PreAuthenticatedAuthenticationToken( -// authSourceService.parse(authSource.get()).getUserId(), null); -// auth.setAuthenticated(true); -// SecurityContextHolder.getContext().setAuthentication(auth); } filterChain.doFilter(request, response); } catch (RuntimeException e) { From 633ccfe0ea100f8044e4dc3d892e4b497359d48d Mon Sep 17 00:00:00 2001 From: Richard Salac Date: Wed, 5 Feb 2025 13:14:23 +0100 Subject: [PATCH 07/11] cr Signed-off-by: Richard Salac --- zaas-service/src/test/resources/application-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zaas-service/src/test/resources/application-test.yml b/zaas-service/src/test/resources/application-test.yml index 4d564e9240..2e624a2895 100644 --- a/zaas-service/src/test/resources/application-test.yml +++ b/zaas-service/src/test/resources/application-test.yml @@ -177,4 +177,4 @@ management: --- spring.config.activate.on-profile: dev -logbackServiceName: ZWEAGW1 +logbackServiceName: ZWEAZS1 From d68139f86193d6db9259a3b957cd2a298a0df333 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20Sala=C4=8D?= Date: Wed, 5 Feb 2025 13:14:51 +0100 Subject: [PATCH 08/11] Update apiml-security-common/src/test/java/org/zowe/apiml/security/common/auth/saf/SafResourceAccessSafTest.java MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Pablo Carle Signed-off-by: Richard Salač --- .../security/common/auth/saf/SafResourceAccessSafTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apiml-security-common/src/test/java/org/zowe/apiml/security/common/auth/saf/SafResourceAccessSafTest.java b/apiml-security-common/src/test/java/org/zowe/apiml/security/common/auth/saf/SafResourceAccessSafTest.java index 278507f5b7..c87c87b465 100644 --- a/apiml-security-common/src/test/java/org/zowe/apiml/security/common/auth/saf/SafResourceAccessSafTest.java +++ b/apiml-security-common/src/test/java/org/zowe/apiml/security/common/auth/saf/SafResourceAccessSafTest.java @@ -99,7 +99,7 @@ void testHasSafResourceAccess_whenUseridEmpty_thenFalse() { @ValueSource(strings = {"", "tooLongUserId"}) void testInvalidUserIds_thenSkipped(String userId) { var auth = new UsernamePasswordAuthenticationToken(userId, ""); - assertDoesNotThrow(() -> safResourceAccessVerifying.hasSafResourceAccess(auth, CLASS, RESOURCE, LEVEL.name())); + assertFalse(assertDoesNotThrow(() -> safResourceAccessVerifying.hasSafResourceAccess(auth, CLASS, RESOURCE, LEVEL.name()))); verify(checkPermissionMock, never()).checkPermission(any(), any(), any(), anyInt()); } From b1ad98f846d1db845a7cf056a9f63f367bc1dbc9 Mon Sep 17 00:00:00 2001 From: Richard Salac Date: Wed, 5 Feb 2025 16:36:42 +0100 Subject: [PATCH 09/11] chore: javadoc Signed-off-by: Richard Salac --- .../config/NewSecurityConfiguration.java | 28 +++++++++++++++++-- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/zaas-service/src/main/java/org/zowe/apiml/zaas/security/config/NewSecurityConfiguration.java b/zaas-service/src/main/java/org/zowe/apiml/zaas/security/config/NewSecurityConfiguration.java index 9068b8cb72..86f645f3c8 100644 --- a/zaas-service/src/main/java/org/zowe/apiml/zaas/security/config/NewSecurityConfiguration.java +++ b/zaas-service/src/main/java/org/zowe/apiml/zaas/security/config/NewSecurityConfiguration.java @@ -161,7 +161,7 @@ public void configure(HttpSecurity http) throws Exception { http.addFilterBefore(new CategorizeCertsFilter(publicKeyCertificatesBase64, certificateValidator), org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter.class) .addFilterBefore(loginFilter("/**", authenticationManager), org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter.class) .addFilterAfter(x509ForwardingAwareAuthenticationFilter("/**"), org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter.class) // this filter consumes certificates from custom attribute and maps them to credentials and authenticates them - .addFilterAfter(new ShouldBeAlreadyAuthenticatedFilter("/**", handlerInitializer.getAuthenticationFailureHandler()), org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter.class); // this filter stops processing of filter chaing because there is nothing on /auth/login endpoint + .addFilterAfter(new ShouldBeAlreadyAuthenticatedFilter("/**", handlerInitializer.getAuthenticationFailureHandler()), org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter.class); // this filter stops processing of filter chain because there is nothing on /auth/login endpoint } private LoginFilter loginFilter(String loginEndpoint, AuthenticationManager authenticationManager) { @@ -188,7 +188,18 @@ private LogoutHandler logoutHandler() { } /** - * Access token endpoint share single filter that handles auth with and without certificate. + * Secures endpoints: + * - /auth/access-token/generate + * + * Requires authentication by a client certificate forwarded form Gateway or basic authentication, supports credentials in header and body. + * The request is fulfilled by the filter chain only, there is no controller to handle it. + * Order of custom filters: + * - CategorizeCertsFilter - checks for forwarded client certificate and put it into a custom request attribute + * - StoreAccessTokenInfoFilter - extracts access token filter from request to a custom attribute + * - LoginFilter - attempts to log in a user using basic authentication credentials, generates access token and stops the chain on success, reply with the token + * - X509ForwardingAwareAuthenticationFilter - attempts to log in a user using forwarded client certificate, generates access token and stops the chain on success, reply with the token + * - ShouldBeAlreadyAuthenticatedFilter - stops filter chain if none of the authentications was successful + * */ @Configuration @RequiredArgsConstructor @@ -221,7 +232,7 @@ public void configure(HttpSecurity http) throws Exception { .addFilterBefore(new StoreAccessTokenInfoFilter(handlerInitializer.getUnAuthorizedHandler().getHandler()), org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter.class) .addFilterBefore(accessTokenFilter("/**", authenticationManager), org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter.class) .addFilterAfter(x509ForwardingAwareAuthenticationFilter("/**"), org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter.class) // this filter consumes certificates from custom attribute and maps them to credentials and authenticates them - .addFilterAfter(new ShouldBeAlreadyAuthenticatedFilter("/**", handlerInitializer.getAuthenticationFailureHandler()), org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter.class); // this filter stops processing of filter chaing because there is nothing on /auth/login endpoint + .addFilterAfter(new ShouldBeAlreadyAuthenticatedFilter("/**", handlerInitializer.getAuthenticationFailureHandler()), org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter.class); // this filter stops processing of filter chain because there is nothing on /auth/access-token/generate endpoint } private LoginFilter accessTokenFilter(String endpoint, AuthenticationManager authenticationManager) { @@ -241,6 +252,17 @@ private X509ForwardingAwareAuthenticationFilter x509ForwardingAwareAuthenticatio } } + /** + * Secures endpoints: + * - /auth/access-token/revoke/tokens/** + * - /auth/access-token/evict + * + * Requires authentication by a client certificate forwarded form Gateway or basic authentication, supports only credentials in header. + * Order of custom filters: + * - CategorizeCertsFilter - checks for forwarded client certificate and put it into a custom request attribute + * - X509AuthAwareFilter - attempts to log in using a user using forwarded client certificate, replaces pre-authentication in security context by the authentication result + * - BasicAuthFilter - attempts to log in a user using credentials from basic authentication header + */ @Configuration @RequiredArgsConstructor @Order(8) From 7d8edfeff85e5fa0c8d89b320801ff8f7d838f36 Mon Sep 17 00:00:00 2001 From: Richard Salac Date: Wed, 5 Feb 2025 17:26:05 +0100 Subject: [PATCH 10/11] fix: cleanup the CertificateOrAuthProtectedEndpoints security config Signed-off-by: Richard Salac --- .../config/NewSecurityConfiguration.java | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/zaas-service/src/main/java/org/zowe/apiml/zaas/security/config/NewSecurityConfiguration.java b/zaas-service/src/main/java/org/zowe/apiml/zaas/security/config/NewSecurityConfiguration.java index 86f645f3c8..0d2c064173 100644 --- a/zaas-service/src/main/java/org/zowe/apiml/zaas/security/config/NewSecurityConfiguration.java +++ b/zaas-service/src/main/java/org/zowe/apiml/zaas/security/config/NewSecurityConfiguration.java @@ -502,15 +502,6 @@ class CertificateOrAuthProtectedEndpoints { private final CompoundAuthProvider compoundAuthProvider; private final AuthenticationProvider tokenAuthenticationProvider; - private final String[] protectedEndpoints = { - SafResourceAccessController.FULL_CONTEXT_PATH, - "/application", - "/gateway/conformance", - "/gateway/api/v1/conformance", - "/gateway/validate", - "/gateway/api/v1/validate" - }; - @Bean public SecurityFilterChain certificateOrAuthEndpointsFilterChain(HttpSecurity http) throws Exception { baseConfigure( @@ -529,7 +520,7 @@ public SecurityFilterChain certificateOrAuthEndpointsFilterChain(HttpSecurity ht // filter out API ML certificate .addFilterBefore(reversedCategorizeCertFilter(), org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter.class); } else { - http.addFilterAfter(new CategorizeCertsFilter(publicKeyCertificatesBase64, certificateValidator), org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter.class); + http.x509(x509 -> x509.userDetailsService(x509UserDetailsService())); // default x509 filter, authenticates trusted cert } return http.authenticationProvider(compoundAuthProvider) // for authenticating credentials @@ -558,7 +549,7 @@ private BasicContentFilter basicFilter(AuthenticationManager authenticationManag authenticationManager, handlerInitializer.getAuthenticationFailureHandler(), handlerInitializer.getResourceAccessExceptionHandler(), - protectedEndpoints); + new String[] {"/"}); } /** @@ -570,7 +561,7 @@ private CookieContentFilter cookieFilter(AuthenticationManager authenticationMan handlerInitializer.getAuthenticationFailureHandler(), handlerInitializer.getResourceAccessExceptionHandler(), authConfigurationProperties, - protectedEndpoints); + new String[] {"/"}); } /** @@ -581,7 +572,7 @@ private BearerContentFilter bearerContentFilter(AuthenticationManager authentica authenticationManager, handlerInitializer.getAuthenticationFailureHandler(), handlerInitializer.getResourceAccessExceptionHandler(), - protectedEndpoints); + new String[] {"/"}); } } From 097d1cf067cf565768b8cf477fad0b178efb7d1a Mon Sep 17 00:00:00 2001 From: Richard Salac Date: Wed, 5 Feb 2025 17:28:28 +0100 Subject: [PATCH 11/11] remove obsolete comment Signed-off-by: Richard Salac --- .../apiml/zaas/security/config/NewSecurityConfiguration.java | 1 - 1 file changed, 1 deletion(-) diff --git a/zaas-service/src/main/java/org/zowe/apiml/zaas/security/config/NewSecurityConfiguration.java b/zaas-service/src/main/java/org/zowe/apiml/zaas/security/config/NewSecurityConfiguration.java index 0d2c064173..88da3baf33 100644 --- a/zaas-service/src/main/java/org/zowe/apiml/zaas/security/config/NewSecurityConfiguration.java +++ b/zaas-service/src/main/java/org/zowe/apiml/zaas/security/config/NewSecurityConfiguration.java @@ -619,7 +619,6 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { .authorizeHttpRequests(requests -> requests .anyRequest() .permitAll()).logout(AbstractHttpConfigurer::disable) - // sort out client and apiml internal certificates .build(); } }