From 707fb6d83819058d5736b2dc38bea3c2d9e07a2d Mon Sep 17 00:00:00 2001 From: vasraz Date: Fri, 8 Oct 2021 14:48:08 +0100 Subject: Large csar handling - object store Change-Id: I4e88bd7bfcc1fdbc93d67da2682f2e873ba243c6 Signed-off-by: Vasyl Razinkov Issue-ID: SDC-3754 --- .../OrchestrationTemplateCandidateImpl.java | 87 +++++------- .../OrchestrationTemplateCandidateImplTest.java | 36 +++-- .../csar/validation/CsarSecurityValidator.java | 2 +- .../security/SecurityManager.java | 84 +++++++---- .../csar/validation/CsarSecurityValidatorTest.java | 104 +++++++++++--- .../security/SecurityManagerTest.java | 154 +++++++++++++++++---- .../templates/default/configuration.yaml.erb | 71 +++++----- .../org/openecomp/sdc/common/errors/Messages.java | 5 +- ...hestrationTemplateCandidateDaoZusammenImpl.java | 57 +++----- 9 files changed, 387 insertions(+), 213 deletions(-) (limited to 'openecomp-be') diff --git a/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/main/java/org/openecomp/sdcrests/vsp/rest/services/OrchestrationTemplateCandidateImpl.java b/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/main/java/org/openecomp/sdcrests/vsp/rest/services/OrchestrationTemplateCandidateImpl.java index 23930ed640..6fe7f9dd0a 100644 --- a/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/main/java/org/openecomp/sdcrests/vsp/rest/services/OrchestrationTemplateCandidateImpl.java +++ b/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/main/java/org/openecomp/sdcrests/vsp/rest/services/OrchestrationTemplateCandidateImpl.java @@ -28,13 +28,14 @@ import static javax.ws.rs.core.Response.Status.NOT_FOUND; import static org.openecomp.core.validation.errors.ErrorMessagesFormatBuilder.getErrorWithParameters; import static org.openecomp.sdc.common.errors.Messages.ERROR_HAS_OCCURRED_WHILE_PERSISTING_THE_ARTIFACT; import static org.openecomp.sdc.common.errors.Messages.ERROR_HAS_OCCURRED_WHILE_REDUCING_THE_ARTIFACT_SIZE; -import static org.openecomp.sdc.common.errors.Messages.EXTERNAL_CSAR_STORE_CONFIGURATION_FAILURE_MISSING_FULL_PATH; import static org.openecomp.sdc.common.errors.Messages.NO_FILE_WAS_UPLOADED_OR_FILE_NOT_EXIST; import static org.openecomp.sdc.common.errors.Messages.PACKAGE_PROCESS_ERROR; import static org.openecomp.sdc.common.errors.Messages.UNEXPECTED_PROBLEM_HAPPENED_WHILE_GETTING; +import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.Collections; @@ -42,8 +43,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; +import java.util.UUID; +import javax.activation.DataHandler; import javax.inject.Named; import javax.ws.rs.core.Response; import org.apache.commons.lang3.tuple.Pair; @@ -55,13 +56,8 @@ import org.openecomp.sdc.activitylog.dao.type.ActivityType; import org.openecomp.sdc.be.csar.storage.ArtifactInfo; import org.openecomp.sdc.be.csar.storage.ArtifactStorageConfig; import org.openecomp.sdc.be.csar.storage.ArtifactStorageManager; -import org.openecomp.sdc.be.csar.storage.CsarPackageReducerConfiguration; -import org.openecomp.sdc.be.csar.storage.CsarSizeReducer; import org.openecomp.sdc.be.csar.storage.PackageSizeReducer; -import org.openecomp.sdc.be.csar.storage.PersistentVolumeArtifactStorageConfig; -import org.openecomp.sdc.be.csar.storage.PersistentVolumeArtifactStorageManager; -import org.openecomp.sdc.be.exception.BusinessException; -import org.openecomp.sdc.common.CommonConfigurationManager; +import org.openecomp.sdc.be.csar.storage.StorageFactory; import org.openecomp.sdc.common.util.ValidationUtils; import org.openecomp.sdc.common.utils.SdcCommon; import org.openecomp.sdc.datatypes.error.ErrorLevel; @@ -98,7 +94,6 @@ import org.springframework.stereotype.Service; public class OrchestrationTemplateCandidateImpl implements OrchestrationTemplateCandidate { private static final Logger LOGGER = LoggerFactory.getLogger(OrchestrationTemplateCandidateImpl.class); - private static final String EXTERNAL_CSAR_STORE = "externalCsarStore"; private final OrchestrationTemplateCandidateManager candidateManager; private final VendorSoftwareProductManager vendorSoftwareProductManager; private final ActivityLogManager activityLogManager; @@ -110,9 +105,10 @@ public class OrchestrationTemplateCandidateImpl implements OrchestrationTemplate this.vendorSoftwareProductManager = VspManagerFactory.getInstance().createInterface(); this.activityLogManager = ActivityLogManagerFactory.getInstance().createInterface(); LOGGER.info("Instantiating artifactStorageManager"); - this.artifactStorageManager = new PersistentVolumeArtifactStorageManager(readArtifactStorageConfiguration()); + final StorageFactory storageFactory = new StorageFactory(); + this.artifactStorageManager = storageFactory.createArtifactStorageManager(); LOGGER.info("Instantiating packageSizeReducer"); - this.packageSizeReducer = new CsarSizeReducer(readPackageReducerConfiguration()); + this.packageSizeReducer = storageFactory.createPackageSizeReducer().orElse(null); } // Constructor used in test to avoid mock static @@ -128,56 +124,45 @@ public class OrchestrationTemplateCandidateImpl implements OrchestrationTemplate this.packageSizeReducer = packageSizeReducer; } - private CsarPackageReducerConfiguration readPackageReducerConfiguration() { - final var commonConfigurationManager = CommonConfigurationManager.getInstance(); - final List foldersToStrip = commonConfigurationManager.getConfigValue(EXTERNAL_CSAR_STORE, "foldersToStrip", new ArrayList<>()); - final int sizeLimit = commonConfigurationManager.getConfigValue(EXTERNAL_CSAR_STORE, "sizeLimit", 1000000); - final int thresholdEntries = commonConfigurationManager.getConfigValue(EXTERNAL_CSAR_STORE, "thresholdEntries", 10000); - LOGGER.info("Folders to strip: '{}'", String.join(", ", foldersToStrip)); - final Set foldersToStripPathSet = foldersToStrip.stream().map(Path::of).collect(Collectors.toSet()); - return new CsarPackageReducerConfiguration(foldersToStripPathSet, sizeLimit, thresholdEntries); - } - - private ArtifactStorageConfig readArtifactStorageConfiguration() { - final var commonConfigurationManager = CommonConfigurationManager.getInstance(); - final boolean isEnabled = commonConfigurationManager.getConfigValue(EXTERNAL_CSAR_STORE, "storeCsarsExternally", false); - LOGGER.info("ArtifactConfig.isEnabled: '{}'", isEnabled); - final String storagePathString = commonConfigurationManager.getConfigValue(EXTERNAL_CSAR_STORE, "fullPath", null); - LOGGER.info("ArtifactConfig.storagePath: '{}'", storagePathString); - if (isEnabled && storagePathString == null) { - throw new OrchestrationTemplateCandidateException(EXTERNAL_CSAR_STORE_CONFIGURATION_FAILURE_MISSING_FULL_PATH.getErrorMessage()); - } - final var storagePath = storagePathString == null ? null : Path.of(storagePathString); - return new PersistentVolumeArtifactStorageConfig(isEnabled, storagePath); - } - @Override public Response upload(String vspId, String versionId, final Attachment fileToUpload, final String user) { vspId = ValidationUtils.sanitizeInputString(vspId); versionId = ValidationUtils.sanitizeInputString(versionId); final byte[] fileToUploadBytes; - final var filename = ValidationUtils.sanitizeInputString(fileToUpload.getDataHandler().getName()); + final DataHandler dataHandler = fileToUpload.getDataHandler(); + final var filename = ValidationUtils.sanitizeInputString(dataHandler.getName()); ArtifactInfo artifactInfo = null; if (artifactStorageManager.isEnabled()) { - final InputStream packageInputStream; + final Path tempArtifactPath; try { - packageInputStream = fileToUpload.getDataHandler().getInputStream(); - } catch (final IOException e) { + final ArtifactStorageConfig storageConfiguration = artifactStorageManager.getStorageConfiguration(); + + final Path folder = Path.of(storageConfiguration.getTempPath()).resolve(vspId).resolve(versionId); + tempArtifactPath = folder.resolve(UUID.randomUUID().toString()); + Files.createDirectories(folder); + try (final InputStream packageInputStream = dataHandler.getInputStream(); + final var fileOutputStream = new FileOutputStream(tempArtifactPath.toFile())) { + packageInputStream.transferTo(fileOutputStream); + } + } catch (final Exception e) { return Response.status(INTERNAL_SERVER_ERROR).entity(buildUploadResponseWithError( new ErrorMessage(ErrorLevel.ERROR, UNEXPECTED_PROBLEM_HAPPENED_WHILE_GETTING.formatMessage(filename)))).build(); } - try { - artifactInfo = artifactStorageManager.upload(vspId, versionId, packageInputStream); - } catch (final BusinessException e) { + try (final InputStream inputStream = Files.newInputStream(tempArtifactPath)) { + artifactInfo = artifactStorageManager.upload(vspId, versionId, inputStream); + } catch (final Exception e) { + LOGGER.error("Package Size Reducer not configured", e); return Response.status(INTERNAL_SERVER_ERROR).entity(buildUploadResponseWithError( new ErrorMessage(ErrorLevel.ERROR, ERROR_HAS_OCCURRED_WHILE_PERSISTING_THE_ARTIFACT.formatMessage(filename)))).build(); } try { - fileToUploadBytes = packageSizeReducer.reduce(artifactInfo.getPath()); - } catch (final BusinessException e) { + fileToUploadBytes = packageSizeReducer.reduce(tempArtifactPath); + Files.delete(tempArtifactPath); + } catch (final Exception e) { + LOGGER.error("Package Size Reducer not configured", e); return Response.status(INTERNAL_SERVER_ERROR).entity(buildUploadResponseWithError( - new ErrorMessage(ErrorLevel.ERROR, ERROR_HAS_OCCURRED_WHILE_REDUCING_THE_ARTIFACT_SIZE.formatMessage(artifactInfo.getPath())))) - .build(); + new ErrorMessage(ErrorLevel.ERROR, + ERROR_HAS_OCCURRED_WHILE_REDUCING_THE_ARTIFACT_SIZE.formatMessage(tempArtifactPath.toString())))).build(); } } else { fileToUploadBytes = fileToUpload.getObject(byte[].class); @@ -192,12 +177,16 @@ public class OrchestrationTemplateCandidateImpl implements OrchestrationTemplate if (onboardPackageInfo == null) { final UploadFileResponseDto uploadFileResponseDto = buildUploadResponseWithError( new ErrorMessage(ErrorLevel.ERROR, PACKAGE_PROCESS_ERROR.formatMessage(filename))); - return Response.ok(uploadFileResponseDto) - .build(); + return Response.ok(uploadFileResponseDto).build(); } final var version = new Version(versionId); final var vspDetails = vendorSoftwareProductManager.getVsp(vspId, version); - return processOnboardPackage(onboardPackageInfo, vspDetails, errorMessages); + final Response response = processOnboardPackage(onboardPackageInfo, vspDetails, errorMessages); + final UploadFileResponseDto entity = (UploadFileResponseDto) response.getEntity(); + if (artifactStorageManager.isEnabled() && !entity.getErrors().isEmpty()) { + artifactStorageManager.delete(artifactInfo); + } + return response; } private Response processOnboardPackage(final OnboardPackageInfo onboardPackageInfo, final VspDetails vspDetails, diff --git a/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/java/org/openecomp/sdcrests/vsp/rest/services/OrchestrationTemplateCandidateImplTest.java b/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/java/org/openecomp/sdcrests/vsp/rest/services/OrchestrationTemplateCandidateImplTest.java index edf29b75c5..2d2c30865a 100644 --- a/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/java/org/openecomp/sdcrests/vsp/rest/services/OrchestrationTemplateCandidateImplTest.java +++ b/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/java/org/openecomp/sdcrests/vsp/rest/services/OrchestrationTemplateCandidateImplTest.java @@ -32,6 +32,8 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.when; import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; @@ -49,14 +51,18 @@ import org.apache.cxf.jaxrs.ext.multipart.ContentDisposition; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentMatchers; +import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.openecomp.core.utilities.orchestration.OnboardingTypesEnum; import org.openecomp.sdc.activitylog.ActivityLogManager; import org.openecomp.sdc.be.csar.storage.ArtifactStorageManager; +import org.openecomp.sdc.be.csar.storage.MinIoArtifactInfo; +import org.openecomp.sdc.be.csar.storage.MinIoStorageArtifactStorageConfig; +import org.openecomp.sdc.be.csar.storage.MinIoStorageArtifactStorageConfig.Credentials; +import org.openecomp.sdc.be.csar.storage.MinIoStorageArtifactStorageConfig.EndPoint; import org.openecomp.sdc.be.csar.storage.PackageSizeReducer; -import org.openecomp.sdc.be.csar.storage.PersistentStorageArtifactInfo; import org.openecomp.sdc.logging.api.Logger; import org.openecomp.sdc.logging.api.LoggerFactory; import org.openecomp.sdc.vendorsoftwareproduct.OrchestrationTemplateCandidateManager; @@ -88,6 +94,7 @@ class OrchestrationTemplateCandidateImplTest { private ArtifactStorageManager artifactStorageManager; @Mock private PackageSizeReducer packageSizeReducer; + @InjectMocks private OrchestrationTemplateCandidateImpl orchestrationTemplateCandidate; @BeforeEach @@ -132,20 +139,15 @@ class OrchestrationTemplateCandidateImplTest { ArgumentMatchers.eq(candidateId), ArgumentMatchers.any())).thenReturn(Optional.of(fds)); - orchestrationTemplateCandidate = - new OrchestrationTemplateCandidateImpl(candidateManager, vendorSoftwareProductManager, activityLogManager, - artifactStorageManager, packageSizeReducer); - } catch (Exception e) { logger.error(e.getMessage(), e); } } @Test - void uploadSignedTest() { + void uploadSignedTest() throws IOException { Response response = orchestrationTemplateCandidate - .upload("1", "1", mockAttachment("filename.zip", this.getClass().getResource("/files/sample-signed.zip")), - "1"); + .upload("1", "1", mockAttachment("filename.zip", this.getClass().getResource("/files/sample-signed.zip")), user); assertEquals(Status.OK.getStatusCode(), response.getStatus()); assertTrue(((UploadFileResponseDto) response.getEntity()).getErrors().isEmpty()); } @@ -153,7 +155,7 @@ class OrchestrationTemplateCandidateImplTest { @Test void uploadNotSignedTest() throws IOException { Response response = orchestrationTemplateCandidate.upload("1", "1", - mockAttachment("filename.csar", this.getClass().getResource("/files/sample-not-signed.csar")), "1"); + mockAttachment("filename.csar", this.getClass().getResource("/files/sample-not-signed.csar")), user); assertEquals(Status.OK.getStatusCode(), response.getStatus()); assertTrue(((UploadFileResponseDto) response.getEntity()).getErrors().isEmpty()); } @@ -161,23 +163,29 @@ class OrchestrationTemplateCandidateImplTest { @Test void uploadNotSignedArtifactStorageManagerIsEnabledTest() throws IOException { when(artifactStorageManager.isEnabled()).thenReturn(true); + when(artifactStorageManager.getStorageConfiguration()).thenReturn( + new MinIoStorageArtifactStorageConfig(true, new EndPoint("host", 9000, false), new Credentials("accessKey", "secretKey"), "tempPath")); + final Path path = Path.of("src/test/resources/files/sample-not-signed.csar"); - when(artifactStorageManager.upload(anyString(), anyString(), any())).thenReturn(new PersistentStorageArtifactInfo(path)); + when(artifactStorageManager.upload(anyString(), anyString(), any())).thenReturn(new MinIoArtifactInfo("vspId", "name")); final byte[] bytes = Files.readAllBytes(path); when(packageSizeReducer.reduce(any())).thenReturn(bytes); Response response = orchestrationTemplateCandidate.upload("1", "1", - mockAttachment("filename.csar", this.getClass().getResource("/files/sample-not-signed.csar")), "1"); + mockAttachment("filename.csar", this.getClass().getResource("/files/sample-not-signed.csar")), user); assertEquals(Status.OK.getStatusCode(), response.getStatus()); assertTrue(((UploadFileResponseDto) response.getEntity()).getErrors().isEmpty()); } - private Attachment mockAttachment(final String fileName, final URL fileToUpload) { + private Attachment mockAttachment(final String fileName, final URL fileToUpload) throws IOException { final Attachment attachment = Mockito.mock(Attachment.class); + final InputStream inputStream = Mockito.mock(InputStream.class); when(attachment.getContentDisposition()).thenReturn(new ContentDisposition("test")); final DataHandler dataHandler = Mockito.mock(DataHandler.class); when(dataHandler.getName()).thenReturn(fileName); when(attachment.getDataHandler()).thenReturn(dataHandler); + when(dataHandler.getInputStream()).thenReturn(inputStream); + when(inputStream.transferTo(any(OutputStream.class))).thenReturn(0L); byte[] bytes = "upload package Test".getBytes(); if (Objects.nonNull(fileToUpload)) { try { @@ -192,9 +200,9 @@ class OrchestrationTemplateCandidateImplTest { } @Test - void uploadSignNotValidTest() { + void uploadSignNotValidTest() throws IOException { Response response = orchestrationTemplateCandidate - .upload("1", "1", mockAttachment("filename.zip", null), "1"); + .upload("1", "1", mockAttachment("filename.zip", null), user); assertEquals(Status.NOT_ACCEPTABLE.getStatusCode(), response.getStatus()); assertFalse(((UploadFileResponseDto) response.getEntity()).getErrors().isEmpty()); } diff --git a/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/main/java/org/openecomp/sdc/vendorsoftwareproduct/impl/orchestration/csar/validation/CsarSecurityValidator.java b/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/main/java/org/openecomp/sdc/vendorsoftwareproduct/impl/orchestration/csar/validation/CsarSecurityValidator.java index bf5abe3737..781b4a6e2c 100644 --- a/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/main/java/org/openecomp/sdc/vendorsoftwareproduct/impl/orchestration/csar/validation/CsarSecurityValidator.java +++ b/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/main/java/org/openecomp/sdc/vendorsoftwareproduct/impl/orchestration/csar/validation/CsarSecurityValidator.java @@ -61,7 +61,7 @@ public class CsarSecurityValidator { } private boolean isArtifactInfoPresent(final ArtifactInfo artifactInfo) { - return artifactInfo != null && artifactInfo.getPath() != null; + return artifactInfo != null; } } diff --git a/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/main/java/org/openecomp/sdc/vendorsoftwareproduct/security/SecurityManager.java b/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/main/java/org/openecomp/sdc/vendorsoftwareproduct/security/SecurityManager.java index fec15b5fcc..53728c0489 100644 --- a/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/main/java/org/openecomp/sdc/vendorsoftwareproduct/security/SecurityManager.java +++ b/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/main/java/org/openecomp/sdc/vendorsoftwareproduct/security/SecurityManager.java @@ -19,12 +19,12 @@ */ package org.openecomp.sdc.vendorsoftwareproduct.security; -import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; - import com.google.common.collect.ImmutableSet; +import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; +import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -56,7 +56,8 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; -import java.util.zip.ZipFile; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; import org.bouncycastle.asn1.cms.ContentInfo; import org.bouncycastle.cert.X509CertificateHolder; import org.bouncycastle.cms.CMSException; @@ -69,7 +70,9 @@ import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.openssl.PEMParser; import org.bouncycastle.operator.OperatorCreationException; import org.openecomp.sdc.be.csar.storage.ArtifactInfo; -import org.openecomp.sdc.common.errors.SdcRuntimeException; +import org.openecomp.sdc.be.csar.storage.ArtifactStorageConfig; +import org.openecomp.sdc.be.csar.storage.ArtifactStorageManager; +import org.openecomp.sdc.be.csar.storage.StorageFactory; import org.openecomp.sdc.logging.api.Logger; import org.openecomp.sdc.logging.api.LoggerFactory; import org.openecomp.sdc.vendorsoftwareproduct.types.OnboardSignedPackage; @@ -83,9 +86,10 @@ public class SecurityManager { public static final Set ALLOWED_SIGNATURE_EXTENSIONS = Set.of("cms"); public static final Set ALLOWED_CERTIFICATE_EXTENSIONS = Set.of("cert", "crt"); private static final String CERTIFICATE_DEFAULT_LOCATION = "cert"; - private static final Logger logger = LoggerFactory.getLogger(SecurityManager.class); + private static final Logger LOGGER = LoggerFactory.getLogger(SecurityManager.class); private static final String UNEXPECTED_ERROR_OCCURRED_DURING_SIGNATURE_VALIDATION = "Unexpected error occurred during signature validation!"; private static final String COULD_NOT_VERIFY_SIGNATURE = "Could not verify signature!"; + private static final String EXTERNAL_CSAR_STORE = "externalCsarStore"; static { if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) { @@ -120,7 +124,7 @@ public class SecurityManager { //if file number in certificate directory changed reload certs String[] certFiles = certificateDirectory.list(); if (certFiles == null) { - logger.error("Certificate directory is empty!"); + LOGGER.error("Certificate directory is empty!"); return ImmutableSet.copyOf(new HashSet<>()); } if (trustedCertificates.size() != certFiles.length) { @@ -160,7 +164,7 @@ public class SecurityManager { } return verify(packageCert, new CMSSignedData(new CMSProcessableByteArray(innerPackageFile), ContentInfo.getInstance(parsedObject))); } catch (final IOException | CMSException e) { - logger.error(e.getMessage(), e); + LOGGER.error(e.getMessage(), e); throw new SecurityManagerException(UNEXPECTED_ERROR_OCCURRED_DURING_SIGNATURE_VALIDATION, e); } } @@ -168,14 +172,27 @@ public class SecurityManager { public boolean verifyPackageSignedData(final OnboardSignedPackage signedPackage, final ArtifactInfo artifactInfo) throws SecurityManagerException { boolean fail = false; + + final StorageFactory storageFactory = new StorageFactory(); + final ArtifactStorageManager artifactStorageManager = storageFactory.createArtifactStorageManager(); + final ArtifactStorageConfig storageConfiguration = artifactStorageManager.getStorageConfiguration(); + final var fileContentHandler = signedPackage.getFileContentHandler(); byte[] packageCert = null; final Optional certificateFilePath = signedPackage.getCertificateFilePath(); if (certificateFilePath.isPresent()) { packageCert = fileContentHandler.getFileContent(certificateFilePath.get()); } - final var path = artifactInfo.getPath(); - final var target = Path.of(path.toString() + "." + UUID.randomUUID()); + + final Path folder = Path.of(storageConfiguration.getTempPath()); + try { + Files.createDirectories(folder); + } catch (final IOException e) { + fail = true; + throw new SecurityManagerException(String.format("Failed to create directory '%s'", folder), e); + } + + final var target = folder.resolve(UUID.randomUUID().toString()); try (final var signatureStream = new ByteArrayInputStream(fileContentHandler.getFileContent(signedPackage.getSignatureFilePath())); final var pemParser = new PEMParser(new InputStreamReader(signatureStream))) { @@ -185,16 +202,18 @@ public class SecurityManager { throw new SecurityManagerException("Signature is not recognized"); } - if (!findCSARandExtract(path, target)) { - fail = true; - return false; + try (final InputStream inputStream = artifactStorageManager.get(artifactInfo)) { + if (!findCSARandExtract(inputStream, target)) { + fail = true; + return false; + } } final var verify = verify(packageCert, new CMSSignedData(new CMSProcessableFile(target.toFile()), ContentInfo.getInstance(parsedObject))); fail = !verify; return verify; } catch (final IOException e) { fail = true; - logger.error(e.getMessage(), e); + LOGGER.error(e.getMessage(), e); throw new SecurityManagerException(UNEXPECTED_ERROR_OCCURRED_DURING_SIGNATURE_VALIDATION, e); } catch (final CMSException e) { fail = true; @@ -205,7 +224,7 @@ public class SecurityManager { } finally { deleteFile(target); if (fail) { - deleteFile(path); + artifactStorageManager.delete(artifactInfo); } } } @@ -214,7 +233,7 @@ public class SecurityManager { try { Files.delete(filePath); } catch (final IOException e) { - logger.warn("Failed to delete '{}' after verifying package signed data", filePath, e); + LOGGER.warn("Failed to delete '{}' after verifying package signed data", filePath, e); } } @@ -246,20 +265,25 @@ public class SecurityManager { } } - private boolean findCSARandExtract(final Path path, final Path target) throws IOException { + private boolean findCSARandExtract(final InputStream inputStream, final Path target) throws IOException { final AtomicBoolean found = new AtomicBoolean(false); - try (final var zf = new ZipFile(path.toString())) { - zf.entries().asIterator().forEachRemaining(entry -> { - final var entryName = entry.getName(); - if (!entry.isDirectory() && entryName.toLowerCase().endsWith(".csar")) { - try { - Files.copy(zf.getInputStream(entry), target, REPLACE_EXISTING); - } catch (final IOException e) { - throw new SdcRuntimeException(UNEXPECTED_ERROR_OCCURRED_DURING_SIGNATURE_VALIDATION, e); + + final var zipInputStream = new ZipInputStream(inputStream); + ZipEntry zipEntry; + byte[] buffer = new byte[2048]; + while ((zipEntry = zipInputStream.getNextEntry()) != null) { + final var entryName = zipEntry.getName(); + if (!zipEntry.isDirectory() && entryName.toLowerCase().endsWith(".csar")) { + try (final FileOutputStream fos = new FileOutputStream(target.toFile()); + final BufferedOutputStream bos = new BufferedOutputStream(fos, buffer.length)) { + + int len; + while ((len = zipInputStream.read(buffer)) > 0) { + bos.write(buffer, 0, len); } - found.set(true); } - }); + found.set(true); + } } return found.get(); } @@ -289,12 +313,12 @@ public class SecurityManager { private void processCertificateDir() throws SecurityManagerException { if (!certificateDirectory.exists() || !certificateDirectory.isDirectory()) { - logger.error("Issue with certificate directory, check if exists!"); + LOGGER.error("Issue with certificate directory, check if exists!"); return; } File[] files = certificateDirectory.listFiles(); if (files == null) { - logger.error("Certificate directory is empty!"); + LOGGER.error("Certificate directory is empty!"); return; } for (File f : files) { @@ -399,10 +423,10 @@ public class SecurityManager { try { cert.checkValidity(); } catch (CertificateExpiredException e) { - logger.error(e.getMessage(), e); + LOGGER.error(e.getMessage(), e); return true; } catch (CertificateNotYetValidException e) { - logger.error(e.getMessage(), e); + LOGGER.error(e.getMessage(), e); return false; } return false; diff --git a/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/java/org/openecomp/sdc/vendorsoftwareproduct/impl/orchestration/csar/validation/CsarSecurityValidatorTest.java b/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/java/org/openecomp/sdc/vendorsoftwareproduct/impl/orchestration/csar/validation/CsarSecurityValidatorTest.java index 96d11eb148..5f880701f3 100644 --- a/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/java/org/openecomp/sdc/vendorsoftwareproduct/impl/orchestration/csar/validation/CsarSecurityValidatorTest.java +++ b/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/java/org/openecomp/sdc/vendorsoftwareproduct/impl/orchestration/csar/validation/CsarSecurityValidatorTest.java @@ -24,25 +24,43 @@ import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; -import static org.mockito.MockitoAnnotations.initMocks; - +import static org.mockito.MockitoAnnotations.openMocks; +import static org.openecomp.sdc.be.csar.storage.StorageFactory.StorageType.MINIO; + +import io.minio.GetObjectArgs; +import io.minio.GetObjectResponse; +import io.minio.MinioClient; +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.UUID; import java.util.stream.Collectors; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Answers; import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; import org.openecomp.sdc.be.csar.storage.ArtifactInfo; -import org.openecomp.sdc.be.csar.storage.PersistentStorageArtifactInfo; +import org.openecomp.sdc.be.csar.storage.MinIoArtifactInfo; +import org.openecomp.sdc.common.CommonConfigurationManager; import org.openecomp.sdc.vendorsoftwareproduct.impl.onboarding.OnboardingPackageProcessor; import org.openecomp.sdc.vendorsoftwareproduct.impl.onboarding.validation.CnfPackageValidator; import org.openecomp.sdc.vendorsoftwareproduct.security.SecurityManager; @@ -50,6 +68,7 @@ import org.openecomp.sdc.vendorsoftwareproduct.security.SecurityManagerException import org.openecomp.sdc.vendorsoftwareproduct.types.OnboardPackageInfo; import org.openecomp.sdc.vendorsoftwareproduct.types.OnboardSignedPackage; +@ExtendWith(MockitoExtension.class) class CsarSecurityValidatorTest { private static final String BASE_DIR = "/vspmanager.csar/signing/"; @@ -57,6 +76,16 @@ class CsarSecurityValidatorTest { private CsarSecurityValidator csarSecurityValidator; @Mock private SecurityManager securityManager; + @Mock + private CommonConfigurationManager commonConfigurationManager; + @Mock + private MinioClient minioClient; + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private MinioClient.Builder builderMinio; + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private GetObjectArgs.Builder getObjectArgsBuilder; + @Mock + private GetObjectArgs getObjectArgs; @AfterEach void tearDown() throws Exception { @@ -74,7 +103,7 @@ class CsarSecurityValidatorTest { @BeforeEach public void setUp() throws Exception { - initMocks(this); + openMocks(this); csarSecurityValidator = new CsarSecurityValidator(securityManager); backup(); } @@ -88,9 +117,9 @@ class CsarSecurityValidatorTest { } @Test - void isSignatureValidTestCorrectStructureAndValidSignatureExists() throws SecurityManagerException, IOException { + void isSignatureValidTestCorrectStructureAndValidSignatureExists() throws SecurityManagerException { final byte[] packageBytes = getFileBytesOrFail("signed-package.zip"); - final OnboardPackageInfo onboardPackageInfo = loadSignedPackageWithArtifactInfo("signed-package.zip", packageBytes, null); + final OnboardPackageInfo onboardPackageInfo = loadSignedPackageWithArtifactInfoS3Store("signed-package.zip", packageBytes, null); when(securityManager.verifyPackageSignedData(any(OnboardSignedPackage.class), any(ArtifactInfo.class))).thenReturn(true); final boolean isSignatureValid = csarSecurityValidator .verifyPackageSignature((OnboardSignedPackage) onboardPackageInfo.getOriginalOnboardPackage(), onboardPackageInfo.getArtifactInfo()); @@ -98,15 +127,53 @@ class CsarSecurityValidatorTest { } @Test - void isSignatureValidTestCorrectStructureAndNotValidSignatureExists() throws SecurityManagerException { - final byte[] packageBytes = getFileBytesOrFail("signed-package-tampered-data.zip"); - final OnboardPackageInfo onboardPackageInfo = loadSignedPackageWithArtifactInfo("signed-package-tampered-data.zip", packageBytes, null); - //no mocked securityManager - csarSecurityValidator = new CsarSecurityValidator(); - Assertions.assertThrows(SecurityManagerException.class, () -> { - csarSecurityValidator - .verifyPackageSignature((OnboardSignedPackage) onboardPackageInfo.getOriginalOnboardPackage(), onboardPackageInfo.getArtifactInfo()); - }); + void isSignatureValidTestCorrectStructureAndNotValidSignatureExists() throws Exception { + + final Map endpoint = new HashMap<>(); + endpoint.put("host", "localhost"); + endpoint.put("port", 9000); + final Map credentials = new HashMap<>(); + credentials.put("accessKey", "login"); + credentials.put("secretKey", "password"); + + try (MockedStatic utilities = Mockito.mockStatic(CommonConfigurationManager.class)) { + utilities.when(CommonConfigurationManager::getInstance).thenReturn(commonConfigurationManager); + try (MockedStatic minioUtilities = Mockito.mockStatic(MinioClient.class)) { + minioUtilities.when(MinioClient::builder).thenReturn(builderMinio); + when(builderMinio + .endpoint(anyString(), anyInt(), anyBoolean()) + .credentials(anyString(), anyString()) + .build() + ).thenReturn(minioClient); + + when(commonConfigurationManager.getConfigValue("externalCsarStore", "endpoint", null)).thenReturn(endpoint); + when(commonConfigurationManager.getConfigValue("externalCsarStore", "credentials", null)).thenReturn(credentials); + when(commonConfigurationManager.getConfigValue("externalCsarStore", "tempPath", null)).thenReturn("cert/2-file-signed-package"); + when(commonConfigurationManager.getConfigValue(eq("externalCsarStore"), eq("storageType"), any())).thenReturn(MINIO.name()); + + final byte[] packageBytes = getFileBytesOrFail("signed-package-tampered-data.zip"); + + when(getObjectArgsBuilder + .bucket(anyString()) + .object(anyString()) + .build() + ).thenReturn(getObjectArgs); + + when(minioClient.getObject(any(GetObjectArgs.class))) + .thenReturn(new GetObjectResponse(null, "bucket", "", "objectName", + new BufferedInputStream(new ByteArrayInputStream(packageBytes)))); + + final OnboardPackageInfo onboardPackageInfo = loadSignedPackageWithArtifactInfoS3Store("signed-package-tampered-data.zip", + packageBytes, + null); + //no mocked securityManager + csarSecurityValidator = new CsarSecurityValidator(); + Assertions.assertThrows(SecurityManagerException.class, () -> { + csarSecurityValidator.verifyPackageSignature((OnboardSignedPackage) onboardPackageInfo.getOriginalOnboardPackage(), + onboardPackageInfo.getArtifactInfo()); + }); + } + } } @Test @@ -148,11 +215,10 @@ class CsarSecurityValidatorTest { CsarSecurityValidatorTest.class.getResource(BASE_DIR + path).toURI())); } - private OnboardPackageInfo loadSignedPackageWithArtifactInfo(final String packageName, final byte[] packageBytes, - final CnfPackageValidator cnfPackageValidator) { + private OnboardPackageInfo loadSignedPackageWithArtifactInfoS3Store(final String packageName, final byte[] packageBytes, + final CnfPackageValidator cnfPackageValidator) { final OnboardingPackageProcessor onboardingPackageProcessor = - new OnboardingPackageProcessor(packageName, packageBytes, cnfPackageValidator, - new PersistentStorageArtifactInfo(Path.of("src/test/resources/vspmanager.csar/signing/signed-package.zip"))); + new OnboardingPackageProcessor(packageName, packageBytes, cnfPackageValidator, new MinIoArtifactInfo("bucket", "object")); final OnboardPackageInfo onboardPackageInfo = onboardingPackageProcessor.getOnboardPackageInfo().orElse(null); if (onboardPackageInfo == null) { fail("Unexpected error. Could not load original package"); diff --git a/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/java/org/openecomp/sdc/vendorsoftwareproduct/security/SecurityManagerTest.java b/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/java/org/openecomp/sdc/vendorsoftwareproduct/security/SecurityManagerTest.java index 6dc5517c45..afc43967c9 100644 --- a/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/java/org/openecomp/sdc/vendorsoftwareproduct/security/SecurityManagerTest.java +++ b/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/java/org/openecomp/sdc/vendorsoftwareproduct/security/SecurityManagerTest.java @@ -22,29 +22,61 @@ package org.openecomp.sdc.vendorsoftwareproduct.security; import static junit.framework.TestCase.assertEquals; import static junit.framework.TestCase.assertTrue; - +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.openMocks; +import static org.openecomp.sdc.be.csar.storage.StorageFactory.StorageType.MINIO; + +import io.minio.GetObjectArgs; +import io.minio.GetObjectResponse; +import io.minio.MinioClient; +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.net.URISyntaxException; import java.nio.file.Files; -import java.nio.file.Path; import java.nio.file.Paths; +import java.util.HashMap; +import java.util.Map; import org.apache.commons.io.FileUtils; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.openecomp.sdc.be.csar.storage.PersistentStorageArtifactInfo; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Answers; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; +import org.openecomp.sdc.be.csar.storage.MinIoArtifactInfo; +import org.openecomp.sdc.common.CommonConfigurationManager; import org.openecomp.sdc.vendorsoftwareproduct.impl.onboarding.OnboardingPackageProcessor; import org.openecomp.sdc.vendorsoftwareproduct.impl.onboarding.validation.CnfPackageValidator; import org.openecomp.sdc.vendorsoftwareproduct.types.OnboardPackageInfo; import org.openecomp.sdc.vendorsoftwareproduct.types.OnboardSignedPackage; +@ExtendWith(MockitoExtension.class) class SecurityManagerTest { private File certDir; private String cerDirPath = "/tmp/cert/"; private SecurityManager securityManager; + @Mock + private CommonConfigurationManager commonConfigurationManager; + @Mock + private MinioClient minioClient; + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private MinioClient.Builder builderMinio; + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private GetObjectArgs.Builder getObjectArgsBuilder; + @Mock + private GetObjectArgs getObjectArgs; private File prepareCertFiles(String origFilePath, String newFilePath) throws IOException, URISyntaxException { File origFile = new File(getClass().getResource(origFilePath).toURI()); @@ -60,12 +92,14 @@ class SecurityManagerTest { @BeforeEach public void setUp() throws IOException { + openMocks(this); certDir = new File(cerDirPath); if (certDir.exists()) { tearDown(); } certDir.mkdirs(); securityManager = new SecurityManager(certDir.getPath()); + } @AfterEach @@ -123,18 +157,51 @@ class SecurityManagerTest { } @Test - void verifySignedDataTestCertIncludedIntoSignatureArtifactStorageManagerIsEnabled() - throws IOException, URISyntaxException, SecurityManagerException { - prepareCertFiles("/cert/rootCA.cert", cerDirPath + "root.cert"); - byte[] fileToUploadBytes = readAllBytes("/cert/2-file-signed-package/2-file-signed-package.zip"); - - final var onboardingPackageProcessor = new OnboardingPackageProcessor("2-file-signed-package.zip", fileToUploadBytes, - new CnfPackageValidator(), - new PersistentStorageArtifactInfo(Path.of("src/test/resources/cert/2-file-signed-package/2-file-signed-package.zip"))); - final OnboardPackageInfo onboardPackageInfo = onboardingPackageProcessor.getOnboardPackageInfo().orElse(null); - - assertTrue(securityManager - .verifyPackageSignedData((OnboardSignedPackage) onboardPackageInfo.getOriginalOnboardPackage(), onboardPackageInfo.getArtifactInfo())); + void verifySignedDataTestCertIncludedIntoSignatureArtifactStorageManagerIsEnabled() throws Exception { + + final Map endpoint = new HashMap<>(); + endpoint.put("host", "localhost"); + endpoint.put("port", 9000); + final Map credentials = new HashMap<>(); + credentials.put("accessKey", "login"); + credentials.put("secretKey", "password"); + + try (MockedStatic utilities = Mockito.mockStatic(CommonConfigurationManager.class)) { + utilities.when(CommonConfigurationManager::getInstance).thenReturn(commonConfigurationManager); + try (MockedStatic minioUtilities = Mockito.mockStatic(MinioClient.class)) { + minioUtilities.when(MinioClient::builder).thenReturn(builderMinio); + when(builderMinio + .endpoint(anyString(), anyInt(), anyBoolean()) + .credentials(anyString(), anyString()) + .build() + ).thenReturn(minioClient); + + when(commonConfigurationManager.getConfigValue("externalCsarStore", "endpoint", null)).thenReturn(endpoint); + when(commonConfigurationManager.getConfigValue("externalCsarStore", "credentials", null)).thenReturn(credentials); + when(commonConfigurationManager.getConfigValue("externalCsarStore", "tempPath", null)).thenReturn("cert/2-file-signed-package"); + when(commonConfigurationManager.getConfigValue(eq("externalCsarStore"), eq("storageType"), any())).thenReturn(MINIO.name()); + + prepareCertFiles("/cert/rootCA.cert", cerDirPath + "root.cert"); + byte[] fileToUploadBytes = readAllBytes("/cert/2-file-signed-package/2-file-signed-package.zip"); + when(getObjectArgsBuilder + .bucket(anyString()) + .object(anyString()) + .build() + ).thenReturn(getObjectArgs); + + when(minioClient.getObject(any(GetObjectArgs.class))) + .thenReturn(new GetObjectResponse(null, "bucket", "", "objectName", + new BufferedInputStream(new ByteArrayInputStream(fileToUploadBytes)))); + + final var onboardingPackageProcessor = new OnboardingPackageProcessor("2-file-signed-package.zip", fileToUploadBytes, + new CnfPackageValidator(), new MinIoArtifactInfo("bucket", "objectName")); + final OnboardPackageInfo onboardPackageInfo = onboardingPackageProcessor.getOnboardPackageInfo().orElse(null); + + assertTrue(securityManager + .verifyPackageSignedData((OnboardSignedPackage) onboardPackageInfo.getOriginalOnboardPackage(), + onboardPackageInfo.getArtifactInfo())); + } + } } @Test @@ -158,18 +225,51 @@ class SecurityManagerTest { } @Test - void verifySignedDataTestCertNotIncludedIntoSignatureArtifactStorageManagerIsEnabled() - throws IOException, URISyntaxException, SecurityManagerException { - prepareCertFiles("/cert/rootCA.cert", cerDirPath + "root.cert"); - byte[] fileToUploadBytes = readAllBytes("/cert/3-file-signed-package/3-file-signed-package.zip"); - - final var onboardingPackageProcessor = new OnboardingPackageProcessor("3-file-signed-package.zip", fileToUploadBytes, - new CnfPackageValidator(), - new PersistentStorageArtifactInfo(Path.of("src/test/resources/cert/3-file-signed-package/3-file-signed-package.zip"))); - final OnboardPackageInfo onboardPackageInfo = onboardingPackageProcessor.getOnboardPackageInfo().orElse(null); - - assertTrue(securityManager - .verifyPackageSignedData((OnboardSignedPackage) onboardPackageInfo.getOriginalOnboardPackage(), onboardPackageInfo.getArtifactInfo())); + void verifySignedDataTestCertNotIncludedIntoSignatureArtifactStorageManagerIsEnabled() throws Exception { + + final Map endpoint = new HashMap<>(); + endpoint.put("host", "localhost"); + endpoint.put("port", 9000); + final Map credentials = new HashMap<>(); + credentials.put("accessKey", "login"); + credentials.put("secretKey", "password"); + + try (MockedStatic utilities = Mockito.mockStatic(CommonConfigurationManager.class)) { + utilities.when(CommonConfigurationManager::getInstance).thenReturn(commonConfigurationManager); + try (MockedStatic minioUtilities = Mockito.mockStatic(MinioClient.class)) { + minioUtilities.when(MinioClient::builder).thenReturn(builderMinio); + when(builderMinio + .endpoint(anyString(), anyInt(), anyBoolean()) + .credentials(anyString(), anyString()) + .build() + ).thenReturn(minioClient); + + when(commonConfigurationManager.getConfigValue("externalCsarStore", "endpoint", null)).thenReturn(endpoint); + when(commonConfigurationManager.getConfigValue("externalCsarStore", "credentials", null)).thenReturn(credentials); + when(commonConfigurationManager.getConfigValue("externalCsarStore", "tempPath", null)).thenReturn("tempPath"); + when(commonConfigurationManager.getConfigValue(eq("externalCsarStore"), eq("storageType"), any())).thenReturn(MINIO.name()); + + prepareCertFiles("/cert/rootCA.cert", cerDirPath + "root.cert"); + byte[] fileToUploadBytes = readAllBytes("/cert/3-file-signed-package/3-file-signed-package.zip"); + when(getObjectArgsBuilder + .bucket(anyString()) + .object(anyString()) + .build() + ).thenReturn(getObjectArgs); + + when(minioClient.getObject(any(GetObjectArgs.class))) + .thenReturn(new GetObjectResponse(null, "bucket", "", "objectName", + new BufferedInputStream(new ByteArrayInputStream(fileToUploadBytes)))); + + final var onboardingPackageProcessor = new OnboardingPackageProcessor("3-file-signed-package.zip", fileToUploadBytes, + new CnfPackageValidator(), new MinIoArtifactInfo("bucket", "objectName")); + final OnboardPackageInfo onboardPackageInfo = onboardingPackageProcessor.getOnboardPackageInfo().orElse(null); + + assertTrue(securityManager + .verifyPackageSignedData((OnboardSignedPackage) onboardPackageInfo.getOriginalOnboardPackage(), + onboardPackageInfo.getArtifactInfo())); + } + } } @Test diff --git a/openecomp-be/dist/sdc-onboard-backend-docker/artifacts/chef-repo/cookbooks/sdc-onboard-backend/templates/default/configuration.yaml.erb b/openecomp-be/dist/sdc-onboard-backend-docker/artifacts/chef-repo/cookbooks/sdc-onboard-backend/templates/default/configuration.yaml.erb index d2c3d10805..3b02114334 100644 --- a/openecomp-be/dist/sdc-onboard-backend-docker/artifacts/chef-repo/cookbooks/sdc-onboard-backend/templates/default/configuration.yaml.erb +++ b/openecomp-be/dist/sdc-onboard-backend-docker/artifacts/chef-repo/cookbooks/sdc-onboard-backend/templates/default/configuration.yaml.erb @@ -1,35 +1,35 @@ catalogNotificationsConfig: - # catalog backend protocol - <% if node[:disableHttp] -%> - catalogBeProtocol: https - <% else %> - catalogBeProtocol: http - <% end -%> - catalogBeHttpPort: <%= @catalog_be_http_port %> - catalogBeSslPort: <%= @catalog_be_ssl_port %> - catalogBeFqdn: <%= @catalog_be_fqdn %> - # do not remove the "" from catalog_notification_url. it is escaping % characters coming from AUTO.json - catalogNotificationUrl: "<%= @catalog_notification_url %>" + # catalog backend protocol + <% if node[:disableHttp] -%> + catalogBeProtocol: https + <% else %> + catalogBeProtocol: http + <% end -%> + catalogBeHttpPort: <%= @catalog_be_http_port %> + catalogBeSslPort: <%= @catalog_be_ssl_port %> + catalogBeFqdn: <%= @catalog_be_fqdn %> + # do not remove the "" from catalog_notification_url. it is escaping % characters coming from AUTO.json + catalogNotificationUrl: "<%= @catalog_notification_url %>" notifications: - pollingIntervalMsec: 2000 - selectionSize: 100 - beHost: <%= @onboard_ip %> - beHttpPort: <%= @onboard_port %> + pollingIntervalMsec: 2000 + selectionSize: 100 + beHost: <%= @onboard_ip %> + beHttpPort: <%= @onboard_port %> cassandraConfig: - cassandraHosts: [<%= @cassandra_ip %>] - cassandraPort: <%= @cassandra_port %> - localDataCenter: <%= @DC_NAME %> - reconnectTimeout : 30000 - socketReadTimeout: <%= @socket_read_timeout %> - socketConnectTimeout: <%= @socket_connect_timeout %> - authenticate: true - username: <%= @cassandra_usr %> - password: <%= @cassandra_pwd %> - ssl: <%= @cassandra_ssl_enabled %> - truststorePath: <%= node['jetty']['truststore_path'] %> - truststorePassword: <%= @cassandra_truststore_password %> + cassandraHosts: [ <%= @cassandra_ip %> ] + cassandraPort: <%= @cassandra_port %> + localDataCenter: <%= @DC_NAME %> + reconnectTimeout: 30000 + socketReadTimeout: <%= @socket_read_timeout %> + socketConnectTimeout: <%= @socket_connect_timeout %> + authenticate: true + username: <%= @cassandra_usr %> + password: <%= @cassandra_pwd %> + ssl: <%= @cassandra_ssl_enabled %> + truststorePath: <%= node['jetty']['truststore_path'] %> + truststorePassword: <%= @cassandra_truststore_password %> # access restriction authCookie: @@ -42,8 +42,8 @@ authCookie: isHttpOnly: true # redirect variable name from portal.properties file redirectURL: "redirect_url" - excludedUrls: ['/.*'] - onboardingExcludedUrls: ['/.*'] + excludedUrls: [ '/.*' ] + onboardingExcludedUrls: [ '/.*' ] basicAuth: enabled: <%= @basic_auth_enabled %> @@ -55,9 +55,16 @@ zipValidation: ignoreManifest: false externalCsarStore: - storeCsarsExternally: false - fullPath: "/home/onap/temp/" + storageType: NONE # NONE, MINIO + endpoint: + host: 127.0.0.1 + port: 9000 + secure: false + credentials: + accessKey: "login" + secretKey: "password" foldersToStrip: - Files/images sizeLimit: 10000000 - thresholdEntries: 10000 \ No newline at end of file + thresholdEntries: 10000 + tempPath: "/home/onap/temp/" diff --git a/openecomp-be/lib/openecomp-common-lib/src/main/java/org/openecomp/sdc/common/errors/Messages.java b/openecomp-be/lib/openecomp-common-lib/src/main/java/org/openecomp/sdc/common/errors/Messages.java index 28ce5d2a62..21ad7a60a5 100644 --- a/openecomp-be/lib/openecomp-common-lib/src/main/java/org/openecomp/sdc/common/errors/Messages.java +++ b/openecomp-be/lib/openecomp-common-lib/src/main/java/org/openecomp/sdc/common/errors/Messages.java @@ -202,11 +202,10 @@ public enum Messages { FAILED_TO_MARK_NOTIFICATION_AS_READ("Failed to mark notifications as read"), FAILED_TO_UPDATE_LAST_SEEN_NOTIFICATION("Failed to update last seen notification for user %s"), FAILED_TO_VERIFY_SIGNATURE("Could not verify signature of signed package."), - EXTERNAL_CSAR_STORE_CONFIGURATION_FAILURE_MISSING_FULL_PATH("externalCsarStore configuration failure, missing 'fullPath'"), + EXTERNAL_CSAR_STORE_CONFIGURATION_FAILURE_MISSING("externalCsarStore configuration failure, missing '%s'"), ERROR_HAS_OCCURRED_WHILE_PERSISTING_THE_ARTIFACT("An error has occurred while persisting the artifact: %s"), ERROR_HAS_OCCURRED_WHILE_REDUCING_THE_ARTIFACT_SIZE("An error has occurred while reducing the artifact's size: %s"), - UNEXPECTED_PROBLEM_HAPPENED_WHILE_GETTING("An unexpected problem happened while getting '%s'"), - PERSISTENCE_STORE_IS_DOWN_OR_NOT_AVAILABLE("Persistence store is down or not available. Error: '%s'"); + UNEXPECTED_PROBLEM_HAPPENED_WHILE_GETTING("An unexpected problem happened while getting '%s'"); // @formatter:on private String errorMessage; diff --git a/openecomp-be/lib/openecomp-sdc-vendor-software-product-lib/openecomp-sdc-vendor-software-product-core/src/main/java/org/openecomp/sdc/vendorsoftwareproduct/dao/impl/zusammen/OrchestrationTemplateCandidateDaoZusammenImpl.java b/openecomp-be/lib/openecomp-sdc-vendor-software-product-lib/openecomp-sdc-vendor-software-product-core/src/main/java/org/openecomp/sdc/vendorsoftwareproduct/dao/impl/zusammen/OrchestrationTemplateCandidateDaoZusammenImpl.java index e5c59968fa..fbb25de0da 100644 --- a/openecomp-be/lib/openecomp-sdc-vendor-software-product-lib/openecomp-sdc-vendor-software-product-core/src/main/java/org/openecomp/sdc/vendorsoftwareproduct/dao/impl/zusammen/OrchestrationTemplateCandidateDaoZusammenImpl.java +++ b/openecomp-be/lib/openecomp-sdc-vendor-software-product-lib/openecomp-sdc-vendor-software-product-core/src/main/java/org/openecomp/sdc/vendorsoftwareproduct/dao/impl/zusammen/OrchestrationTemplateCandidateDaoZusammenImpl.java @@ -31,7 +31,6 @@ import com.amdocs.zusammen.utils.fileutils.FileUtils; import java.io.ByteArrayInputStream; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; -import java.nio.file.Path; import java.util.Optional; import lombok.AllArgsConstructor; import lombok.Getter; @@ -39,12 +38,8 @@ import org.openecomp.core.utilities.json.JsonUtil; import org.openecomp.core.utilities.orchestration.OnboardingTypesEnum; import org.openecomp.core.zusammen.api.ZusammenAdaptor; import org.openecomp.sdc.be.csar.storage.ArtifactInfo; -import org.openecomp.sdc.be.csar.storage.ArtifactStorageConfig; import org.openecomp.sdc.be.csar.storage.ArtifactStorageManager; -import org.openecomp.sdc.be.csar.storage.PersistentVolumeArtifactStorageConfig; -import org.openecomp.sdc.be.csar.storage.PersistentVolumeArtifactStorageManager; -import org.openecomp.sdc.common.CommonConfigurationManager; -import org.openecomp.sdc.common.errors.Messages; +import org.openecomp.sdc.be.csar.storage.StorageFactory; import org.openecomp.sdc.datatypes.model.ElementType; import org.openecomp.sdc.heat.datatypes.structure.ValidationStructureList; import org.openecomp.sdc.logging.api.Logger; @@ -56,15 +51,15 @@ import org.openecomp.sdc.versioning.dao.types.Version; public class OrchestrationTemplateCandidateDaoZusammenImpl implements OrchestrationTemplateCandidateDao { - private static final Logger logger = LoggerFactory.getLogger(OrchestrationTemplateCandidateDaoZusammenImpl.class); + private static final Logger LOGGER = LoggerFactory.getLogger(OrchestrationTemplateCandidateDaoZusammenImpl.class); private static final String EMPTY_DATA = "{}"; - private static final String EXTERNAL_CSAR_STORE = "externalCsarStore"; private final ZusammenAdaptor zusammenAdaptor; private final ArtifactStorageManager artifactStorageManager; public OrchestrationTemplateCandidateDaoZusammenImpl(final ZusammenAdaptor zusammenAdaptor) { this.zusammenAdaptor = zusammenAdaptor; - this.artifactStorageManager = new PersistentVolumeArtifactStorageManager(readArtifactStorageConfiguration()); + LOGGER.info("Instantiating artifactStorageManager"); + this.artifactStorageManager = new StorageFactory().createArtifactStorageManager(); } @Override @@ -74,14 +69,14 @@ public class OrchestrationTemplateCandidateDaoZusammenImpl implements Orchestrat @Override public Optional get(String vspId, Version version) { - logger.info("Getting orchestration template for vsp id {}", vspId); + LOGGER.info("Getting orchestration template for vsp id {}", vspId); SessionContext context = createSessionContext(); ElementContext elementContext = new ElementContext(vspId, version.getId()); Optional candidateElement = zusammenAdaptor .getElementByName(context, elementContext, null, ElementType.OrchestrationTemplateCandidate.name()); if (!candidateElement.isPresent() || VspZusammenUtil.hasEmptyData(candidateElement.get().getData()) || candidateElement.get().getSubElements() .isEmpty()) { - logger.info("Orchestration template for vsp id {} does not exist / has empty data", vspId); + LOGGER.info("Orchestration template for vsp id {} does not exist / has empty data", vspId); return Optional.empty(); } OrchestrationTemplateCandidateData candidate = new OrchestrationTemplateCandidateData(); @@ -89,26 +84,26 @@ public class OrchestrationTemplateCandidateDaoZusammenImpl implements Orchestrat candidateElement.get().getSubElements().stream() .map(element -> zusammenAdaptor.getElement(context, elementContext, element.getElementId().toString())) .forEach(element -> element.ifPresent(candidateInfoElement -> populateCandidate(candidate, candidateInfoElement, true))); - logger.info("Finished getting orchestration template for vsp id {}", vspId); + LOGGER.info("Finished getting orchestration template for vsp id {}", vspId); return candidate.getFileSuffix() == null ? Optional.empty() : Optional.of(candidate); } @Override public Optional getInfo(String vspId, Version version) { - logger.info("Getting orchestration template info for vsp id {}", vspId); + LOGGER.info("Getting orchestration template info for vsp id {}", vspId); SessionContext context = createSessionContext(); ElementContext elementContext = new ElementContext(vspId, version.getId()); Optional candidateElement = zusammenAdaptor .getElementInfoByName(context, elementContext, null, ElementType.OrchestrationTemplateCandidate.name()); if (!candidateElement.isPresent() || candidateElement.get().getSubElements().isEmpty()) { - logger.info("Orchestration template info for vsp id {} does not exist", vspId); + LOGGER.info("Orchestration template info for vsp id {} does not exist", vspId); return Optional.empty(); } OrchestrationTemplateCandidateData candidate = new OrchestrationTemplateCandidateData(); candidateElement.get().getSubElements().stream() .map(elementInfo -> zusammenAdaptor.getElement(context, elementContext, elementInfo.getId().toString())) .forEach(element -> element.ifPresent(candidateInfoElement -> populateCandidate(candidate, candidateInfoElement, false))); - logger.info("Finished getting orchestration template info for vsp id {}", vspId); + LOGGER.info("Finished getting orchestration template info for vsp id {}", vspId); return candidate.getFileSuffix() == null ? Optional.empty() : Optional.of(candidate); } @@ -149,7 +144,7 @@ public class OrchestrationTemplateCandidateDaoZusammenImpl implements Orchestrat @Override public void update(final String vspId, final Version version, final OrchestrationTemplateCandidateData candidateData) { - logger.info("Uploading candidate data entity for vsp id {}", vspId); + LOGGER.info("Uploading candidate data entity for vsp id {}", vspId); final ZusammenElement candidateElement = buildStructuralElement(ElementType.OrchestrationTemplateCandidate, Action.UPDATE); candidateElement.setData(new ByteArrayInputStream(candidateData.getFilesDataStructure().getBytes())); final ZusammenElement candidateContentElement = buildStructuralElement(ElementType.OrchestrationTemplateCandidateContent, Action.UPDATE); @@ -170,7 +165,7 @@ public class OrchestrationTemplateCandidateDaoZusammenImpl implements Orchestrat throw new OrchestrationTemplateCandidateDaoZusammenException("No artifact info provided"); } final ArtifactInfo artifactInfo = artifactStorageManager.persist(vspId, versionId, candidateArtifactInfo); - originalPackageElement.setData(new ByteArrayInputStream(artifactInfo.getPath().toString().getBytes(StandardCharsets.UTF_8))); + originalPackageElement.setData(new ByteArrayInputStream(artifactInfo.getInfo().getBytes(StandardCharsets.UTF_8))); } else { originalPackageElement.setData(new ByteArrayInputStream(candidateData.getOriginalFileContentData().array())); } @@ -185,12 +180,12 @@ public class OrchestrationTemplateCandidateDaoZusammenImpl implements Orchestrat final var context = createSessionContext(); final var elementContext = new ElementContext(vspId, versionId); zusammenAdaptor.saveElement(context, elementContext, candidateElement, "Update Orchestration Template Candidate"); - logger.info("Finished uploading candidate data entity for vsp id {}", vspId); + LOGGER.info("Finished uploading candidate data entity for vsp id {}", vspId); } @Override public void updateValidationData(String vspId, Version version, ValidationStructureList validationData) { - logger.info("Updating validation data of orchestration template candidate for VSP id {} ", vspId); + LOGGER.info("Updating validation data of orchestration template candidate for VSP id {} ", vspId); ZusammenElement validationDataElement = buildStructuralElement(ElementType.OrchestrationTemplateCandidateValidationData, Action.UPDATE); validationDataElement.setData(validationData == null ? new ByteArrayInputStream(EMPTY_DATA.getBytes()) : new ByteArrayInputStream(JsonUtil.object2Json(validationData).getBytes())); @@ -199,23 +194,23 @@ public class OrchestrationTemplateCandidateDaoZusammenImpl implements Orchestrat SessionContext context = createSessionContext(); ElementContext elementContext = new ElementContext(vspId, version.getId()); zusammenAdaptor.saveElement(context, elementContext, candidateElement, "Update Orchestration Template Candidate validation data"); - logger.info("Finished updating validation data of orchestration template candidate for VSP id {}", vspId); + LOGGER.info("Finished updating validation data of orchestration template candidate for VSP id {}", vspId); } @Override public void updateStructure(String vspId, Version version, FilesDataStructure fileDataStructure) { - logger.info("Updating orchestration template for VSP id {}", vspId); + LOGGER.info("Updating orchestration template for VSP id {}", vspId); ZusammenElement candidateElement = buildStructuralElement(ElementType.OrchestrationTemplateCandidate, Action.UPDATE); candidateElement.setData(new ByteArrayInputStream(JsonUtil.object2Json(fileDataStructure).getBytes())); SessionContext context = createSessionContext(); ElementContext elementContext = new ElementContext(vspId, version.getId()); zusammenAdaptor.saveElement(context, elementContext, candidateElement, "Update Orchestration Template Candidate structure"); - logger.info("Finished uploading candidate data entity for vsp id {}", vspId); + LOGGER.info("Finished uploading candidate data entity for vsp id {}", vspId); } @Override public Optional getStructure(String vspId, Version version) { - logger.info("Getting orchestration template candidate structure for vsp id {}", vspId); + LOGGER.info("Getting orchestration template candidate structure for vsp id {}", vspId); SessionContext context = createSessionContext(); ElementContext elementContext = new ElementContext(vspId, version.getId()); Optional element = zusammenAdaptor @@ -223,24 +218,10 @@ public class OrchestrationTemplateCandidateDaoZusammenImpl implements Orchestrat if (element.isPresent() && !VspZusammenUtil.hasEmptyData(element.get().getData())) { return Optional.of(new String(FileUtils.toByteArray(element.get().getData()))); } - logger.info("Finished getting orchestration template candidate structure for vsp id {}", vspId); + LOGGER.info("Finished getting orchestration template candidate structure for vsp id {}", vspId); return Optional.empty(); } - private ArtifactStorageConfig readArtifactStorageConfiguration() { - final var commonConfigurationManager = CommonConfigurationManager.getInstance(); - final boolean isEnabled = commonConfigurationManager.getConfigValue(EXTERNAL_CSAR_STORE, "storeCsarsExternally", false); - logger.info("ArtifactConfig.isEnabled: '{}'", isEnabled); - final String storagePathString = commonConfigurationManager.getConfigValue(EXTERNAL_CSAR_STORE, "fullPath", null); - logger.info("ArtifactConfig.storagePath: '{}'", storagePathString); - if (isEnabled && storagePathString == null) { - throw new OrchestrationTemplateCandidateDaoZusammenException( - Messages.EXTERNAL_CSAR_STORE_CONFIGURATION_FAILURE_MISSING_FULL_PATH.getErrorMessage()); - } - final var storagePath = storagePathString == null ? null : Path.of(storagePathString); - return new PersistentVolumeArtifactStorageConfig(isEnabled, storagePath); - } - @Getter @AllArgsConstructor private enum InfoPropertyName { -- cgit 1.2.3-korg