From e9e809241f411e9bc2f7aa298b659474b059c98a Mon Sep 17 00:00:00 2001 From: achmelo <37397715+achmelo@users.noreply.github.com> Date: Tue, 27 Sep 2022 08:45:06 +0200 Subject: [PATCH] fix: do not require clientAuth extension (#2595) * remove duplicate certificate validation step Signed-off-by: achmelo * revert Signed-off-by: achmelo * remove import Signed-off-by: achmelo * not relevant test Signed-off-by: achmelo * imports Signed-off-by: achmelo Signed-off-by: achmelo Co-authored-by: Petr Weinfurt --- .../src/test/resources/application.yml | 2 +- .../config/ComponentsConfiguration.java | 4 +- .../login/x509/X509AbstractMapper.java | 52 ----------------- .../login/x509/X509CommonNameUserMapper.java | 14 ++--- .../login/x509/X509ExternalMapper.java | 57 +++++++++---------- .../schema/source/X509AuthSourceService.java | 20 ++----- .../apiml/acceptance/SafIdtSchemeTest.java | 7 +-- .../zowe/apiml/acceptance/X509SchemeTest.java | 20 ------- .../x509/X509CommonNameUserMapperTest.java | 16 +----- .../source/X509AuthSourceServiceTest.java | 53 ++--------------- .../source/X509CNAuthSourceServiceTest.java | 30 ++-------- .../schemes/X509SchemeTest.java | 16 ++---- .../zowe/apiml/util/config/SslContext.java | 50 ++++++++-------- 13 files changed, 87 insertions(+), 254 deletions(-) delete mode 100644 gateway-service/src/main/java/org/zowe/apiml/gateway/security/login/x509/X509AbstractMapper.java diff --git a/cloud-gateway-service/src/test/resources/application.yml b/cloud-gateway-service/src/test/resources/application.yml index 9b1b69eaf2..39c48b1bf1 100644 --- a/cloud-gateway-service/src/test/resources/application.yml +++ b/cloud-gateway-service/src/test/resources/application.yml @@ -1,7 +1,7 @@ eureka: client: serviceUrl: - defaultZone: https://localhost:10011/eureka/ + defaultZone: https://localhost:999/eureka/ apiml: service: diff --git a/gateway-service/src/main/java/org/zowe/apiml/gateway/security/config/ComponentsConfiguration.java b/gateway-service/src/main/java/org/zowe/apiml/gateway/security/config/ComponentsConfiguration.java index 0610bda030..2730d978cd 100644 --- a/gateway-service/src/main/java/org/zowe/apiml/gateway/security/config/ComponentsConfiguration.java +++ b/gateway-service/src/main/java/org/zowe/apiml/gateway/security/config/ComponentsConfiguration.java @@ -17,7 +17,7 @@ import org.springframework.context.annotation.Lazy; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.zowe.apiml.gateway.security.login.Providers; -import org.zowe.apiml.gateway.security.login.x509.X509AbstractMapper; +import org.zowe.apiml.gateway.security.login.x509.X509AuthenticationMapper; import org.zowe.apiml.gateway.security.login.x509.X509CommonNameUserMapper; import org.zowe.apiml.gateway.security.service.AuthenticationService; import org.zowe.apiml.gateway.security.service.TokenCreationService; @@ -71,7 +71,7 @@ public Providers loginProviders( */ @Bean @Qualifier("x509MFAuthSourceService") - public X509AuthSourceService getX509MFAuthSourceService(X509AbstractMapper mapper, TokenCreationService tokenCreationService, AuthenticationService authenticationService) { + public X509AuthSourceService getX509MFAuthSourceService(X509AuthenticationMapper mapper, TokenCreationService tokenCreationService, AuthenticationService authenticationService) { return new X509AuthSourceService(mapper, tokenCreationService, authenticationService); } diff --git a/gateway-service/src/main/java/org/zowe/apiml/gateway/security/login/x509/X509AbstractMapper.java b/gateway-service/src/main/java/org/zowe/apiml/gateway/security/login/x509/X509AbstractMapper.java deleted file mode 100644 index dde07454cc..0000000000 --- a/gateway-service/src/main/java/org/zowe/apiml/gateway/security/login/x509/X509AbstractMapper.java +++ /dev/null @@ -1,52 +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.gateway.security.login.x509; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.security.authentication.AuthenticationServiceException; - -import java.security.cert.CertificateParsingException; -import java.security.cert.X509Certificate; -import java.util.List; - -@Slf4j -public abstract class X509AbstractMapper implements X509AuthenticationMapper { - - private static final String CLIENT_AUTH_OID = "1.3.6.1.5.5.7.3.2"; - - /** - * Verify that the certificate is valid and that contains the OID for client authentication - * - * @param certificate - * @return - */ - public boolean isClientAuthCertificate(X509Certificate certificate) { - List extendedKeyUsage; - try { - extendedKeyUsage = certificate.getExtendedKeyUsage(); - } catch (CertificateParsingException e) { - throw new AuthenticationServiceException("Can't get extensions from certificate"); - } - if (extendedKeyUsage == null) { - log.debug("X509 certificate is missing the client certificate extended usage definition"); - return false; - } - final boolean containsOID = extendedKeyUsage.contains(CLIENT_AUTH_OID); - - if (containsOID) { - log.debug("X509 certificate does contain the client certificate extended usage definition."); - } else { - log.debug("X509 certificate is missing the client certificate extended usage definition."); - } - - return containsOID; - } -} diff --git a/gateway-service/src/main/java/org/zowe/apiml/gateway/security/login/x509/X509CommonNameUserMapper.java b/gateway-service/src/main/java/org/zowe/apiml/gateway/security/login/x509/X509CommonNameUserMapper.java index efd0d2affb..0f023bf6e4 100644 --- a/gateway-service/src/main/java/org/zowe/apiml/gateway/security/login/x509/X509CommonNameUserMapper.java +++ b/gateway-service/src/main/java/org/zowe/apiml/gateway/security/login/x509/X509CommonNameUserMapper.java @@ -28,7 +28,7 @@ @Component @ConditionalOnExpression("T(org.springframework.util.StringUtils).isEmpty('${apiml.security.x509.externalMapperUrl}')" ) -public class X509CommonNameUserMapper extends X509AbstractMapper { +public class X509CommonNameUserMapper implements X509AuthenticationMapper { /** @@ -38,13 +38,11 @@ public class X509CommonNameUserMapper extends X509AbstractMapper { * @return the user */ public String mapCertificateToMainframeUserId(X509Certificate certificate) { - if (isClientAuthCertificate(certificate)) { - String dn = certificate.getSubjectX500Principal().getName(); - LdapName ldapDN = getLdapName(dn); - for (Rdn rdn : ldapDN.getRdns()) { - if ("cn".equalsIgnoreCase(rdn.getType())) { - return String.valueOf(rdn.getValue()); - } + String dn = certificate.getSubjectX500Principal().getName(); + LdapName ldapDN = getLdapName(dn); + for (Rdn rdn : ldapDN.getRdns()) { + if ("cn".equalsIgnoreCase(rdn.getType())) { + return String.valueOf(rdn.getValue()); } } return null; diff --git a/gateway-service/src/main/java/org/zowe/apiml/gateway/security/login/x509/X509ExternalMapper.java b/gateway-service/src/main/java/org/zowe/apiml/gateway/security/login/x509/X509ExternalMapper.java index 31d99e26d1..3bbd297903 100644 --- a/gateway-service/src/main/java/org/zowe/apiml/gateway/security/login/x509/X509ExternalMapper.java +++ b/gateway-service/src/main/java/org/zowe/apiml/gateway/security/login/x509/X509ExternalMapper.java @@ -43,7 +43,7 @@ @RequiredArgsConstructor @ConditionalOnExpression("!T(org.springframework.util.StringUtils).isEmpty('${apiml.security.x509.externalMapperUrl}')" ) -public class X509ExternalMapper extends X509AbstractMapper { +public class X509ExternalMapper implements X509AuthenticationMapper { private final CloseableHttpClient httpClientProxy; private final TokenCreationService tokenCreationService; @@ -64,36 +64,33 @@ public class X509ExternalMapper extends X509AbstractMapper { */ @Override public String mapCertificateToMainframeUserId(X509Certificate certificate) { - if (isClientAuthCertificate(certificate)) { - try { - String jwtToken = tokenCreationService.createJwtTokenWithoutCredentials(externalMapperUser); - - HttpPost httpPost = new HttpPost(new URI(externalMapperUrl)); - HttpEntity httpEntity = new ByteArrayEntity(certificate.getEncoded()); - httpPost.setEntity(httpEntity); - - httpPost.setHeader(new BasicHeader("Cookie", "apimlAuthenticationToken=" + jwtToken)); - log.debug("Executing request against external mapper API: {}", httpPost.toString()); - - HttpResponse httpResponse = httpClientProxy.execute(httpPost); - String response = EntityUtils.toString(httpResponse.getEntity(), StandardCharsets.UTF_8); - log.debug("External mapper API returned: {}", response); - - if (response == null || response.isEmpty()) { - return null; - } - - ObjectMapper objectMapper = new ObjectMapper(); - CertMapperResponse certMapperResponse = objectMapper.readValue(response, CertMapperResponse.class); - return certMapperResponse.getUserId().trim(); - } catch (URISyntaxException e) { - log.error("Wrong service URI provided", e); - } catch (CertificateEncodingException e) { - log.error("Can`t get encoded data from certificate", e); - } catch (IOException e) { - log.error("Not able to send certificate to mapper", e); + try { + String jwtToken = tokenCreationService.createJwtTokenWithoutCredentials(externalMapperUser); + + HttpPost httpPost = new HttpPost(new URI(externalMapperUrl)); + HttpEntity httpEntity = new ByteArrayEntity(certificate.getEncoded()); + httpPost.setEntity(httpEntity); + + httpPost.setHeader(new BasicHeader("Cookie", "apimlAuthenticationToken=" + jwtToken)); + log.debug("Executing request against external mapper API: {}", httpPost.toString()); + + HttpResponse httpResponse = httpClientProxy.execute(httpPost); + String response = EntityUtils.toString(httpResponse.getEntity(), StandardCharsets.UTF_8); + log.debug("External mapper API returned: {}", response); + + if (response == null || response.isEmpty()) { + return null; } - return null; + + ObjectMapper objectMapper = new ObjectMapper(); + CertMapperResponse certMapperResponse = objectMapper.readValue(response, CertMapperResponse.class); + return certMapperResponse.getUserId().trim(); + } catch (URISyntaxException e) { + log.error("Wrong service URI provided", e); + } catch (CertificateEncodingException e) { + log.error("Can`t get encoded data from certificate", e); + } catch (IOException e) { + log.error("Not able to send certificate to mapper", e); } return null; } diff --git a/gateway-service/src/main/java/org/zowe/apiml/gateway/security/service/schema/source/X509AuthSourceService.java b/gateway-service/src/main/java/org/zowe/apiml/gateway/security/service/schema/source/X509AuthSourceService.java index 00e3489ae4..cd961172e1 100644 --- a/gateway-service/src/main/java/org/zowe/apiml/gateway/security/service/schema/source/X509AuthSourceService.java +++ b/gateway-service/src/main/java/org/zowe/apiml/gateway/security/service/schema/source/X509AuthSourceService.java @@ -12,7 +12,7 @@ import com.netflix.zuul.context.RequestContext; import lombok.RequiredArgsConstructor; -import org.zowe.apiml.gateway.security.login.x509.X509AbstractMapper; +import org.zowe.apiml.gateway.security.login.x509.X509AuthenticationMapper; import org.zowe.apiml.gateway.security.service.AuthenticationService; import org.zowe.apiml.gateway.security.service.TokenCreationService; import org.zowe.apiml.gateway.security.service.schema.source.AuthSource.Origin; @@ -30,7 +30,7 @@ /** * Basic implementation of AuthSourceService interface which uses client certificate as an authentication source. - * This implementation relies on concrete implementation of {@link X509AbstractMapper} for validation and parsing of + * This implementation relies on concrete implementation of {@link X509AuthenticationMapper} for validation and parsing of * the client certificate. */ @RequiredArgsConstructor @@ -38,7 +38,7 @@ public class X509AuthSourceService implements AuthSourceService { @InjectApimlLogger protected final ApimlLogger logger = ApimlLogger.empty(); - private final X509AbstractMapper mapper; + private final X509AuthenticationMapper mapper; private final TokenCreationService tokenService; private final AuthenticationService authenticationService; @@ -83,15 +83,7 @@ public boolean isValid(AuthSource authSource) { */ protected boolean isValid(X509Certificate clientCert) { logger.log(MessageType.DEBUG, "Validating X509 client certificate."); - if (clientCert == null) { - return false; - } - - if (mapper.isClientAuthCertificate(clientCert)) { - return true; - } else { - throw new AuthSchemeException("org.zowe.apiml.gateway.security.scheme.x509ExtendedKeyUsageError"); - } + return !(clientCert == null); } /** @@ -131,10 +123,10 @@ protected X509Certificate getOne(X509Certificate[] certs) { * Parse client certificate: get common name, distinguished name and encoded certificate value. * * @param clientCert {@link X509Certificate} client certificate to parse. - * @param mapper instance of {@link X509AbstractMapper} to use for parsing. + * @param mapper instance of {@link X509AuthenticationMapper} to use for parsing. * @return parsed authentication source or null in case of CertificateEncodingException. */ - private Parsed parseClientCert(X509Certificate clientCert, X509AbstractMapper mapper) { + private Parsed parseClientCert(X509Certificate clientCert, X509AuthenticationMapper mapper) { try { String commonName = mapper.mapCertificateToMainframeUserId(clientCert); String encodedCert = Base64.getEncoder().encodeToString(clientCert.getEncoded()); diff --git a/gateway-service/src/test/java/org/zowe/apiml/acceptance/SafIdtSchemeTest.java b/gateway-service/src/test/java/org/zowe/apiml/acceptance/SafIdtSchemeTest.java index 3b89872487..1133badde8 100644 --- a/gateway-service/src/test/java/org/zowe/apiml/acceptance/SafIdtSchemeTest.java +++ b/gateway-service/src/test/java/org/zowe/apiml/acceptance/SafIdtSchemeTest.java @@ -201,7 +201,7 @@ void setUp() throws Exception { } @Nested - class WhenClientAuthInExtendedKeyUsage { + class WhenClientAuthCertificate { @Test void thenValidSafIdTokenProvided() throws IOException { given() @@ -223,12 +223,11 @@ void thenValidSafIdTokenProvided() throws IOException { * client authentication then request will continue with X-Zowe-Auth-Failure header only. */ @Nested - class WhenNoClientAuthInExtendedKeyUsage { + class WhenNoClientAuthCertificate { @Test void thenNoSafIdTokenProvided() throws IOException { given() - .config(SslContext.apimlRootCert) .when() .get(basePath + serviceWithDefaultConfiguration.getPath()) .then() @@ -237,7 +236,7 @@ void thenNoSafIdTokenProvided() throws IOException { ArgumentCaptor captor = ArgumentCaptor.forClass(HttpUriRequest.class); verify(mockClient, times(1)).execute(captor.capture()); assertThat(captor.getValue().getHeaders("X-SAF-Token").length, is(0)); - assertHeaderWithValue(captor.getValue(), AUTH_FAIL_HEADER, "ZWEAG165E X509 certificate is missing the client certificate extended usage definition"); + assertHeaderWithValue(captor.getValue(), AUTH_FAIL_HEADER, "ZWEAG160E No authentication provided in the request"); } } } diff --git a/gateway-service/src/test/java/org/zowe/apiml/acceptance/X509SchemeTest.java b/gateway-service/src/test/java/org/zowe/apiml/acceptance/X509SchemeTest.java index 96ee5807f2..4e9718340d 100644 --- a/gateway-service/src/test/java/org/zowe/apiml/acceptance/X509SchemeTest.java +++ b/gateway-service/src/test/java/org/zowe/apiml/acceptance/X509SchemeTest.java @@ -126,26 +126,6 @@ private void assertHeaders(HttpUriRequest toVerify, String expectedCnHeader, Str @Nested class GivenInvalidCertificate { - @Test - void whenServerCertificate_thenNoCertDetailsInRequestHeaders() throws IOException { - String errorHeaderValue = "ZWEAG165E X509 certificate is missing the client certificate extended usage definition"; - - applicationRegistry.setCurrentApplication(serviceWithCustomConfiguration.getId()); - mockValid200HttpResponse(); - - given() - .config(SslContext.apimlRootCert) - .when() - .get(basePath + serviceWithCustomConfiguration.getPath()) - .then() - .statusCode(is(HttpStatus.SC_OK)); - - ArgumentCaptor captor = ArgumentCaptor.forClass(HttpUriRequest.class); - verify(mockClient, times(1)).execute(captor.capture()); - - assertHeaders(captor.getValue(), errorHeaderValue); - } - @Test void whenNoCertificate_thenNoCertDetailsInRequestHeaders() throws IOException { String errorHeaderValue = "ZWEAG167E No client certificate provided in the request"; diff --git a/gateway-service/src/test/java/org/zowe/apiml/gateway/security/login/x509/X509CommonNameUserMapperTest.java b/gateway-service/src/test/java/org/zowe/apiml/gateway/security/login/x509/X509CommonNameUserMapperTest.java index bbb9531fd3..3741a15efd 100644 --- a/gateway-service/src/test/java/org/zowe/apiml/gateway/security/login/x509/X509CommonNameUserMapperTest.java +++ b/gateway-service/src/test/java/org/zowe/apiml/gateway/security/login/x509/X509CommonNameUserMapperTest.java @@ -19,7 +19,6 @@ import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.doThrow; class X509CommonNameUserMapperTest { @@ -50,19 +49,6 @@ void whenWrongDN_exceptionIsThrown() { assertEquals("Not able to create ldap name from certificate. Cause: Invalid name: wrong DN", exception.getMessage()); } - @Test - void whenWrongExtension_throwException() { - X509Certificate x509Certificate = - X509Utils.getCertificate(X509Utils.correctBase64("zowe"), "CN=user,OU=CA CZ,O=Broadcom,L=Prague,ST=Czechia,C=CZ"); - try { - doThrow(new CertificateParsingException()).when(x509Certificate).getExtendedKeyUsage(); - } catch (CertificateParsingException e) { - throw new RuntimeException("Error mocking exception"); - } - Exception exception = assertThrows(AuthenticationServiceException.class, () -> x509CommonNameUserMapper.isClientAuthCertificate(x509Certificate)); - assertEquals("Can't get extensions from certificate", exception.getMessage()); - } - @Test void whenNullExtension_thenReturnFalse() { X509Certificate x509Certificate = @@ -72,7 +58,7 @@ void whenNullExtension_thenReturnFalse() { } catch (CertificateParsingException e) { throw new RuntimeException("Error mocking exception"); } - assertFalse(x509CommonNameUserMapper.isClientAuthCertificate(x509Certificate)); + } } diff --git a/gateway-service/src/test/java/org/zowe/apiml/gateway/security/service/schema/source/X509AuthSourceServiceTest.java b/gateway-service/src/test/java/org/zowe/apiml/gateway/security/service/schema/source/X509AuthSourceServiceTest.java index 3fac25a63f..3697154506 100644 --- a/gateway-service/src/test/java/org/zowe/apiml/gateway/security/service/schema/source/X509AuthSourceServiceTest.java +++ b/gateway-service/src/test/java/org/zowe/apiml/gateway/security/service/schema/source/X509AuthSourceServiceTest.java @@ -11,13 +11,12 @@ package org.zowe.apiml.gateway.security.service.schema.source; import com.netflix.zuul.context.RequestContext; -import org.assertj.core.util.Arrays; import org.junit.jupiter.api.*; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.security.authentication.AuthenticationServiceException; import org.springframework.web.client.ResourceAccessException; -import org.zowe.apiml.gateway.security.login.x509.X509AbstractMapper; +import org.zowe.apiml.gateway.security.login.x509.X509AuthenticationMapper; import org.zowe.apiml.gateway.security.service.AuthenticationService; import org.zowe.apiml.gateway.security.service.TokenCreationService; import org.zowe.apiml.gateway.security.service.schema.source.AuthSource.Origin; @@ -32,9 +31,7 @@ import java.util.Base64; import java.util.Optional; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; @ExtendWith(MockitoExtension.class) @@ -55,13 +52,13 @@ class GivenX509SourceTest { TokenCreationService tokenCreationService; X509AuthSourceService service; AuthenticationService authenticationService; - X509AbstractMapper mapper; + X509AuthenticationMapper mapper; @BeforeEach void setup() { authenticationService = mock(AuthenticationService.class); tokenCreationService = mock(TokenCreationService.class); - mapper = mock(X509AbstractMapper.class); + mapper = mock(X509AuthenticationMapper.class); service = new X509AuthSourceService(mapper, tokenCreationService, authenticationService); } @@ -84,12 +81,12 @@ void givenX509Source_thenTranslateException() { @Nested @TestInstance(TestInstance.Lifecycle.PER_CLASS) class X509MFAuthSourceServiceTest { - private X509AbstractMapper mapper; + private X509AuthenticationMapper mapper; private X509AuthSourceService serviceUnderTest; @BeforeEach void init() { - mapper = mock(X509AbstractMapper.class); + mapper = mock(X509AuthenticationMapper.class); serviceUnderTest = spy(new X509AuthSourceService(mapper, null, null)); } @@ -169,9 +166,7 @@ void whenClientCertInRequest_thenAuthSourceIsPresent() { @Test void whenValidate_thenCorrect() { - when(mapper.isClientAuthCertificate(x509Certificate)).thenReturn(true); Assertions.assertTrue(serviceUnderTest.isValid(new X509AuthSource(x509Certificate))); - verify(mapper, times(1)).isClientAuthCertificate(x509Certificate); } @Test @@ -204,18 +199,6 @@ void setup() { RequestContext.testSetCurrentContext(context); } - @Test - void whenServerCertInRequestInCustomAttribute_thenThrows() { - when(context.getRequest()).thenReturn(request); - when(request.getAttribute("client.auth.X509Certificate")).thenReturn(Arrays.array(x509Certificate)); - when(mapper.isClientAuthCertificate(any())).thenReturn(false); - - assertThrows(AuthSchemeException.class, () -> serviceUnderTest.getAuthSourceFromRequest()); - - verify(request, times(1)).getAttribute("client.auth.X509Certificate"); - verify(request, times(0)).getAttribute("javax.servlet.request.X509Certificate"); - } - @Test void whenInternalApimlCertInRequestInStandardAttribute_thenThrows() { when(context.getRequest()).thenReturn(request); @@ -232,17 +215,6 @@ void whenIncorrectAuthSourceType_thenIsValidFalse() { assertFalse(serviceUnderTest.isValid(new JwtAuthSource(""))); } - @Test - void whenAuthenticationServiceException_thenThrowsWhenValidate() { - context = spy(RequestContext.class); - RequestContext.testSetCurrentContext(context); - - AuthSource authSource = new X509AuthSource(x509Certificate); - when(mapper.isClientAuthCertificate(x509Certificate)).thenThrow(new AuthenticationServiceException("Can't get extensions from certificate")); - assertThrows(AuthenticationServiceException.class, () -> serviceUnderTest.isValid(authSource)); - verify(mapper, times(1)).isClientAuthCertificate(x509Certificate); - } - @Test void whenUnknownAuthSource_thenParsedIsNull() { assertNull(serviceUnderTest.parse(new JwtAuthSource(""))); @@ -266,19 +238,6 @@ void returnNullWhenCertificateEncodingExceptionIsThrown() throws CertificateEnco verify(mapper, times(1)).mapCertificateToMainframeUserId(x509Certificate); } - @Nested - class WhenNotAClientCertificate { - @Test - void thenThrowsWhenValidate() { - context = spy(RequestContext.class); - RequestContext.testSetCurrentContext(context); - - AuthSource authSource = new X509AuthSource(x509Certificate); - when(mapper.isClientAuthCertificate(x509Certificate)).thenReturn(false); - assertThrows(AuthSchemeException.class, () -> serviceUnderTest.isValid(authSource)); - verify(mapper, times(1)).isClientAuthCertificate(x509Certificate); - } - } } } } diff --git a/gateway-service/src/test/java/org/zowe/apiml/gateway/security/service/schema/source/X509CNAuthSourceServiceTest.java b/gateway-service/src/test/java/org/zowe/apiml/gateway/security/service/schema/source/X509CNAuthSourceServiceTest.java index a3aa06721b..6bd86e1a32 100644 --- a/gateway-service/src/test/java/org/zowe/apiml/gateway/security/service/schema/source/X509CNAuthSourceServiceTest.java +++ b/gateway-service/src/test/java/org/zowe/apiml/gateway/security/service/schema/source/X509CNAuthSourceServiceTest.java @@ -10,18 +10,7 @@ package org.zowe.apiml.gateway.security.service.schema.source; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - import com.netflix.zuul.context.RequestContext; -import java.security.cert.X509Certificate; -import java.util.Optional; -import javax.servlet.http.HttpServletRequest; import org.assertj.core.util.Arrays; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; @@ -31,6 +20,12 @@ import org.zowe.apiml.gateway.security.service.AuthenticationService; import org.zowe.apiml.gateway.security.service.TokenCreationService; +import javax.servlet.http.HttpServletRequest; +import java.security.cert.X509Certificate; +import java.util.Optional; + +import static org.mockito.Mockito.*; + class X509CNAuthSourceServiceTest { private RequestContext context; private HttpServletRequest request; @@ -54,7 +49,6 @@ void init() { void whenClientCertInRequestInCustomAttribute_thenAuthSourceIsPresent() { when(context.getRequest()).thenReturn(request); when(request.getAttribute("client.auth.X509Certificate")).thenReturn(Arrays.array(x509Certificate)); - when(mapper.isClientAuthCertificate(any())).thenReturn(true); Optional authSource = serviceUnderTest.getAuthSourceFromRequest(); @@ -66,23 +60,11 @@ void whenClientCertInRequestInCustomAttribute_thenAuthSourceIsPresent() { Assertions.assertEquals(x509Certificate, authSource.get().getRawSource()); } - @Test - void whenServerCertInRequest_thenThrows() { - when(context.getRequest()).thenReturn(request); - when(request.getAttribute("javax.servlet.request.X509Certificate")).thenReturn(Arrays.array(x509Certificate)); - when(mapper.isClientAuthCertificate(any())).thenReturn(false); - - assertThrows(AuthSchemeException.class, () -> serviceUnderTest.getAuthSourceFromRequest()); - - verify(request, times(1)).getAttribute("client.auth.X509Certificate"); - verify(request, times(1)).getAttribute("javax.servlet.request.X509Certificate"); - } @Test void whenInternalApimlCertInRequestInStandardAttribute_thenAuthSourceIsPresent() { when(context.getRequest()).thenReturn(request); when(request.getAttribute("javax.servlet.request.X509Certificate")).thenReturn(Arrays.array(x509Certificate)); - when(mapper.isClientAuthCertificate(any())).thenReturn(true); Optional authSource = serviceUnderTest.getAuthSourceFromRequest(); diff --git a/integration-tests/src/test/java/org/zowe/apiml/integration/authentication/schemes/X509SchemeTest.java b/integration-tests/src/test/java/org/zowe/apiml/integration/authentication/schemes/X509SchemeTest.java index 69b1780c2a..8aefaf9a78 100644 --- a/integration-tests/src/test/java/org/zowe/apiml/integration/authentication/schemes/X509SchemeTest.java +++ b/integration-tests/src/test/java/org/zowe/apiml/integration/authentication/schemes/X509SchemeTest.java @@ -11,7 +11,9 @@ package org.zowe.apiml.integration.authentication.schemes; import io.restassured.http.Header; -import org.junit.jupiter.api.*; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; import org.zowe.apiml.util.TestWithStartedInstances; import org.zowe.apiml.util.categories.DiscoverableClientDependentTest; import org.zowe.apiml.util.categories.X509Test; @@ -24,7 +26,7 @@ import static io.restassured.RestAssured.given; import static org.hamcrest.CoreMatchers.startsWith; import static org.hamcrest.core.Is.is; -import static org.zowe.apiml.util.requests.Endpoints.*; +import static org.zowe.apiml.util.requests.Endpoints.X509_ENDPOINT; /** * Use Discoverable Client to verify that when the x509 certificate is used for the call to the southbound service @@ -83,16 +85,6 @@ void givenSelfSignedUntrustedCertificate_andMaliciousHeaderInRequest_thenEmptyBo .body("cn", is("")).statusCode(200); } - @Test - void givenServerCertificateInRequest() { - given() - .config(SslContext.apimlRootCert) - .when() - .get(X509SchemeTest.URL) - .then() - .header("X-Zowe-Auth-Failure", is("ZWEAG165E X509 certificate is missing the client certificate extended usage definition")).statusCode(200); - } - @Test void givenNoCertificate_thenEmptyBodyIsReturned() { given() diff --git a/integration-tests/src/testFixtures/java/org/zowe/apiml/util/config/SslContext.java b/integration-tests/src/testFixtures/java/org/zowe/apiml/util/config/SslContext.java index 8e93697673..2e6046c86f 100644 --- a/integration-tests/src/testFixtures/java/org/zowe/apiml/util/config/SslContext.java +++ b/integration-tests/src/testFixtures/java/org/zowe/apiml/util/config/SslContext.java @@ -1,14 +1,14 @@ -/* - * 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.util.config; +/* + * 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.util.config; import io.restassured.config.RestAssuredConfig; import io.restassured.config.SSLConfig; @@ -41,23 +41,23 @@ public class SslContext { private static AtomicBoolean isInitialized = new AtomicBoolean(false); private static AtomicReference configurer = new AtomicReference<>(); - public synchronized static void prepareSslAuthentication(SslContextConfigurer providedCconfigurer) throws Exception { + public synchronized static void prepareSslAuthentication(SslContextConfigurer providedConfigurer) throws Exception { - if (configurer.get() != null && !configurer.get().equals(providedCconfigurer)) { + if (configurer.get() != null && !configurer.get().equals(providedConfigurer)) { throw new IllegalStateException("You cannot initialize this class twice with different configuration"); } if (!isInitialized.get()) { - configurer.set(providedCconfigurer); - X509HostnameVerifier hostnameVerifier = providedCconfigurer.getHostnameVerifier(); + configurer.set(providedConfigurer); + X509HostnameVerifier hostnameVerifier = providedConfigurer.getHostnameVerifier(); log.info("SSLContext is constructing. This should happen only once."); TrustStrategy trustStrategy = (X509Certificate[] chain, String authType) -> true; SSLContext sslContext = SSLContextBuilder .create() - .loadKeyMaterial(ResourceUtils.getFile(providedCconfigurer.getKeystoreLocalhostJks()), - providedCconfigurer.getKeystorePassword(), providedCconfigurer.getKeystorePassword(), + .loadKeyMaterial(ResourceUtils.getFile(providedConfigurer.getKeystoreLocalhostJks()), + providedConfigurer.getKeystorePassword(), providedConfigurer.getKeystorePassword(), (Map aliases, Socket socket) -> "apimtst") .loadTrustMaterial(null, trustStrategy) .build(); @@ -66,8 +66,8 @@ public synchronized static void prepareSslAuthentication(SslContextConfigurer pr SSLContext sslContext2 = SSLContextBuilder .create() - .loadKeyMaterial(ResourceUtils.getFile(providedCconfigurer.getKeystore()), - providedCconfigurer.getKeystorePassword(), providedCconfigurer.getKeystorePassword()) + .loadKeyMaterial(ResourceUtils.getFile(providedConfigurer.getKeystore()), + providedConfigurer.getKeystorePassword(), providedConfigurer.getKeystorePassword()) .loadTrustMaterial(null, trustStrategy) .build(); clientCertApiml = RestAssuredConfig.newConfig().sslConfig(new SSLConfig().sslSocketFactory(new SSLSocketFactory(sslContext2, hostnameVerifier))); @@ -79,8 +79,8 @@ public synchronized static void prepareSslAuthentication(SslContextConfigurer pr tlsWithoutCert = RestAssuredConfig.newConfig().sslConfig(new SSLConfig().sslSocketFactory(new SSLSocketFactory(sslContext3, hostnameVerifier))); SSLContext sslContext4 = SSLContextBuilder .create() - .loadKeyMaterial(ResourceUtils.getFile(providedCconfigurer.getKeystoreLocalhostJks()), - providedCconfigurer.getKeystorePassword(), providedCconfigurer.getKeystorePassword(), + .loadKeyMaterial(ResourceUtils.getFile(providedConfigurer.getKeystoreLocalhostJks()), + providedConfigurer.getKeystorePassword(), providedConfigurer.getKeystorePassword(), (Map aliases, Socket socket) -> "unknownuser") .loadTrustMaterial(null, trustStrategy) .build(); @@ -88,8 +88,8 @@ public synchronized static void prepareSslAuthentication(SslContextConfigurer pr SSLContext sslContext5 = SSLContextBuilder .create() - .loadKeyMaterial(ResourceUtils.getFile(providedCconfigurer.getKeystoreLocalhostJks()), - providedCconfigurer.getKeystorePassword(), providedCconfigurer.getKeystorePassword(), + .loadKeyMaterial(ResourceUtils.getFile(providedConfigurer.getKeystoreLocalhostJks()), + providedConfigurer.getKeystorePassword(), providedConfigurer.getKeystorePassword(), (Map aliases, Socket socket) -> "user") .loadTrustMaterial(null, trustStrategy) .build(); @@ -135,8 +135,8 @@ public synchronized static void prepareSslAuthentication(SslContextConfigurer pr SSLContext sslContext7 = SSLContextBuilder .create() - .loadKeyMaterial(ResourceUtils.getFile(providedCconfigurer.getKeystoreLocalhostJks()), - providedCconfigurer.getKeystorePassword(), providedCconfigurer.getKeystorePassword(), + .loadKeyMaterial(ResourceUtils.getFile(providedConfigurer.getKeystoreLocalhostJks()), + providedConfigurer.getKeystorePassword(), providedConfigurer.getKeystorePassword(), (Map aliases, Socket socket) -> "apiml external certificate authority") .loadTrustMaterial(null, trustStrategy) .build();