2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2019 Nordix Foundation
4 * Copyright (C) 2021 Nokia
5 * ================================================================================
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
17 * SPDX-License-Identifier: Apache-2.0
18 * ============LICENSE_END=========================================================
20 package org.openecomp.sdc.vendorsoftwareproduct.impl.onboarding;
22 import static org.openecomp.sdc.common.errors.Messages.COULD_NOT_READ_MANIFEST_FILE;
23 import static org.openecomp.sdc.common.errors.Messages.PACKAGE_EMPTY_ERROR;
24 import static org.openecomp.sdc.common.errors.Messages.PACKAGE_INVALID_ERROR;
25 import static org.openecomp.sdc.common.errors.Messages.PACKAGE_INVALID_EXTENSION;
26 import static org.openecomp.sdc.common.errors.Messages.PACKAGE_MISSING_INTERNAL_PACKAGE;
27 import static org.openecomp.sdc.common.errors.Messages.PACKAGE_PROCESS_ERROR;
28 import static org.openecomp.sdc.common.errors.Messages.PACKAGE_PROCESS_INTERNAL_PACKAGE_ERROR;
29 import static org.openecomp.sdc.vendorsoftwareproduct.security.SecurityManager.ALLOWED_CERTIFICATE_EXTENSIONS;
30 import static org.openecomp.sdc.vendorsoftwareproduct.security.SecurityManager.ALLOWED_SIGNATURE_EXTENSIONS;
32 import java.io.ByteArrayInputStream;
34 import java.io.FileInputStream;
35 import java.io.InputStream;
36 import java.nio.ByteBuffer;
37 import java.nio.charset.StandardCharsets;
38 import java.util.ArrayList;
39 import java.util.Collections;
40 import java.util.HashSet;
41 import java.util.List;
43 import java.util.Objects;
44 import java.util.Optional;
46 import org.apache.commons.collections4.CollectionUtils;
47 import org.apache.commons.collections4.MapUtils;
48 import org.apache.commons.io.FilenameUtils;
49 import org.openecomp.core.utilities.file.FileContentHandler;
50 import org.openecomp.core.utilities.json.JsonUtil;
51 import org.openecomp.core.utilities.orchestration.OnboardingTypesEnum;
52 import org.openecomp.sdc.common.CommonConfigurationManager;
53 import org.openecomp.sdc.common.utils.CommonUtil;
54 import org.openecomp.sdc.common.utils.SdcCommon;
55 import org.openecomp.sdc.common.zip.exception.ZipException;
56 import org.openecomp.sdc.datatypes.error.ErrorLevel;
57 import org.openecomp.sdc.datatypes.error.ErrorMessage;
58 import org.openecomp.sdc.heat.datatypes.manifest.FileData;
59 import org.openecomp.sdc.heat.datatypes.manifest.ManifestContent;
60 import org.openecomp.sdc.logging.api.Logger;
61 import org.openecomp.sdc.logging.api.LoggerFactory;
62 import org.openecomp.sdc.vendorsoftwareproduct.impl.onboarding.validation.CnfPackageValidator;
63 import org.openecomp.sdc.vendorsoftwareproduct.types.OnboardPackage;
64 import org.openecomp.sdc.vendorsoftwareproduct.types.OnboardPackageInfo;
65 import org.openecomp.sdc.vendorsoftwareproduct.types.OnboardSignedPackage;
67 public class OnboardingPackageProcessor {
69 private static final Logger LOGGER = LoggerFactory.getLogger(OnboardingPackageProcessor.class);
70 private static final String CSAR_EXTENSION = "csar";
71 private static final String ZIP_EXTENSION = "zip";
72 private final String packageFileName;
73 private final byte[] packageFileContent;
74 private final Set<ErrorMessage> errorMessages = new HashSet<>();
75 private final OnboardPackageInfo onboardPackageInfo;
76 private final CnfPackageValidator cnfPackageValidator;
77 private FileContentHandler packageContent;
79 public OnboardingPackageProcessor(final String packageFileName, final byte[] packageFileContent,
80 final CnfPackageValidator cnfPackageValidator) {
81 this.packageFileName = packageFileName;
82 this.packageFileContent = packageFileContent;
83 this.cnfPackageValidator = cnfPackageValidator;
84 onboardPackageInfo = processPackage();
87 public Optional<OnboardPackageInfo> getOnboardPackageInfo() {
88 return Optional.ofNullable(onboardPackageInfo);
91 public boolean hasErrors() {
92 return errorMessages.stream()
93 .anyMatch(error -> error.getLevel() == ErrorLevel.ERROR);
96 public boolean hasNoErrors() {
97 return errorMessages.stream()
98 .noneMatch(error -> error.getLevel() == ErrorLevel.ERROR);
101 public Set<ErrorMessage> getErrorMessages() {
102 return errorMessages;
105 private OnboardPackageInfo processPackage() {
106 OnboardPackageInfo packageInfo = null;
109 final String packageName = FilenameUtils.getBaseName(packageFileName);
110 final String packageExtension = FilenameUtils.getExtension(packageFileName);
111 if (hasSignedPackageStructure()) {
112 packageInfo = processSignedPackage(packageName, packageExtension);
114 if (packageExtension.equalsIgnoreCase(CSAR_EXTENSION)) {
115 packageInfo = processCsarPackage(packageName, packageExtension);
116 } else if (packageExtension.equalsIgnoreCase(ZIP_EXTENSION)) {
117 packageInfo = processOnapNativeZipPackage(packageName, packageExtension);
124 private void validateFile() {
125 if (!hasValidExtension()) {
126 String message = PACKAGE_INVALID_EXTENSION.formatMessage(packageFileName, String.join(", ", CSAR_EXTENSION, ZIP_EXTENSION));
127 reportError(ErrorLevel.ERROR, message);
130 packageContent = CommonUtil.getZipContent(packageFileContent);
131 if (isPackageEmpty()) {
132 String message = PACKAGE_EMPTY_ERROR.formatMessage(packageFileName);
133 reportError(ErrorLevel.ERROR, message);
135 } catch (final ZipException e) {
136 String message = PACKAGE_PROCESS_ERROR.formatMessage(packageFileName);
137 reportError(ErrorLevel.ERROR, message);
138 LOGGER.error(message, e);
143 private OnboardPackageInfo processCsarPackage(String packageName, String packageExtension) {
144 OnboardPackage onboardPackage = new OnboardPackage(packageName, packageExtension, ByteBuffer.wrap(packageFileContent),
145 new OnboardingPackageContentHandler(packageContent));
146 return new OnboardPackageInfo(onboardPackage, OnboardingTypesEnum.CSAR);
149 private OnboardPackageInfo createOnboardPackageInfoForZip(String packageName, String packageExtension) {
150 return new OnboardPackageInfo(
151 new OnboardPackage(packageName, packageExtension, ByteBuffer.wrap(packageFileContent),
152 packageContent), OnboardingTypesEnum.ZIP);
155 private OnboardPackageInfo processOnapNativeZipPackage(String packageName, String packageExtension) {
156 if (CommonConfigurationManager.getInstance().getConfigValue("zipValidation", "ignoreManifest", false)) {
157 return createOnboardPackageInfoForZip(packageName, packageExtension);
159 ManifestContent manifest = getManifest();
160 if (manifest != null) {
161 List<String> errors = validateZipPackage(manifest);
162 if (errors.isEmpty()) {
163 return createOnboardPackageInfoForZip(packageName, packageExtension);
165 errors.forEach(message -> reportError(ErrorLevel.ERROR, message));
168 reportError(ErrorLevel.ERROR,
169 COULD_NOT_READ_MANIFEST_FILE.formatMessage(SdcCommon.MANIFEST_NAME, packageFileName));
174 List<String> validateZipPackage(ManifestContent manifest) {
175 ManifestAnalyzer analyzer = new ManifestAnalyzer(manifest);
176 List<String> errors = Collections.emptyList();
177 if (analyzer.hasHelmEntries()) {
178 if (shouldValidateHelmPackage(analyzer)) {
179 errors = cnfPackageValidator.validateHelmPackage(analyzer.getHelmEntries());
182 addDummyHeat(manifest);
186 boolean shouldValidateHelmPackage(ManifestAnalyzer analyzer) {
187 return analyzer.hasHelmEntries() && !analyzer.hasHeatEntries();
190 private ManifestContent getManifest() {
191 ManifestContent manifest = null;
192 try (InputStream zipFileManifest = packageContent.getFileContentAsStream(SdcCommon.MANIFEST_NAME)) {
193 manifest = JsonUtil.json2Object(zipFileManifest, ManifestContent.class);
194 } catch (Exception e) {
195 final String message = COULD_NOT_READ_MANIFEST_FILE.formatMessage(SdcCommon.MANIFEST_NAME, packageFileName);
196 LOGGER.error(message, e);
201 private void addDummyHeat(ManifestContent manifestContent) {
202 // temporary fix for adding dummy base
203 List<FileData> newfiledata = new ArrayList<>();
205 boolean heatBase = false;
206 for (FileData fileData : manifestContent.getData()) {
207 if (Objects.nonNull(fileData.getType()) && fileData.getType().equals(FileData.Type.HELM) && fileData.getBase()) {
209 fileData.setBase(false);
210 FileData dummyHeat = new FileData();
211 dummyHeat.setBase(true);
212 dummyHeat.setFile("base_template_dummy_ignore.yaml");
213 dummyHeat.setType(FileData.Type.HEAT);
214 FileData dummyEnv = new FileData();
215 dummyEnv.setBase(false);
216 dummyEnv.setFile("base_template_dummy_ignore.env");
217 dummyEnv.setType(FileData.Type.HEAT_ENV);
218 List<FileData> dataEnvList = new ArrayList<>();
219 dataEnvList.add(dummyEnv);
220 dummyHeat.setData(dataEnvList);
221 newfiledata.add(dummyHeat);
222 String filePath = new File("").getAbsolutePath() + "/resources";
223 File envFilePath = new File(filePath + "/base_template.env");
224 File baseFilePath = new File(filePath + "/base_template.yaml");
225 try (InputStream envStream = new FileInputStream(envFilePath); InputStream baseStream = new FileInputStream(baseFilePath)) {
226 packageContent.addFile("base_template_dummy_ignore.env", envStream);
227 packageContent.addFile("base_template_dummy_ignore.yaml", baseStream);
228 } catch (Exception e) {
229 LOGGER.error("Failed creating input stream {}", e);
234 manifestContent.getData().addAll(newfiledata);
235 InputStream manifestContentStream = new ByteArrayInputStream(
236 (JsonUtil.object2Json(manifestContent)).getBytes(StandardCharsets.UTF_8));
237 packageContent.remove(SdcCommon.MANIFEST_NAME);
238 packageContent.addFile(SdcCommon.MANIFEST_NAME, manifestContentStream);
240 } catch (Exception e) {
241 final String message = PACKAGE_INVALID_ERROR.formatMessage(packageFileName);
242 LOGGER.error(message, e);
246 private boolean hasValidExtension() {
247 final String packageExtension = FilenameUtils.getExtension(packageFileName);
248 return packageExtension.equalsIgnoreCase(CSAR_EXTENSION) || packageExtension.equalsIgnoreCase(ZIP_EXTENSION);
251 private OnboardPackageInfo processSignedPackage(final String packageName, final String packageExtension) {
252 final String internalPackagePath = findInternalPackagePath().orElse(null);
253 if (internalPackagePath == null) {
254 reportError(ErrorLevel.ERROR, PACKAGE_MISSING_INTERNAL_PACKAGE.getErrorMessage());
257 final String signatureFilePath = findSignatureFilePath().orElse(null);
258 final String certificateFilePath = findCertificateFilePath().orElse(null);
259 final OnboardSignedPackage onboardSignedPackage = new OnboardSignedPackage(packageName, packageExtension, ByteBuffer.wrap(packageFileContent),
260 packageContent, signatureFilePath, internalPackagePath, certificateFilePath);
261 final String internalPackageName = FilenameUtils.getName(internalPackagePath);
262 final String internalPackageBaseName = FilenameUtils.getBaseName(internalPackagePath);
263 final String internalPackageExtension = FilenameUtils.getExtension(internalPackagePath);
264 final byte[] internalPackageContent = packageContent.getFileContent(internalPackagePath);
265 final OnboardPackage onboardPackage;
267 final OnboardingPackageContentHandler fileContentHandler = new OnboardingPackageContentHandler(
268 CommonUtil.getZipContent(internalPackageContent));
269 onboardPackage = new OnboardPackage(internalPackageBaseName, internalPackageExtension, internalPackageContent, fileContentHandler);
270 } catch (final ZipException e) {
271 final String message = PACKAGE_PROCESS_INTERNAL_PACKAGE_ERROR.formatMessage(internalPackageName);
272 LOGGER.error(message, e);
273 reportError(ErrorLevel.ERROR, message);
276 return new OnboardPackageInfo(onboardSignedPackage, onboardPackage, OnboardingTypesEnum.SIGNED_CSAR);
279 private void reportError(final ErrorLevel errorLevel, final String message) {
280 errorMessages.add(new ErrorMessage(errorLevel, message));
283 private Optional<String> findInternalPackagePath() {
284 return packageContent.getFileList().stream().filter(filePath -> {
285 final String extension = FilenameUtils.getExtension(filePath);
286 return CSAR_EXTENSION.equalsIgnoreCase(extension) || ZIP_EXTENSION.equalsIgnoreCase(extension);
290 private boolean isPackageEmpty() {
291 return MapUtils.isEmpty(packageContent.getFiles());
294 private boolean hasSignedPackageStructure() {
295 if (MapUtils.isEmpty(packageContent.getFiles()) || !CollectionUtils.isEmpty(packageContent.getFolderList())) {
298 final int numberOfFiles = packageContent.getFileList().size();
299 if (numberOfFiles == 2) {
300 return hasOneInternalPackageFile(packageContent) && hasOneSignatureFile(packageContent);
302 if (numberOfFiles == 3) {
303 return hasOneInternalPackageFile(packageContent) && hasOneSignatureFile(packageContent) && hasOneCertificateFile(packageContent);
308 private boolean hasOneInternalPackageFile(final FileContentHandler fileContentHandler) {
309 return fileContentHandler.getFileList().parallelStream().map(FilenameUtils::getExtension).map(String::toLowerCase)
310 .filter(file -> file.endsWith(CSAR_EXTENSION)).count() == 1;
313 private boolean hasOneSignatureFile(final FileContentHandler fileContentHandler) {
314 return fileContentHandler.getFileList().parallelStream().map(FilenameUtils::getExtension).map(String::toLowerCase)
315 .filter(ALLOWED_SIGNATURE_EXTENSIONS::contains).count() == 1;
318 private boolean hasOneCertificateFile(final FileContentHandler fileContentHandler) {
319 return fileContentHandler.getFileList().parallelStream().map(FilenameUtils::getExtension).map(String::toLowerCase)
320 .filter(ALLOWED_CERTIFICATE_EXTENSIONS::contains).count() == 1;
323 private Optional<String> findSignatureFilePath() {
324 final Map<String, byte[]> files = packageContent.getFiles();
325 return files.keySet().stream().filter(fileName -> ALLOWED_SIGNATURE_EXTENSIONS.contains(FilenameUtils.getExtension(fileName).toLowerCase()))
329 private Optional<String> findCertificateFilePath() {
330 final Map<String, byte[]> files = packageContent.getFiles();
331 return files.keySet().stream().filter(fileName -> ALLOWED_CERTIFICATE_EXTENSIONS.contains(FilenameUtils.getExtension(fileName).toLowerCase()))