diff options
32 files changed, 818 insertions, 187 deletions
diff --git a/certService/src/main/java/org/onap/aaf/certservice/api/CertificationService.java b/certService/src/main/java/org/onap/aaf/certservice/api/CertificationController.java index 945fc6d1..e663909c 100644 --- a/certService/src/main/java/org/onap/aaf/certservice/api/CertificationService.java +++ b/certService/src/main/java/org/onap/aaf/certservice/api/CertificationController.java @@ -39,15 +39,15 @@ import org.springframework.web.bind.annotation.RestController; @RestController -public class CertificationService { +public class CertificationController { - private static final Logger LOGGER = LoggerFactory.getLogger(CertificationService.class); + private static final Logger LOGGER = LoggerFactory.getLogger(CertificationController.class); private final CsrModelFactory csrModelFactory; private final CertificationModelFactory certificationModelFactory; @Autowired - CertificationService(CsrModelFactory csrModelFactory, CertificationModelFactory certificationModelFactory) { + CertificationController(CsrModelFactory csrModelFactory, CertificationModelFactory certificationModelFactory) { this.csrModelFactory = csrModelFactory; this.certificationModelFactory = certificationModelFactory; } @@ -81,5 +81,4 @@ public class CertificationService { } - } diff --git a/certService/src/main/java/org/onap/aaf/certservice/api/ReloadConfigController.java b/certService/src/main/java/org/onap/aaf/certservice/api/ReloadConfigController.java new file mode 100644 index 00000000..5390a00c --- /dev/null +++ b/certService/src/main/java/org/onap/aaf/certservice/api/ReloadConfigController.java @@ -0,0 +1,58 @@ +/* + * ============LICENSE_START======================================================= + * PROJECT + * ================================================================================ + * Copyright (C) 2020 Nokia. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.aaf.certservice.api; + +import org.onap.aaf.certservice.certification.configuration.CmpServersConfig; +import org.onap.aaf.certservice.certification.configuration.CmpServersConfigLoadingException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class ReloadConfigController { + + private static final Logger LOGGER = LoggerFactory.getLogger(ReloadConfigController.class); + + private final CmpServersConfig cmpServersConfig; + + @Autowired + public ReloadConfigController(CmpServersConfig cmpServersConfig) { + this.cmpServersConfig = cmpServersConfig; + } + + @GetMapping("/reload") + public ResponseEntity<String> reloadConfiguration() throws CmpServersConfigLoadingException { + cmpServersConfig.reloadConfiguration(); + return new ResponseEntity<>(HttpStatus.OK); + } + + @ExceptionHandler(value = CmpServersConfigLoadingException.class) + public ResponseEntity<String> handle(CmpServersConfigLoadingException exception) { + LOGGER.error(exception.getMessage(), exception.getCause()); + return new ResponseEntity<>(exception.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR); + } + +} diff --git a/certService/src/main/java/org/onap/aaf/certservice/certification/configuration/CmpServersConfig.java b/certService/src/main/java/org/onap/aaf/certservice/certification/configuration/CmpServersConfig.java index 25e69251..a304b5a6 100644 --- a/certService/src/main/java/org/onap/aaf/certservice/certification/configuration/CmpServersConfig.java +++ b/certService/src/main/java/org/onap/aaf/certservice/certification/configuration/CmpServersConfig.java @@ -20,34 +20,31 @@ package org.onap.aaf.certservice.certification.configuration; +import java.io.File; +import java.util.Collections; +import java.util.List; +import javax.annotation.PostConstruct; import org.onap.aaf.certservice.certification.configuration.model.Cmpv2Server; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; -import org.springframework.cloud.context.config.annotation.RefreshScope; -import org.springframework.cloud.context.scope.refresh.RefreshScopeRefreshedEvent; import org.springframework.context.annotation.Configuration; -import javax.annotation.PostConstruct; -import java.io.File; -import java.util.Collections; -import java.util.List; -import org.springframework.context.event.EventListener; - @Configuration public class CmpServersConfig { private static final Logger LOGGER = LoggerFactory.getLogger(CmpServersConfig.class); + private static final String LOADING_SUCCESS_MESSAGE = "CMP Servers configuration successfully loaded from file {}"; private static final String CMP_SERVERS_CONFIG_FILENAME = "cmpServers.json"; + private static final String INIT_CONFIGURATION = "Loading initial configuration"; private static final String REFRESHING_CONFIGURATION = "Refreshing configuration"; - private String configPath; - private List<Cmpv2Server> cmpServers; + private final String configPath; private final CmpServersConfigLoader cmpServersConfigLoader; + private List<Cmpv2Server> cmpServers; + @Autowired public CmpServersConfig(@Value("${app.config.path}") String configPath, CmpServersConfigLoader cmpServersConfigLoader) { @@ -56,18 +53,28 @@ public class CmpServersConfig { } @PostConstruct - void loadConfiguration() { - String configFilePath = configPath + File.separator + CMP_SERVERS_CONFIG_FILENAME; - this.cmpServers = Collections.unmodifiableList(cmpServersConfigLoader.load(configFilePath)); + void init() { + LOGGER.info(INIT_CONFIGURATION); + try { + loadConfiguration(); + } catch (CmpServersConfigLoadingException e) { + LOGGER.error(e.getMessage(), e.getCause()); + } } - @EventListener - public void onRefreshScope(final RefreshScopeRefreshedEvent event) { + public void reloadConfiguration() throws CmpServersConfigLoadingException { LOGGER.info(REFRESHING_CONFIGURATION); loadConfiguration(); } + void loadConfiguration() throws CmpServersConfigLoadingException { + String configFilePath = configPath + File.separator + CMP_SERVERS_CONFIG_FILENAME; + this.cmpServers = Collections.unmodifiableList(cmpServersConfigLoader.load(configFilePath)); + LOGGER.info(LOADING_SUCCESS_MESSAGE, configFilePath); + } + public List<Cmpv2Server> getCmpServers() { return cmpServers; } + } diff --git a/certService/src/main/java/org/onap/aaf/certservice/certification/configuration/CmpServersConfigLoader.java b/certService/src/main/java/org/onap/aaf/certservice/certification/configuration/CmpServersConfigLoader.java index 94530100..1072d630 100644 --- a/certService/src/main/java/org/onap/aaf/certservice/certification/configuration/CmpServersConfigLoader.java +++ b/certService/src/main/java/org/onap/aaf/certservice/certification/configuration/CmpServersConfigLoader.java @@ -24,20 +24,18 @@ import com.fasterxml.jackson.databind.ObjectMapper; import java.io.File; import java.io.IOException; import java.security.InvalidParameterException; -import java.util.ArrayList; import java.util.List; import org.onap.aaf.certservice.certification.configuration.model.CmpServers; import org.onap.aaf.certservice.certification.configuration.model.Cmpv2Server; import org.onap.aaf.certservice.certification.configuration.validation.Cmpv2ServerConfigurationValidator; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component class CmpServersConfigLoader { - private static final Logger LOGGER = LoggerFactory.getLogger(CmpServersConfigLoader.class); + private static final String LOADING_EXCEPTION_MESSAGE = "Exception occurred during CMP Servers configuration loading"; + private static final String VALIDATION_EXCEPTION_MESSAGE = "Validation of CMPv2 servers configuration failed"; private final Cmpv2ServerConfigurationValidator validator; @@ -46,19 +44,16 @@ class CmpServersConfigLoader { this.validator = validator; } - List<Cmpv2Server> load(String path) { - List<Cmpv2Server> servers = new ArrayList<>(); + List<Cmpv2Server> load(String path) throws CmpServersConfigLoadingException { try { - servers = loadConfigFromFile(path).getCmpv2Servers(); + List<Cmpv2Server> servers = loadConfigFromFile(path).getCmpv2Servers(); servers.forEach(validator::validate); - LOGGER.info("CMP Servers configuration successfully loaded from file {}", path); + return servers; } catch (IOException e) { - LOGGER.error("Exception occurred during CMP Servers configuration loading: ", e); + throw new CmpServersConfigLoadingException(LOADING_EXCEPTION_MESSAGE, e); } catch (InvalidParameterException e) { - LOGGER.error("Validation of CMPv2 servers configuration failed:", e); + throw new CmpServersConfigLoadingException(VALIDATION_EXCEPTION_MESSAGE, e); } - - return servers; } private CmpServers loadConfigFromFile(String path) throws IOException { diff --git a/certService/src/main/java/org/onap/aaf/certservice/certification/configuration/CmpServersConfigLoadingException.java b/certService/src/main/java/org/onap/aaf/certservice/certification/configuration/CmpServersConfigLoadingException.java new file mode 100644 index 00000000..b7c3638d --- /dev/null +++ b/certService/src/main/java/org/onap/aaf/certservice/certification/configuration/CmpServersConfigLoadingException.java @@ -0,0 +1,32 @@ +/* + * ============LICENSE_START======================================================= + * PROJECT + * ================================================================================ + * Copyright (C) 2020 Nokia. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.aaf.certservice.certification.configuration; + +public class CmpServersConfigLoadingException extends Exception { + + public CmpServersConfigLoadingException(String message) { + super(message); + } + + public CmpServersConfigLoadingException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/certService/src/main/java/org/onap/aaf/certservice/certification/configuration/model/Authentication.java b/certService/src/main/java/org/onap/aaf/certservice/certification/configuration/model/Authentication.java index af254d61..3785cf8e 100644 --- a/certService/src/main/java/org/onap/aaf/certservice/certification/configuration/model/Authentication.java +++ b/certService/src/main/java/org/onap/aaf/certservice/certification/configuration/model/Authentication.java @@ -20,12 +20,15 @@ package org.onap.aaf.certservice.certification.configuration.model; +import javax.validation.constraints.NotNull; import org.hibernate.validator.constraints.Length; public class Authentication { + @NotNull @Length(min = 1, max = 256) private String iak; + @NotNull @Length(min = 1, max = 256) private String rv; diff --git a/certService/src/main/java/org/onap/aaf/certservice/certification/configuration/model/Cmpv2Server.java b/certService/src/main/java/org/onap/aaf/certservice/certification/configuration/model/Cmpv2Server.java index 9f8f9796..20b83b82 100644 --- a/certService/src/main/java/org/onap/aaf/certservice/certification/configuration/model/Cmpv2Server.java +++ b/certService/src/main/java/org/onap/aaf/certservice/certification/configuration/model/Cmpv2Server.java @@ -20,19 +20,23 @@ package org.onap.aaf.certservice.certification.configuration.model; +import javax.validation.Valid; +import javax.validation.constraints.NotNull; import org.bouncycastle.asn1.x500.X500Name; import org.hibernate.validator.constraints.Length; import org.onap.aaf.certservice.certification.configuration.validation.constraints.Cmpv2URL; -import javax.validation.Valid; - public class Cmpv2Server { + @NotNull @Valid private Authentication authentication; + @NotNull private CaMode caMode; + @NotNull @Length(min = 1, max = 128) private String caName; + @NotNull private X500Name issuerDN; @Cmpv2URL private String url; diff --git a/certService/src/main/resources/application.properties b/certService/src/main/resources/application.properties index ac81c3a0..aac9b96f 100644 --- a/certService/src/main/resources/application.properties +++ b/certService/src/main/resources/application.properties @@ -3,7 +3,7 @@ springdoc.show-actuator=true management.endpoints.enabled-by-default=true management.endpoint.configprops.enabled=true -management.endpoints.web.exposure.include=refresh,health +management.endpoints.web.exposure.include=health # Swagger configuration diff --git a/certService/src/test/java/org/onap/aaf/certservice/api/CertificationServiceTest.java b/certService/src/test/java/org/onap/aaf/certservice/api/CertificationControllerTest.java index ee1ce1ef..2fc6d665 100644 --- a/certService/src/test/java/org/onap/aaf/certservice/api/CertificationServiceTest.java +++ b/certService/src/test/java/org/onap/aaf/certservice/api/CertificationControllerTest.java @@ -20,11 +20,21 @@ package org.onap.aaf.certservice.api; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +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.when; + import com.google.gson.Gson; +import java.io.IOException; +import java.util.Arrays; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; -import org.mockito.MockitoAnnotations; +import org.mockito.junit.jupiter.MockitoExtension; import org.onap.aaf.certservice.certification.CertificationModelFactory; import org.onap.aaf.certservice.certification.CsrModelFactory; import org.onap.aaf.certservice.certification.CsrModelFactory.StringBase64; @@ -36,20 +46,11 @@ import org.onap.aaf.certservice.certification.model.CsrModel; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import java.io.IOException; -import java.util.Arrays; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertEquals; -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.when; - -class CertificationServiceTest { +@ExtendWith(MockitoExtension.class) +class CertificationControllerTest { - private CertificationService certificationService; + private CertificationController certificationController; @Mock private CsrModelFactory csrModelFactory; @@ -58,9 +59,8 @@ class CertificationServiceTest { private CertificationModelFactory certificationModelFactory; @BeforeEach - void serUp() { - MockitoAnnotations.initMocks(this); - certificationService = new CertificationService(csrModelFactory, certificationModelFactory); + void setUp() { + certificationController = new CertificationController(csrModelFactory, certificationModelFactory); } @Test @@ -81,7 +81,7 @@ class CertificationServiceTest { // when ResponseEntity<String> testResponse = - certificationService.signCertificate(testCaName, "encryptedCSR", "encryptedPK"); + certificationController.signCertificate(testCaName, "encryptedCSR", "encryptedPK"); CertificationModel responseCertificationModel = new Gson().fromJson(testResponse.getBody(), CertificationModel.class); @@ -101,7 +101,7 @@ class CertificationServiceTest { // when Exception exception = assertThrows( - CsrDecryptionException.class, () -> certificationService. + CsrDecryptionException.class, () -> certificationController. signCertificate("TestCa", "encryptedCSR", "encryptedPK") ); @@ -120,7 +120,7 @@ class CertificationServiceTest { // when Exception exception = assertThrows( - KeyDecryptionException.class, () -> certificationService. + KeyDecryptionException.class, () -> certificationController. signCertificate("TestCa", "encryptedCSR", "encryptedPK") ); diff --git a/certService/src/test/java/org/onap/aaf/certservice/api/ReloadConfigControllerTest.java b/certService/src/test/java/org/onap/aaf/certservice/api/ReloadConfigControllerTest.java new file mode 100644 index 00000000..17db24b8 --- /dev/null +++ b/certService/src/test/java/org/onap/aaf/certservice/api/ReloadConfigControllerTest.java @@ -0,0 +1,89 @@ +/* + * ============LICENSE_START======================================================= + * PROJECT + * ================================================================================ + * Copyright (C) 2020 Nokia. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.aaf.certservice.api; + +import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.doThrow; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.onap.aaf.certservice.certification.configuration.CmpServersConfig; +import org.onap.aaf.certservice.certification.configuration.CmpServersConfigLoadingException; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; + +@ExtendWith(MockitoExtension.class) +public class ReloadConfigControllerTest { + + private static final String ERROR_MESSAGE = "Exception occurred during CMP Servers configuration loading"; + + private ReloadConfigController reloadConfigController; + + @Mock + public CmpServersConfig cmpServersConfig; + + @BeforeEach + public void setUp() { + this.reloadConfigController = new ReloadConfigController(cmpServersConfig); + } + + @Test + public void shouldReturnStatusOkWhenSuccessfullyReloaded() throws CmpServersConfigLoadingException { + // When + ResponseEntity<String> response = reloadConfigController.reloadConfiguration(); + + // Then + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); + } + + @Test + public void shouldRethrowSameErrorWhenFailedToReload() throws CmpServersConfigLoadingException { + // Given + doThrow(new CmpServersConfigLoadingException(ERROR_MESSAGE)).when(cmpServersConfig).reloadConfiguration(); + + // When + Exception exception = assertThrows( + CmpServersConfigLoadingException.class, + () -> reloadConfigController.reloadConfiguration()); + + // Then + Assertions.assertThat(exception.getMessage()).isEqualTo(ERROR_MESSAGE); + } + + @Test + void shouldReturnErrorStatusAndMessageWhenExceptionOccurred() { + // Given + CmpServersConfigLoadingException exception = new CmpServersConfigLoadingException(ERROR_MESSAGE); + + // When + ResponseEntity<String> response = reloadConfigController.handle(exception); + + // Then + assertEquals(ERROR_MESSAGE, response.getBody()); + assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode()); + } +} diff --git a/certService/src/test/java/org/onap/aaf/certservice/certification/configuration/CmpServersConfigLoaderTest.java b/certService/src/test/java/org/onap/aaf/certservice/certification/configuration/CmpServersConfigLoaderTest.java index d14dc7b7..c78f6ceb 100644 --- a/certService/src/test/java/org/onap/aaf/certservice/certification/configuration/CmpServersConfigLoaderTest.java +++ b/certService/src/test/java/org/onap/aaf/certservice/certification/configuration/CmpServersConfigLoaderTest.java @@ -20,26 +20,26 @@ package org.onap.aaf.certservice.certification.configuration; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.List; +import java.util.Map; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.onap.aaf.certservice.CertServiceApplication; import org.onap.aaf.certservice.certification.configuration.model.Cmpv2Server; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.PropertySource; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; -import java.io.IOException; -import java.util.List; -import java.util.Map; - -import static org.assertj.core.api.Assertions.assertThat; - @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = CertServiceApplication.class) class CmpServersConfigLoaderTest { private static final String EXISTING_CONFIG_FILENAME = "cmpServers.json"; - private static final String NONEXISTING_CONFIG_FILENAME = "nonExisting_cmpServers.json"; + private static final String INVALID_CONFIG_FILENAME = "invalidCmpServers.json"; + private static final String NONEXISTENT_CONFIG_FILENAME = "nonExisting_cmpServers.json"; + private static final Map<String, String> EXPECTED_FIRST_CMP_SERVER = Map.of( "CA_NAME", "TEST", "URL", "http://127.0.0.1/ejbca/publicweb/cmp/cmp", @@ -61,9 +61,9 @@ class CmpServersConfigLoaderTest { private CmpServersConfigLoader configLoader; @Test - void shouldLoadCmpServersConfigWhenFileAvailable() { + void shouldLoadCmpServersConfigWhenFileAvailable() throws CmpServersConfigLoadingException { // Given - String path = getClass().getClassLoader().getResource(EXISTING_CONFIG_FILENAME).getFile(); + String path = getResourcePath(EXISTING_CONFIG_FILENAME); // When List<Cmpv2Server> cmpServers = configLoader.load(path); @@ -76,13 +76,32 @@ class CmpServersConfigLoaderTest { } @Test - void shouldReturnEmptyListWhenFileMissing() { + void shouldThrowExceptionWhenFileMissing() { // When - List<Cmpv2Server> cmpServers = configLoader.load(NONEXISTING_CONFIG_FILENAME); + Exception exception = assertThrows( + CmpServersConfigLoadingException.class, + () -> configLoader.load(NONEXISTENT_CONFIG_FILENAME)); // Then - assertThat(cmpServers).isNotNull(); - assertThat(cmpServers).isEmpty(); + assertThat(exception.getMessage()).contains("Exception occurred during CMP Servers configuration loading"); + } + + @Test + void shouldThrowExceptionWhenConfigurationIsInvalid() { + // Given + String path = getResourcePath(INVALID_CONFIG_FILENAME); + + // When + Exception exception = assertThrows( + CmpServersConfigLoadingException.class, + () -> configLoader.load(path)); + + // Then + assertThat(exception.getMessage()).contains("Validation of CMPv2 servers configuration failed"); + } + + private String getResourcePath(String invalidConfigFilename) { + return getClass().getClassLoader().getResource(invalidConfigFilename).getFile(); } private void verifyThatCmpServerEquals(Cmpv2Server cmpv2Server, Map<String, String> expected) { diff --git a/certService/src/test/java/org/onap/aaf/certservice/certification/configuration/CmpServersConfigTest.java b/certService/src/test/java/org/onap/aaf/certservice/certification/configuration/CmpServersConfigTest.java index 7184384c..5d850fe9 100644 --- a/certService/src/test/java/org/onap/aaf/certservice/certification/configuration/CmpServersConfigTest.java +++ b/certService/src/test/java/org/onap/aaf/certservice/certification/configuration/CmpServersConfigTest.java @@ -20,6 +20,12 @@ package org.onap.aaf.certservice.certification.configuration; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.startsWith; + +import java.util.List; import org.bouncycastle.asn1.x500.X500Name; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -30,19 +36,12 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.onap.aaf.certservice.certification.configuration.model.Authentication; import org.onap.aaf.certservice.certification.configuration.model.CaMode; import org.onap.aaf.certservice.certification.configuration.model.Cmpv2Server; -import org.springframework.cloud.context.scope.refresh.RefreshScopeRefreshedEvent; - -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.startsWith; @ExtendWith(MockitoExtension.class) class CmpServersConfigTest { + private static final String ERROR_MESSAGE = "Exception occurred during CMP Servers configuration loading"; private static final String APP_CONFIG_PATH = "/fake/path/to/config"; - private static final List<Cmpv2Server> SAMPLE_CMP_SERVERS = generateTestConfiguration(); @Mock @@ -56,16 +55,19 @@ class CmpServersConfigTest { } @Test - void shouldCallLoaderWithPathFromPropertiesWhenCreated() { - this.cmpServersConfig.loadConfiguration(); // Manual PostConstruct call + void shouldCallLoaderWithPathFromPropertiesWhenCreated() throws CmpServersConfigLoadingException { + // When + this.cmpServersConfig.init(); // Manual PostConstruct call + + // Then Mockito.verify(cmpServersConfigLoader).load(startsWith(APP_CONFIG_PATH)); } @Test - void shouldReturnLoadedServersWhenGetCalled() { + void shouldReturnLoadedServersWhenGetCalled() throws CmpServersConfigLoadingException { // Given Mockito.when(cmpServersConfigLoader.load(any())).thenReturn(SAMPLE_CMP_SERVERS); - this.cmpServersConfig.loadConfiguration(); // Manual PostConstruct call + this.cmpServersConfig.init(); // Manual PostConstruct call // When List<Cmpv2Server> receivedCmpServers = this.cmpServersConfig.getCmpServers(); @@ -75,16 +77,14 @@ class CmpServersConfigTest { } @Test - void shouldReturnLoadedServersAfterRefreshWhenGetCalled() { + void shouldReturnLoadedServersAfterReloadWhenGetCalled() throws CmpServersConfigLoadingException { // Given Mockito.when(cmpServersConfigLoader.load(any())).thenReturn(SAMPLE_CMP_SERVERS); - List<Cmpv2Server> receivedCmpServers = this.cmpServersConfig.getCmpServers(); assertThat(receivedCmpServers).isNull(); - this.cmpServersConfig.onRefreshScope(new RefreshScopeRefreshedEvent()); - // When + this.cmpServersConfig.reloadConfiguration(); receivedCmpServers = this.cmpServersConfig.getCmpServers(); // Then @@ -92,20 +92,71 @@ class CmpServersConfigTest { } @Test - void shouldNotReturnIakAndRvWhenToStringMethodIsUsed() { + void shouldRethrowExceptionWhenReloaded() throws CmpServersConfigLoadingException { + // Given + Mockito.when(cmpServersConfigLoader.load(any())).thenThrow(new CmpServersConfigLoadingException( + ERROR_MESSAGE)); + + // Then + assertThrows( + CmpServersConfigLoadingException.class, + () -> cmpServersConfig.reloadConfiguration()); + } + + @Test + void shouldPassMessageToRethrownErrorWhenReloadingFails() throws CmpServersConfigLoadingException { + // Given + Mockito.when(cmpServersConfigLoader.load(any())).thenThrow(new CmpServersConfigLoadingException(ERROR_MESSAGE)); + + // When + Exception exception = assertThrows( + CmpServersConfigLoadingException.class, + () -> cmpServersConfig.reloadConfiguration()); + + // Then + assertThat(exception.getMessage()).isEqualTo(ERROR_MESSAGE); + } + + @Test + void shouldNotReturnIakAndRvWhenToStringMethodIsUsed() throws CmpServersConfigLoadingException { // Given Mockito.when(cmpServersConfigLoader.load(any())).thenReturn(SAMPLE_CMP_SERVERS); - this.cmpServersConfig.loadConfiguration(); // Manual PostConstruct call + this.cmpServersConfig.init(); // Manual PostConstruct call // When List<Cmpv2Server> receivedCmpServers = this.cmpServersConfig.getCmpServers(); // Then - receivedCmpServers.forEach((server)-> assertThat(server.toString()) - .doesNotContain( - server.getAuthentication().getIak(), - server.getAuthentication().getRv() - )); + receivedCmpServers.forEach((server) -> assertThat(server.toString()) + .doesNotContain( + server.getAuthentication().getIak(), + server.getAuthentication().getRv() + )); + } + + @Test + void shouldRethrowErrorWhenLoadingFails() throws CmpServersConfigLoadingException { + // Given + Mockito.when(cmpServersConfigLoader.load(any())).thenThrow(new CmpServersConfigLoadingException(ERROR_MESSAGE)); + + // Then + assertThrows( + CmpServersConfigLoadingException.class, + () -> cmpServersConfig.loadConfiguration()); + } + + @Test + void shouldPassMessageToRethrownErrorWhenLoadingFails() throws CmpServersConfigLoadingException { + // Given + Mockito.when(cmpServersConfigLoader.load(any())).thenThrow(new CmpServersConfigLoadingException(ERROR_MESSAGE)); + + // When + Exception exception = assertThrows( + CmpServersConfigLoadingException.class, + () -> cmpServersConfig.loadConfiguration()); + + // Then + assertThat(exception.getMessage()).isEqualTo(ERROR_MESSAGE); } private static List<Cmpv2Server> generateTestConfiguration() { diff --git a/certService/src/test/java/org/onap/aaf/certservice/certification/configuration/validation/Cmpv2ServerConfigurationValidatorTest.java b/certService/src/test/java/org/onap/aaf/certservice/certification/configuration/validation/Cmpv2ServerConfigurationValidatorTest.java index 18097608..10a7ba46 100644 --- a/certService/src/test/java/org/onap/aaf/certservice/certification/configuration/validation/Cmpv2ServerConfigurationValidatorTest.java +++ b/certService/src/test/java/org/onap/aaf/certservice/certification/configuration/validation/Cmpv2ServerConfigurationValidatorTest.java @@ -20,6 +20,9 @@ package org.onap.aaf.certservice.certification.configuration.validation; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertThrows; + import org.bouncycastle.asn1.x500.X500Name; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -32,13 +35,12 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; -import static org.junit.jupiter.api.Assertions.assertThrows; - @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = CertServiceApplication.class) class Cmpv2ServerConfigurationValidatorTest { + private static final String EMPTY_STRING = ""; + @Autowired private Cmpv2ServerConfigurationValidator validator; @@ -52,53 +54,121 @@ class Cmpv2ServerConfigurationValidatorTest { } @Test - public void givenValidServerDetailsWhenValidatingShouldNotThrowAnyException() { - //then + public void shouldNotThrowExceptionWhenServerConfigurationIsValid() { + // Then assertDoesNotThrow(() -> validator.validate(server)); } @Test - public void givenWrongProtocolInURLServerDetailsWhenValidatingShouldThrowException() { - //given + public void shouldThrowExceptionWhenWrongProtocolInURL() { + // Given server.setUrl("https://test.test.test:60000/"); - //then - assertThrows(IllegalArgumentException.class, () -> {validator.validate(server);}); + // Then + assertExceptionIsThrown(); } @Test - public void givenWrongPortInURLServerDetailsWhenValidatingShouldThrowException() { - //given + public void shouldThrowExceptionWhenWrongPortInURL() { + // Given server.setUrl("http://test.test.test:70000/"); - //then - assertThrows(IllegalArgumentException.class, () -> validator.validate(server)); + // Then + assertExceptionIsThrown(); } @Test - public void givenWrongCANameLengthInURLServerDetailsWhenValidatingShouldThrowException() { - //given - server.setCaName(""); + public void shouldThrowExceptionWhenWrongCANameLength() { + // Given + server.setCaName(EMPTY_STRING); - //then - assertThrows(IllegalArgumentException.class, () -> validator.validate(server)); + // Then + assertExceptionIsThrown(); } @Test - public void givenWrongRVLengthInURLServerDetailsWhenValidatingShouldThrowException() { - //given - authentication.setRv(""); + public void shouldThrowExceptionWhenWrongRVLength() { + // Given + authentication.setRv(EMPTY_STRING); - //then - assertThrows(IllegalArgumentException.class, () -> validator.validate(server)); + // Then + assertExceptionIsThrown(); } + @Test - public void givenWrongIAKLengthInURLServerDetailsWhenValidatingShouldThrowException() { - //given - authentication.setIak(""); + public void shouldThrowExceptionWhenWrongIAKLength() { + // Given + authentication.setIak(EMPTY_STRING); + + // Then + assertExceptionIsThrown(); + } + + @Test + public void shouldThrowExceptionWhenCaNameIsNull() { + // Given + server.setCaName(null); + + // Then + assertExceptionIsThrown(); + } + + @Test + public void shouldThrowExceptionWhenIssuerDnIsNull() { + // Given + server.setIssuerDN(null); + + // Then + assertExceptionIsThrown(); + } + + @Test + public void shouldThrowExceptionWhenCaModeIsNull() { + // Given + server.setCaMode(null); + + // Then + assertExceptionIsThrown(); + } + + @Test + public void shouldThrowExceptionWhenUrlIsNull() { + // Given + server.setUrl(null); + + // Then + assertExceptionIsThrown(); + } + + @Test + public void shouldThrowExceptionWhenAuthenticationIsNull() { + // Given + server.setAuthentication(null); + + // Then + assertExceptionIsThrown(); + } + + @Test + public void shouldThrowExceptionWhenIakIsNull() { + // Given + authentication.setIak(null); + + // Then + assertExceptionIsThrown(); + } + + @Test + public void shouldThrowExceptionWhenRvIsNull() { + // Given + authentication.setRv(null); + + // Then + assertExceptionIsThrown(); + } - //then + private void assertExceptionIsThrown() { assertThrows(IllegalArgumentException.class, () -> validator.validate(server)); } diff --git a/certService/src/test/java/org/onap/aaf/certservice/certification/configuration/validation/constraints/Cmpv2URLValidatorTest.java b/certService/src/test/java/org/onap/aaf/certservice/certification/configuration/validation/constraints/Cmpv2URLValidatorTest.java index a2a03860..78368d8a 100644 --- a/certService/src/test/java/org/onap/aaf/certservice/certification/configuration/validation/constraints/Cmpv2URLValidatorTest.java +++ b/certService/src/test/java/org/onap/aaf/certservice/certification/configuration/validation/constraints/Cmpv2URLValidatorTest.java @@ -27,7 +27,7 @@ import static org.junit.jupiter.api.Assertions.*; class Cmpv2URLValidatorTest { - private Cmpv2URLValidator validator = new Cmpv2URLValidator(); + private final Cmpv2URLValidator validator = new Cmpv2URLValidator(); @Test public void givenCorrectURLWhenValidatingShouldReturnTrue() { diff --git a/certService/src/test/java/org/onap/aaf/certservice/certification/configuration/validation/constraints/violations/PortNumberViolationTest.java b/certService/src/test/java/org/onap/aaf/certservice/certification/configuration/validation/constraints/violations/PortNumberViolationTest.java index 8ae4cd88..d1850473 100644 --- a/certService/src/test/java/org/onap/aaf/certservice/certification/configuration/validation/constraints/violations/PortNumberViolationTest.java +++ b/certService/src/test/java/org/onap/aaf/certservice/certification/configuration/validation/constraints/violations/PortNumberViolationTest.java @@ -27,7 +27,7 @@ import static org.junit.jupiter.api.Assertions.*; class PortNumberViolationTest { - private PortNumberViolation violation = new PortNumberViolation(); + private final PortNumberViolation violation = new PortNumberViolation(); @Test public void givenValidPortShouldReturnTrue() { diff --git a/certService/src/test/java/org/onap/aaf/certservice/certification/configuration/validation/constraints/violations/RequestTypeViolationTest.java b/certService/src/test/java/org/onap/aaf/certservice/certification/configuration/validation/constraints/violations/RequestTypeViolationTest.java index 9e220560..cba2f340 100644 --- a/certService/src/test/java/org/onap/aaf/certservice/certification/configuration/validation/constraints/violations/RequestTypeViolationTest.java +++ b/certService/src/test/java/org/onap/aaf/certservice/certification/configuration/validation/constraints/violations/RequestTypeViolationTest.java @@ -26,7 +26,7 @@ import static org.junit.jupiter.api.Assertions.*; class RequestTypeViolationTest { - private RequestTypeViolation violation = new RequestTypeViolation(); + private final RequestTypeViolation violation = new RequestTypeViolation(); @Test public void givenValidRequestTypeShouldReturnTrue() { diff --git a/certService/src/test/resources/invalidCmpServers.json b/certService/src/test/resources/invalidCmpServers.json new file mode 100644 index 00000000..ac4b34af --- /dev/null +++ b/certService/src/test/resources/invalidCmpServers.json @@ -0,0 +1,19 @@ +{ + "cmpv2Servers": [ + { + "caName": " ", + "url": "http://127.0.0.1/ejbca/publicweb/cmp/cmp", + "issuerDN": "CN=ManagementCA", + "caMode": "CLIENT" + }, + { + "caName": "TEST2", + "url": "http://127.0.0.1/ejbca/publicweb/cmp/cmpRA", + "caMode": "RA", + "authentication": { + "iak": "xxx", + "rv": "yyy" + } + } + ] +}
\ No newline at end of file diff --git a/certServiceClient/pom.xml b/certServiceClient/pom.xml index 5e11f583..4b7c0cf9 100644 --- a/certServiceClient/pom.xml +++ b/certServiceClient/pom.xml @@ -155,11 +155,7 @@ </dependency> <dependency> <groupId>org.junit.jupiter</groupId> - <artifactId>junit-jupiter-engine</artifactId> - </dependency> - <dependency> - <groupId>org.junit.jupiter</groupId> - <artifactId>junit-jupiter-api</artifactId> + <artifactId>junit-jupiter</artifactId> </dependency> <dependency> <groupId>org.mockito</groupId> diff --git a/certServiceClient/src/main/java/org/onap/aaf/certservice/client/CertServiceClient.java b/certServiceClient/src/main/java/org/onap/aaf/certservice/client/CertServiceClient.java index 7072a883..d3d7f26d 100644 --- a/certServiceClient/src/main/java/org/onap/aaf/certservice/client/CertServiceClient.java +++ b/certServiceClient/src/main/java/org/onap/aaf/certservice/client/CertServiceClient.java @@ -19,13 +19,14 @@ package org.onap.aaf.certservice.client; +import java.security.KeyPair; import org.onap.aaf.certservice.client.api.ExitableException; +import org.onap.aaf.certservice.client.certification.PrivateKeyToPemEncoder; import org.onap.aaf.certservice.client.certification.CsrFactory; import org.onap.aaf.certservice.client.certification.KeyPairFactory; import org.onap.aaf.certservice.client.certification.conversion.KeystoreTruststoreCreator; import org.onap.aaf.certservice.client.certification.conversion.KeystoreTruststoreCreatorFactory; - -import java.security.KeyPair; +import org.onap.aaf.certservice.client.common.Base64Encoder; import org.onap.aaf.certservice.client.configuration.EnvsForClient; import org.onap.aaf.certservice.client.configuration.EnvsForCsr; import org.onap.aaf.certservice.client.configuration.factory.ClientConfigurationFactory; @@ -39,7 +40,6 @@ import org.onap.aaf.certservice.client.httpclient.model.CertServiceResponse; import static org.onap.aaf.certservice.client.api.ExitCode.SUCCESS_EXIT_CODE; import static org.onap.aaf.certservice.client.certification.EncryptionAlgorithmConstants.KEY_SIZE; import static org.onap.aaf.certservice.client.certification.EncryptionAlgorithmConstants.RSA_ENCRYPTION_ALGORITHM; -import static org.onap.aaf.certservice.client.common.Base64Coder.encode; public class CertServiceClient { @@ -51,22 +51,23 @@ public class CertServiceClient { public void run() { KeyPairFactory keyPairFactory = new KeyPairFactory(RSA_ENCRYPTION_ALGORITHM, KEY_SIZE); + PrivateKeyToPemEncoder pkEncoder = new PrivateKeyToPemEncoder(); + Base64Encoder base64Encoder = new Base64Encoder(); try { ClientConfiguration clientConfiguration = new ClientConfigurationFactory(new EnvsForClient()).create(); CsrConfiguration csrConfiguration = new CsrConfigurationFactory(new EnvsForCsr()).create(); KeyPair keyPair = keyPairFactory.create(); CsrFactory csrFactory = new CsrFactory(csrConfiguration); - String csr = csrFactory.createEncodedCsr(keyPair); CloseableHttpClientProvider provider = new CloseableHttpClientProvider( clientConfiguration.getRequestTimeout()); HttpClient httpClient = new HttpClient(provider, clientConfiguration.getUrlToCertService()); CertServiceResponse certServiceData = - httpClient.retrieveCertServiceData( - clientConfiguration.getCaName(), - csr, - encode(keyPair.getPrivate().toString())); + httpClient.retrieveCertServiceData( + clientConfiguration.getCaName(), + base64Encoder.encode(csrFactory.createCsrInPem(keyPair)), + base64Encoder.encode(pkEncoder.encodePrivateKeyToPem(keyPair.getPrivate()))); KeystoreTruststoreCreator filesCreator = new KeystoreTruststoreCreatorFactory( clientConfiguration.getCertsOutputPath()).create(); diff --git a/certServiceClient/src/main/java/org/onap/aaf/certservice/client/api/ExitCode.java b/certServiceClient/src/main/java/org/onap/aaf/certservice/client/api/ExitCode.java index 561cfd2a..670cbe90 100644 --- a/certServiceClient/src/main/java/org/onap/aaf/certservice/client/api/ExitCode.java +++ b/certServiceClient/src/main/java/org/onap/aaf/certservice/client/api/ExitCode.java @@ -26,7 +26,8 @@ public enum ExitCode { CSR_GENERATION_EXCEPTION(4), CERT_SERVICE_API_CONNECTION_EXCEPTION(5), HTTP_CLIENT_EXCEPTION(6), - PKCS12_CONVERSION_EXCEPTION(7); + PKCS12_CONVERSION_EXCEPTION(7), + PK_TO_PEM_ENCODING_EXCEPTION(8); private final int value; diff --git a/certServiceClient/src/main/java/org/onap/aaf/certservice/client/certification/CsrFactory.java b/certServiceClient/src/main/java/org/onap/aaf/certservice/client/certification/CsrFactory.java index f936636a..83fa6d44 100644 --- a/certServiceClient/src/main/java/org/onap/aaf/certservice/client/certification/CsrFactory.java +++ b/certServiceClient/src/main/java/org/onap/aaf/certservice/client/certification/CsrFactory.java @@ -66,12 +66,12 @@ public class CsrFactory { } - public String createEncodedCsr(KeyPair keyPair) throws CsrGenerationException { + public String createCsrInPem(KeyPair keyPair) throws CsrGenerationException { PKCS10CertificationRequest request; String csrParameters = getMandatoryParameters().append(getOptionalParameters()).toString(); X500Principal subject = new X500Principal(csrParameters); request = createPKCS10Csr(subject, keyPair); - return encodeToBase64(convertPKC10CsrToPem(request)); + return convertPKC10CsrToPem(request); } @@ -151,8 +151,4 @@ public class CsrFactory { private static Boolean isParameterPresent(String parameter) { return parameter != null && !"".equals(parameter); } - - private static String encodeToBase64(String csrInPem) { - return Base64.getEncoder().encodeToString(csrInPem.getBytes(StandardCharsets.UTF_8)); - } } diff --git a/certServiceClient/src/main/java/org/onap/aaf/certservice/client/certification/PrivateKeyToPemEncoder.java b/certServiceClient/src/main/java/org/onap/aaf/certservice/client/certification/PrivateKeyToPemEncoder.java new file mode 100644 index 00000000..77995958 --- /dev/null +++ b/certServiceClient/src/main/java/org/onap/aaf/certservice/client/certification/PrivateKeyToPemEncoder.java @@ -0,0 +1,51 @@ +/* + * ============LICENSE_START======================================================= + * aaf-certservice-client + * ================================================================================ + * Copyright (C) 2020 Nokia. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.aaf.certservice.client.certification; + + + +import java.io.IOException; +import java.io.StringWriter; +import java.security.PrivateKey; + +import org.bouncycastle.openssl.jcajce.JcaPEMWriter; +import org.bouncycastle.util.io.pem.PemObject; +import org.onap.aaf.certservice.client.certification.exception.PkEncodingException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class PrivateKeyToPemEncoder { + + public static final String PEM_OBJECT_TYPE = "RSA PRIVATE KEY"; + private final Logger LOGGER = LoggerFactory.getLogger(PrivateKeyToPemEncoder.class); + + public String encodePrivateKeyToPem(PrivateKey pk) throws PkEncodingException { + LOGGER.info("Encoding PrivateKey to PEM"); + StringWriter stringWriter = new StringWriter(); + try (JcaPEMWriter pemWriter = new JcaPEMWriter(stringWriter)) { + pemWriter.writeObject(new PemObject(PEM_OBJECT_TYPE, pk.getEncoded())); + } catch (IOException e) { + LOGGER.error("Exception occurred during encoding PrivateKey to PEM", e); + throw new PkEncodingException(e); + } + return stringWriter.toString(); + } +} diff --git a/certServiceClient/src/main/java/org/onap/aaf/certservice/client/certification/conversion/PKCS12FilesCreator.java b/certServiceClient/src/main/java/org/onap/aaf/certservice/client/certification/conversion/PKCS12FilesCreator.java index 60121b03..d8c41bfd 100644 --- a/certServiceClient/src/main/java/org/onap/aaf/certservice/client/certification/conversion/PKCS12FilesCreator.java +++ b/certServiceClient/src/main/java/org/onap/aaf/certservice/client/certification/conversion/PKCS12FilesCreator.java @@ -21,6 +21,8 @@ package org.onap.aaf.certservice.client.certification.conversion; import java.io.FileOutputStream; import java.io.IOException; +import java.nio.file.Path; + import org.onap.aaf.certservice.client.certification.exception.PemToPKCS12ConverterException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -39,10 +41,10 @@ class PKCS12FilesCreator { PKCS12FilesCreator(String path) { - keystoreJksPath = path + KEYSTORE_JKS; - keystorePassPath = path + KEYSTORE_PASS; - truststoreJksPath = path + TRUSTSTORE_JKS; - truststorePassPath = path + TRUSTSTORE_PASS; + keystoreJksPath = Path.of(path, KEYSTORE_JKS).toString(); + keystorePassPath = Path.of(path, KEYSTORE_PASS).toString(); + truststoreJksPath = Path.of(path, TRUSTSTORE_JKS).toString(); + truststorePassPath = Path.of(path, TRUSTSTORE_PASS).toString(); } void saveKeystoreData(byte[] keystoreData, String keystorePassword) throws PemToPKCS12ConverterException { diff --git a/certServiceClient/src/main/java/org/onap/aaf/certservice/client/certification/exception/PkEncodingException.java b/certServiceClient/src/main/java/org/onap/aaf/certservice/client/certification/exception/PkEncodingException.java new file mode 100644 index 00000000..596a6a44 --- /dev/null +++ b/certServiceClient/src/main/java/org/onap/aaf/certservice/client/certification/exception/PkEncodingException.java @@ -0,0 +1,35 @@ +/*============LICENSE_START======================================================= + * aaf-certservice-client + * ================================================================================ + * Copyright (C) 2020 Nokia. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.aaf.certservice.client.certification.exception; + +import org.onap.aaf.certservice.client.api.ExitCode; +import org.onap.aaf.certservice.client.api.ExitableException; + +public class PkEncodingException extends ExitableException { + private static final ExitCode EXIT_CODE = ExitCode.PK_TO_PEM_ENCODING_EXCEPTION; + + public PkEncodingException(Throwable e) { + super(e); + } + + public int applicationExitCode() { + return EXIT_CODE.getValue(); + } +} diff --git a/certServiceClient/src/main/java/org/onap/aaf/certservice/client/common/Base64Coder.java b/certServiceClient/src/main/java/org/onap/aaf/certservice/client/common/Base64Encoder.java index c066187d..1f90db1b 100644 --- a/certServiceClient/src/main/java/org/onap/aaf/certservice/client/common/Base64Coder.java +++ b/certServiceClient/src/main/java/org/onap/aaf/certservice/client/common/Base64Encoder.java @@ -1,5 +1,4 @@ -/* - * ============LICENSE_START======================================================= +/*============LICENSE_START======================================================= * aaf-certservice-client * ================================================================================ * Copyright (C) 2020 Nokia. All rights reserved. @@ -22,8 +21,8 @@ package org.onap.aaf.certservice.client.common; import org.bouncycastle.util.encoders.Base64; -public class Base64Coder { - public static String encode(String string){ +public class Base64Encoder { + public String encode(String string){ return new String(Base64.encode(string.getBytes())); } -} +}
\ No newline at end of file diff --git a/certServiceClient/src/main/java/org/onap/aaf/certservice/client/configuration/EnvValidationUtils.java b/certServiceClient/src/main/java/org/onap/aaf/certservice/client/configuration/EnvValidationUtils.java index e65d688d..b0405274 100644 --- a/certServiceClient/src/main/java/org/onap/aaf/certservice/client/configuration/EnvValidationUtils.java +++ b/certServiceClient/src/main/java/org/onap/aaf/certservice/client/configuration/EnvValidationUtils.java @@ -27,7 +27,7 @@ public final class EnvValidationUtils { private EnvValidationUtils() {} public static Boolean isPathValid(String path) { - return path.matches("^/|(/[a-zA-Z0-9_-]+)+$"); + return path.matches("^/|(/[a-zA-Z0-9_-]+)+/?$"); } public static Boolean isAlphaNumeric(String caName) { diff --git a/certServiceClient/src/test/java/org/onap/aaf/certservice/client/certification/CsrFactoryTest.java b/certServiceClient/src/test/java/org/onap/aaf/certservice/client/certification/CsrFactoryTest.java index 16b5e03b..809a91f2 100644 --- a/certServiceClient/src/test/java/org/onap/aaf/certservice/client/certification/CsrFactoryTest.java +++ b/certServiceClient/src/test/java/org/onap/aaf/certservice/client/certification/CsrFactoryTest.java @@ -52,7 +52,7 @@ public class CsrFactoryTest { when(config.getOrganizationUnit()).thenReturn("ONAP"); when(config.getState()).thenReturn("California"); - assertThat(new CsrFactory(config).createEncodedCsr(keyPair)).isNotEmpty(); + assertThat(new CsrFactory(config).createCsrInPem(keyPair)).isNotEmpty(); } } diff --git a/certServiceClient/src/test/java/org/onap/aaf/certservice/client/certification/PrivateKeyToPemEncoderTest.java b/certServiceClient/src/test/java/org/onap/aaf/certservice/client/certification/PrivateKeyToPemEncoderTest.java new file mode 100644 index 00000000..def9c1d5 --- /dev/null +++ b/certServiceClient/src/test/java/org/onap/aaf/certservice/client/certification/PrivateKeyToPemEncoderTest.java @@ -0,0 +1,66 @@ +/*============LICENSE_START======================================================= + * aaf-certservice-client + * ================================================================================ + * Copyright (C) 2020 Nokia. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.aaf.certservice.client.certification; + + +import org.bouncycastle.util.io.pem.PemObject; +import org.bouncycastle.util.io.pem.PemReader; +import org.junit.jupiter.api.Test; +import org.onap.aaf.certservice.client.certification.exception.PkEncodingException; + +import java.io.IOException; +import java.io.StringReader; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; + +import static org.assertj.core.api.Assertions.assertThat; + +class PrivateKeyToPemEncoderTest { + + private static final String ENCRYPTION_ALGORITHM = "RSA"; + private static final String RESOURCES_DIR = "src/test/resources/"; + private static final String PRIVATE_KEY_PEM_PATH = RESOURCES_DIR + "rsaPrivateKeyPem"; + + @Test + public void shouldReturnProperlyEncodedPrivateKey() throws InvalidKeySpecException, NoSuchAlgorithmException, PkEncodingException, IOException { + //given + String expectedPem = Files.readString(Paths.get(PRIVATE_KEY_PEM_PATH)); + PrivateKeyToPemEncoder testedPkEncoder = new PrivateKeyToPemEncoder(); + //when + PrivateKey privateKey = extractPrivateKeyFromPem(expectedPem); + String resultPkInPem = testedPkEncoder.encodePrivateKeyToPem(privateKey); + //then + assertThat(resultPkInPem).isEqualTo(expectedPem); + } + + private PrivateKey extractPrivateKeyFromPem(String pem) throws NoSuchAlgorithmException, InvalidKeySpecException, IOException { + PemReader pemReader = new PemReader(new StringReader(pem)); + PemObject pemObject = pemReader.readPemObject(); + pemReader.close(); + PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(pemObject.getContent()); + KeyFactory kf = KeyFactory.getInstance(ENCRYPTION_ALGORITHM); + return kf.generatePrivate(spec); + } +}
\ No newline at end of file diff --git a/certServiceClient/src/test/java/org/onap/aaf/certservice/client/configuration/EnvValidationUtilsTest.java b/certServiceClient/src/test/java/org/onap/aaf/certservice/client/configuration/EnvValidationUtilsTest.java new file mode 100644 index 00000000..8f4fe6a3 --- /dev/null +++ b/certServiceClient/src/test/java/org/onap/aaf/certservice/client/configuration/EnvValidationUtilsTest.java @@ -0,0 +1,41 @@ +/*============LICENSE_START======================================================= + * aaf-certservice-client + * ================================================================================ + * Copyright (C) 2020 Nokia. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.aaf.certservice.client.configuration; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class EnvValidationUtilsTest { + + @ParameterizedTest + @ValueSource(strings = {"/var/log", "/", "/var/log/", "/second_var", "/second-var"}) + public void shouldAcceptValidPath(String path){ + assertTrue(EnvValidationUtils.isPathValid(path)); + } + + @ParameterizedTest + @ValueSource(strings = {"/var/log?", "", "var_", "var", "//", "/var//log"}) + public void shouldRejectInvalidPath(String path){ + assertFalse(EnvValidationUtils.isPathValid(path)); + } +}
\ No newline at end of file diff --git a/certServiceClient/src/test/java/org/onap/aaf/certservice/client/configuration/model/CsrConfigurationFactoryTest.java b/certServiceClient/src/test/java/org/onap/aaf/certservice/client/configuration/model/CsrConfigurationFactoryTest.java index 707094c0..bb566e81 100644 --- a/certServiceClient/src/test/java/org/onap/aaf/certservice/client/configuration/model/CsrConfigurationFactoryTest.java +++ b/certServiceClient/src/test/java/org/onap/aaf/certservice/client/configuration/model/CsrConfigurationFactoryTest.java @@ -20,13 +20,17 @@ package org.onap.aaf.certservice.client.configuration.model; +import org.assertj.core.api.Condition; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.onap.aaf.certservice.client.api.ExitCode; import org.onap.aaf.certservice.client.configuration.CsrConfigurationEnvs; import org.onap.aaf.certservice.client.configuration.EnvsForCsr; import org.onap.aaf.certservice.client.configuration.exception.CsrConfigurationException; import org.onap.aaf.certservice.client.configuration.factory.CsrConfigurationFactory; import java.util.Optional; +import java.util.function.Predicate; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; @@ -43,23 +47,30 @@ public class CsrConfigurationFactoryTest { private final String ORGANIZATION_UNIT_VALID = "ONAP"; private final String STATE_VALID = "California"; private final String COMMON_NAME_INVALID = "onap.org*&"; + private final String COUNTRY_INVALID = "PLA"; + private final String ORGANIZATION_INVALID = "Linux?Foundation"; private EnvsForCsr envsForCsr = mock(EnvsForCsr.class); - + private CsrConfigurationFactory testedFactory; + private Condition<CsrConfigurationException> expectedExitCodeCondition = new Condition<>("Correct exit code"){ + @Override + public boolean matches(CsrConfigurationException e) { + return e.applicationExitCode() == ExitCode.CSR_CONFIGURATION_EXCEPTION.getValue(); + } + }; + + @BeforeEach + void setUp() { + testedFactory = new CsrConfigurationFactory(envsForCsr); + } @Test - void create_shouldReturnSuccessWhenAllVariablesAreSetAndValid() throws CsrConfigurationException { + void shouldReturnCorrectConfiguration_WhenAllVariablesAreSetAndValid() throws CsrConfigurationException { // given - when(envsForCsr.getCommonName()).thenReturn(Optional.of(COMMON_NAME_VALID)); - when(envsForCsr.getSubjectAlternativesName()).thenReturn(Optional.of(SANS_VALID)); - when(envsForCsr.getCountry()).thenReturn(Optional.of(COUNTRY_VALID)); - when(envsForCsr.getLocation()).thenReturn(Optional.of(LOCATION_VALID)); - when(envsForCsr.getOrganization()).thenReturn(Optional.of(ORGANIZATION_VALID)); - when(envsForCsr.getOrganizationUnit()).thenReturn(Optional.of(ORGANIZATION_UNIT_VALID)); - when(envsForCsr.getState()).thenReturn(Optional.of(STATE_VALID)); + mockEnvsWithAllValidParameters(); // when - CsrConfiguration configuration = new CsrConfigurationFactory(envsForCsr).create(); + CsrConfiguration configuration = testedFactory.create(); // then assertThat(configuration.getCommonName()).isEqualTo(COMMON_NAME_VALID); @@ -72,15 +83,12 @@ public class CsrConfigurationFactoryTest { } @Test - void create_shouldReturnSuccessWhenNotRequiredVariablesAreNotSet() throws CsrConfigurationException { + void shouldReturnCorrectConfiguration_WhenNotRequiredVariablesAreNotSet() throws CsrConfigurationException { // given - when(envsForCsr.getCommonName()).thenReturn(Optional.of(COMMON_NAME_VALID)); - when(envsForCsr.getState()).thenReturn(Optional.of(STATE_VALID)); - when(envsForCsr.getCountry()).thenReturn(Optional.of(COUNTRY_VALID)); - when(envsForCsr.getOrganization()).thenReturn(Optional.of(ORGANIZATION_VALID)); + mockEnvsWithValidRequiredParameters(); // when - CsrConfiguration configuration = new CsrConfigurationFactory(envsForCsr).create(); + CsrConfiguration configuration = testedFactory.create(); // then assertThat(configuration.getCommonName()).isEqualTo(COMMON_NAME_VALID); @@ -91,22 +99,89 @@ public class CsrConfigurationFactoryTest { @Test - void create_shouldReturnCsrConfigurationExceptionWhenCommonNameContainsSpecialCharacters() { + void shouldThrowCsrConfigurationException_WhenCommonNameInvalid() { // given - when(envsForCsr.getCommonName()).thenReturn(Optional.of(COMMON_NAME_INVALID)); + mockEnvsWithInvalidCommonName(); + + // when/then + assertThatExceptionOfType(CsrConfigurationException.class) + .isThrownBy(testedFactory::create) + .withMessageContaining(CsrConfigurationEnvs.COMMON_NAME + " is invalid.") + .has(expectedExitCodeCondition); + } + + @Test + void shouldThrowCsrConfigurationException_WhenOrganizationInvalid() { + // given + mockEnvsWithInvalidOrganization(); + + // when/then + assertThatExceptionOfType(CsrConfigurationException.class) + .isThrownBy(testedFactory::create) + .withMessageContaining(CsrConfigurationEnvs.ORGANIZATION + " is invalid.") + .has(expectedExitCodeCondition); + + } + + @Test + void shouldThrowCsrConfigurationException_WhenCountryInvalid() { + // given + mockEnvsWithInvalidCountry(); + + // when/then + assertThatExceptionOfType(CsrConfigurationException.class) + .isThrownBy(testedFactory::create) + .withMessageContaining(CsrConfigurationEnvs.COUNTRY + " is invalid.") + .has(expectedExitCodeCondition); + + } + + @Test + void shouldThrowCsrConfigurationExceptionWhenStateInvalid() { + // given + mockEnvsWithInvalidState(); + // when/then + assertThatExceptionOfType(CsrConfigurationException.class) + .isThrownBy(testedFactory::create) + .withMessageContaining(CsrConfigurationEnvs.STATE + " is invalid.") + .has(expectedExitCodeCondition); + } + + private void mockEnvsWithAllValidParameters() { + mockEnvsWithValidRequiredParameters(); + mockEnvsWithValidOptionalParameters(); + } + + private void mockEnvsWithValidOptionalParameters() { + when(envsForCsr.getOrganizationUnit()).thenReturn(Optional.of(ORGANIZATION_UNIT_VALID)); + when(envsForCsr.getLocation()).thenReturn(Optional.of(LOCATION_VALID)); when(envsForCsr.getSubjectAlternativesName()).thenReturn(Optional.of(SANS_VALID)); + } + + private void mockEnvsWithValidRequiredParameters() { + when(envsForCsr.getCommonName()).thenReturn(Optional.of(COMMON_NAME_VALID)); when(envsForCsr.getCountry()).thenReturn(Optional.of(COUNTRY_VALID)); - when(envsForCsr.getLocation()).thenReturn(Optional.of(LOCATION_VALID)); when(envsForCsr.getOrganization()).thenReturn(Optional.of(ORGANIZATION_VALID)); - when(envsForCsr.getOrganizationUnit()).thenReturn(Optional.of(ORGANIZATION_UNIT_VALID)); - when(envsForCsr.getState()).thenReturn(Optional.of(SANS_VALID)); + when(envsForCsr.getState()).thenReturn(Optional.of(STATE_VALID)); + } - // when - CsrConfigurationFactory configurationFactory = new CsrConfigurationFactory(envsForCsr); + private void mockEnvsWithInvalidCommonName() { + mockEnvsWithAllValidParameters(); + when(envsForCsr.getCommonName()).thenReturn(Optional.of(COMMON_NAME_INVALID)); + } - // when/then - assertThatExceptionOfType(CsrConfigurationException.class) - .isThrownBy(configurationFactory::create) - .withMessageContaining(CsrConfigurationEnvs.COMMON_NAME + " is invalid."); + private void mockEnvsWithInvalidCountry() { + mockEnvsWithAllValidParameters(); + when(envsForCsr.getCountry()).thenReturn(Optional.of(COUNTRY_INVALID)); + } + + private void mockEnvsWithInvalidOrganization() { + mockEnvsWithAllValidParameters(); + when(envsForCsr.getOrganization()).thenReturn(Optional.of(ORGANIZATION_INVALID)); + } + + private void mockEnvsWithInvalidState() { + mockEnvsWithAllValidParameters(); + when(envsForCsr.getState()).thenReturn(Optional.empty()); } } diff --git a/certServiceClient/src/test/resources/rsaPrivateKeyPem b/certServiceClient/src/test/resources/rsaPrivateKeyPem new file mode 100644 index 00000000..a99cc3c8 --- /dev/null +++ b/certServiceClient/src/test/resources/rsaPrivateKeyPem @@ -0,0 +1,28 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCwooLW/yfXHIGs +djOW6zCM6mzGq4ZkFr0LMVBE+Y9dckGsYJzCrfC4pQtFjcvTlwalu6/YOgieR/zY +bgVF7Ic0IYV+BssO+t6Zx2xYli4NIGc5kJgDrKtR6lWvH8AMnQEr+QiDElLBWobU ++QGn5v8A528Ow5yD1fmxKTqqvWS4v1rOShGCIdse5ViraGjnMFxOV6u6pGqa17v7 +dTh0XIUyF/o3aSbmBHXkmvQ4pu/K1ncsF2zHIqUWAc7j8y1u5uE5o8b+dUzkcS4t +QKfjFKP81I7XQNmpGZ8REzWyaYVk3RrMCju6iVgdKrs198Wif0b7wGswFv4BFhOp +2jceFUwfAgMBAAECggEABjDb9x8gTVjRbrMB4eNCY14ADAKNBksJuy+ySYiZrsPH +a3xDYktoaYBXYcuzfioH8J0gb6qxDKMnSIqqoqXEo14daKpiSZcfYDJuKLiyyoD9 +PTZFLbPKmWdmM2ogeBC0rs7eroFg5yf+G87ScQkWnPh/mvveK3y/cKcqSDu1IQh8 +3b8KQshC5g4iBqCfOMW3ASF03M4zmM3brKMWsdsAWEbFHQ34H10FXTHrAINpWIZK +s2NL3z9tK6hXrwlZdKH6R/JWczSO7O5MBjLfeXZK7q3Tw4qtFWWjcNwfPlUZKMAS +3fZFamFwY//qW+0yuCO59o70d9Pjm6p0DWsfOs9t8QKBgQD1iRdGV4xZXZT9Q4Wh +LQMnChjuNHcmhdBYbmC03j3AffQwkQ1dKt++9uWYdy9dO9v1w7aygAMQI36jkDvR +UJ1Rnmt9gQpeOL/wHP3R0uHbTtxLeGnX3Oo1Yx7Wfl98rq4mmBxjO5Lgft/6kTgz +XgeiNDWi53KwDEOoFaZhWihZ6QKBgQC4Ka5Hc9wXD/5utpvNs5ut+9zjY0kx6kr8 +SyDZExbVR1ohtvSQ2sd2JvZPyFS0VbvYfFPhyCYcWW9LDEX168CZT5aHgcNop1Iu +Szq8nYrljFa5Ibdlpf0qxC6JgObC2XytUR0O7BaXHBWpl0/wLpLTcfU2wTDLRoH8 +JLu7P3MoxwKBgQCI9DWqQ60CL8Op3J7NvviyLtynCVaogx0qJi8E062oD9lDubS1 +kfOJZde8ykX+ACR5mffu6p5KwzGg9BOZdhi57N5R+8cXtRnCSbl97t2R4RPZeMm4 +4P02WBpcU9LZDeoPlurGovUTCVHPRm8Nn9YsMGj2e5ip/71BJQpP5OT6+QKBgQCP +NYJb+AG3QW22hHQmArxWEFxVyrh5g1sqU/XIOCryUVkKjK4kEq02+NdjdUJBNcYs +c4n7MlxIgVelQXcJ5HlR/uzslQDy2eJzM3cKg2wmUvqBXnGyLuDvJ72UmdNYxC1K +zZ/OIdLzURibV5oHCQCOQrjQCm06NasQ+zOtSYrwswKBgQCUHhgxynFNyidxPFzX +V0X5xCbJ3jvJNjZFRsItQ97vEAkfJqxCnOZKMti0JWSlLBEViaKnqaA+ZE/SeJ/k +Jut5h9gu4QIdeF4mf9v3tjuEQP7RaMCD6xnFZnebkQf6wlZz5VaXME4ICpi1Cnk7 +DySS9CMoRnwdwY7hAbfPtupKDA== +-----END RSA PRIVATE KEY----- @@ -263,13 +263,7 @@ </dependency> <dependency> <groupId>org.junit.jupiter</groupId> - <artifactId>junit-jupiter-engine</artifactId> - <version>${junit.version}</version> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.junit.jupiter</groupId> - <artifactId>junit-jupiter-api</artifactId> + <artifactId>junit-jupiter</artifactId> <version>${junit.version}</version> <scope>test</scope> </dependency> |