e5b68cb9173a84a48f6201ebc96aaecb4ce12fa5
[sdc.git] /
1 /*
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
9  *
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.
16  *
17  *  SPDX-License-Identifier: Apache-2.0
18  *  ============LICENSE_END=========================================================
19  */
20 package org.openecomp.sdc.vendorsoftwareproduct.impl.onboarding;
21
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;
31
32 import java.io.ByteArrayInputStream;
33 import java.io.File;
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;
42 import java.util.Map;
43 import java.util.Objects;
44 import java.util.Optional;
45 import java.util.Set;
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;
66
67 public class OnboardingPackageProcessor {
68
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;
78
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();
85     }
86
87     public Optional<OnboardPackageInfo> getOnboardPackageInfo() {
88         return Optional.ofNullable(onboardPackageInfo);
89     }
90
91     public boolean hasErrors() {
92         return errorMessages.stream()
93             .anyMatch(error -> error.getLevel() == ErrorLevel.ERROR);
94     }
95
96     public boolean hasNoErrors() {
97         return errorMessages.stream()
98             .noneMatch(error -> error.getLevel() == ErrorLevel.ERROR);
99     }
100
101     public Set<ErrorMessage> getErrorMessages() {
102         return errorMessages;
103     }
104
105     private OnboardPackageInfo processPackage() {
106         OnboardPackageInfo packageInfo = null;
107         validateFile();
108         if (hasNoErrors()) {
109             final String packageName = FilenameUtils.getBaseName(packageFileName);
110             final String packageExtension = FilenameUtils.getExtension(packageFileName);
111             if (hasSignedPackageStructure()) {
112                 packageInfo = processSignedPackage(packageName, packageExtension);
113             } else {
114                 if (packageExtension.equalsIgnoreCase(CSAR_EXTENSION)) {
115                     packageInfo = processCsarPackage(packageName, packageExtension);
116                 } else if (packageExtension.equalsIgnoreCase(ZIP_EXTENSION)) {
117                     packageInfo = processOnapNativeZipPackage(packageName, packageExtension);
118                 }
119             }
120         }
121         return packageInfo;
122     }
123
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);
128         } else {
129             try {
130                 packageContent = CommonUtil.getZipContent(packageFileContent);
131                 if (isPackageEmpty()) {
132                     String message = PACKAGE_EMPTY_ERROR.formatMessage(packageFileName);
133                     reportError(ErrorLevel.ERROR, message);
134                 }
135             } catch (final ZipException e) {
136                 String message = PACKAGE_PROCESS_ERROR.formatMessage(packageFileName);
137                 reportError(ErrorLevel.ERROR, message);
138                 LOGGER.error(message, e);
139             }
140         }
141     }
142
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);
147     }
148
149     private OnboardPackageInfo createOnboardPackageInfoForZip(String packageName, String packageExtension) {
150         return new OnboardPackageInfo(
151                 new OnboardPackage(packageName, packageExtension, ByteBuffer.wrap(packageFileContent),
152                         packageContent), OnboardingTypesEnum.ZIP);
153     }
154
155     private OnboardPackageInfo processOnapNativeZipPackage(String packageName, String packageExtension) {
156         if (CommonConfigurationManager.getInstance().getConfigValue("zipValidation", "ignoreManifest", false)) {
157             return createOnboardPackageInfoForZip(packageName, packageExtension);
158         }
159         ManifestContent manifest = getManifest();
160         if (manifest != null) {
161             List<String> errors = validateZipPackage(manifest);
162             if (errors.isEmpty()) {
163                 return createOnboardPackageInfoForZip(packageName, packageExtension);
164             } else {
165                 errors.forEach(message -> reportError(ErrorLevel.ERROR, message));
166             }
167         } else {
168             reportError(ErrorLevel.ERROR,
169                     COULD_NOT_READ_MANIFEST_FILE.formatMessage(SdcCommon.MANIFEST_NAME, packageFileName));
170         }
171         return null;
172     }
173
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());
180             }
181         }
182         addDummyHeat(manifest);
183         return errors;
184     }
185
186     boolean shouldValidateHelmPackage(ManifestAnalyzer analyzer) {
187         return analyzer.hasHelmEntries() && !analyzer.hasHeatEntries();
188     }
189
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);
197         }
198         return manifest;
199     }
200
201     private void addDummyHeat(ManifestContent manifestContent) {
202         // temporary fix for adding dummy base
203         List<FileData> newfiledata = new ArrayList<>();
204         try {
205             boolean heatBase = false;
206             for (FileData fileData : manifestContent.getData()) {
207                 if (Objects.nonNull(fileData.getType()) && fileData.getType().equals(FileData.Type.HELM) && fileData.getBase()) {
208                     heatBase = true;
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);
230                     }
231                 }
232             }
233             if (heatBase) {
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);
239             }
240         } catch (Exception e) {
241             final String message = PACKAGE_INVALID_ERROR.formatMessage(packageFileName);
242             LOGGER.error(message, e);
243         }
244     }
245
246     private boolean hasValidExtension() {
247         final String packageExtension = FilenameUtils.getExtension(packageFileName);
248         return packageExtension.equalsIgnoreCase(CSAR_EXTENSION) || packageExtension.equalsIgnoreCase(ZIP_EXTENSION);
249     }
250
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());
255             return null;
256         }
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;
266         try {
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);
274             return null;
275         }
276         return new OnboardPackageInfo(onboardSignedPackage, onboardPackage, OnboardingTypesEnum.SIGNED_CSAR);
277     }
278
279     private void reportError(final ErrorLevel errorLevel, final String message) {
280         errorMessages.add(new ErrorMessage(errorLevel, message));
281     }
282
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);
287         }).findFirst();
288     }
289
290     private boolean isPackageEmpty() {
291         return MapUtils.isEmpty(packageContent.getFiles());
292     }
293
294     private boolean hasSignedPackageStructure() {
295         if (MapUtils.isEmpty(packageContent.getFiles()) || !CollectionUtils.isEmpty(packageContent.getFolderList())) {
296             return false;
297         }
298         final int numberOfFiles = packageContent.getFileList().size();
299         if (numberOfFiles == 2) {
300             return hasOneInternalPackageFile(packageContent) && hasOneSignatureFile(packageContent);
301         }
302         if (numberOfFiles == 3) {
303             return hasOneInternalPackageFile(packageContent) && hasOneSignatureFile(packageContent) && hasOneCertificateFile(packageContent);
304         }
305         return false;
306     }
307
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;
311     }
312
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;
316     }
317
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;
321     }
322
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()))
326             .findFirst();
327     }
328
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()))
332             .findFirst();
333     }
334 }