Update vulnerable package dependencies
[sdc.git] / catalog-be-plugins / etsi-nfv-nsd-csar-plugin / src / main / java / org / openecomp / sdc / be / plugins / etsi / nfv / nsd / generator / EtsiNfvNsdCsarGeneratorImpl.java
1 /*
2  * ============LICENSE_START=======================================================
3  *  Copyright (C) 2020 Nordix Foundation
4  *  ================================================================================
5  *  Licensed under the Apache License, Version 2.0 (the "License");
6  *  you may not use this file except in compliance with the License.
7  *  You may obtain a copy of the License at
8  *
9  *        http://www.apache.org/licenses/LICENSE-2.0
10  *  Unless required by applicable law or agreed to in writing, software
11  *  distributed under the License is distributed on an "AS IS" BASIS,
12  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *  See the License for the specific language governing permissions and
14  *  limitations under the License.
15  *
16  *  SPDX-License-Identifier: Apache-2.0
17  *  ============LICENSE_END=========================================================
18  */
19 package org.openecomp.sdc.be.plugins.etsi.nfv.nsd.generator;
20
21 import static org.openecomp.sdc.be.plugins.etsi.nfv.nsd.security.NsdCsarEtsiOption2Signer.SIGNATURE_EXTENSION;
22 import static org.openecomp.sdc.common.api.ArtifactTypeEnum.ETSI_PACKAGE;
23 import static org.openecomp.sdc.common.api.ArtifactTypeEnum.ONBOARDED_PACKAGE;
24
25 import fj.data.Either;
26 import java.io.File;
27 import java.io.IOException;
28 import java.nio.file.Files;
29 import java.util.ArrayList;
30 import java.util.Collections;
31 import java.util.HashMap;
32 import java.util.HashSet;
33 import java.util.List;
34 import java.util.Map;
35 import java.util.Map.Entry;
36 import java.util.Optional;
37 import java.util.Set;
38 import java.util.zip.ZipEntry;
39 import java.util.zip.ZipOutputStream;
40 import org.apache.commons.collections.CollectionUtils;
41 import org.apache.commons.collections.MapUtils;
42 import org.apache.commons.io.FilenameUtils;
43 import org.apache.commons.io.IOUtils;
44 import org.apache.commons.io.output.ByteArrayOutputStream;
45 import org.apache.commons.lang3.StringUtils;
46 import org.openecomp.sdc.be.csar.security.api.model.CertificateInfo;
47 import org.openecomp.sdc.be.dao.cassandra.ArtifactCassandraDao;
48 import org.openecomp.sdc.be.dao.cassandra.CassandraOperationStatus;
49 import org.openecomp.sdc.be.datatypes.enums.JsonPresentationFields;
50 import org.openecomp.sdc.be.model.ArtifactDefinition;
51 import org.openecomp.sdc.be.model.Component;
52 import org.openecomp.sdc.be.model.ComponentInstance;
53 import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.builder.NsdCsarManifestBuilder;
54 import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.builder.NsdToscaMetadataBuilder;
55 import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.exception.NsdException;
56 import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.factory.NsDescriptorGeneratorFactory;
57 import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.generator.config.EtsiVersion;
58 import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.generator.config.NsDescriptorConfig;
59 import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.generator.config.NsDescriptorVersionComparator;
60 import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.model.Nsd;
61 import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.model.NsdCsar;
62 import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.model.VnfDescriptor;
63 import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.security.NsdCsarEtsiOption2Signer;
64 import org.openecomp.sdc.be.resources.data.DAOArtifactData;
65 import org.openecomp.sdc.be.tosca.utils.OperationArtifactUtil;
66 import org.slf4j.Logger;
67 import org.slf4j.LoggerFactory;
68 import org.springframework.beans.factory.config.BeanDefinition;
69 import org.springframework.context.annotation.Scope;
70 import org.springframework.core.io.Resource;
71 import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
72
73 /**
74  * Implementation of a ETSI NFV NSD CSAR generator
75  */
76 @org.springframework.stereotype.Component("etsiNfvNsdCsarGenerator")
77 @Scope(BeanDefinition.SCOPE_PROTOTYPE)
78 public class EtsiNfvNsdCsarGeneratorImpl implements EtsiNfvNsdCsarGenerator {
79
80     private static final Logger LOGGER = LoggerFactory.getLogger(EtsiNfvNsdCsarGeneratorImpl.class);
81     private static final String MANIFEST_EXT = "mf";
82     private static final String SLASH = "/";
83     private static final String DOT = ".";
84     private static final String CSAR_SIGNATURE_EXTENSION = ".cms";
85     private static final String CSAR_EXTENSION = ".csar";
86     private static final String DOT_YAML = DOT + "yaml";
87     private static final String DEFINITION = "Definitions";
88     private static final String TOSCA_META_PATH = "TOSCA-Metadata/TOSCA.meta";
89     private final VnfDescriptorGenerator vnfDescriptorGenerator;
90     private final NsDescriptorGeneratorFactory nsDescriptorGeneratorFactory;
91     private final ArtifactCassandraDao artifactCassandraDao;
92     private final NsDescriptorConfig nsDescriptorConfig;
93     private final NsdCsarEtsiOption2Signer nsdCsarEtsiOption2Signer;
94
95     public EtsiNfvNsdCsarGeneratorImpl(final NsDescriptorConfig nsDescriptorConfig, final VnfDescriptorGenerator vnfDescriptorGenerator,
96                                        final NsDescriptorGeneratorFactory nsDescriptorGeneratorFactory,
97                                        final ArtifactCassandraDao artifactCassandraDao,
98                                        final NsdCsarEtsiOption2Signer nsdCsarEtsiOption2Signer) {
99         this.nsDescriptorConfig = nsDescriptorConfig;
100         this.vnfDescriptorGenerator = vnfDescriptorGenerator;
101         this.nsDescriptorGeneratorFactory = nsDescriptorGeneratorFactory;
102         this.artifactCassandraDao = artifactCassandraDao;
103         this.nsdCsarEtsiOption2Signer = nsdCsarEtsiOption2Signer;
104     }
105
106     @Override
107     public NsdCsar generateNsdCsar(final Component component) throws NsdException {
108         if (component == null) {
109             throw new NsdException("Could not generate the NSD CSAR, invalid component argument");
110         }
111         loadComponentArtifacts(component);
112         loadComponentInstancesArtifacts(component);
113         final String componentName = component.getName();
114         try {
115             LOGGER.debug("Starting NSD CSAR generation for component '{}'", componentName);
116             final String nsdFileName = getNsdFileName(component);
117             final NsdCsar nsdCsar = new NsdCsar(nsdFileName);
118
119             final List<VnfDescriptor> vnfDescriptorList = generateVnfPackages(component);
120             vnfDescriptorList.forEach(vnfPackage -> nsdCsar.addAllFiles(vnfPackage.getDefinitionFiles()));
121
122             final EtsiVersion etsiVersion = nsDescriptorConfig.getNsVersion();
123             final Nsd nsd = generateNsd(component, vnfDescriptorList);
124
125             nsdCsar.addFile(getNsdPath(nsdFileName), nsd.getContents());
126             nsdCsar.addFile(TOSCA_META_PATH, buildToscaMetaContent(nsdFileName).getBytes());
127
128             nsdCsar.addAllFiles(createEtsiSolNsdTypeEntries(etsiVersion));
129             for (final String referencedFile : nsd.getArtifactReferences()) {
130                 getReferencedArtifact(component, referencedFile)
131                     .ifPresent(artifactDefinition -> nsdCsar.addFile(referencedFile, artifactDefinition.getPayloadData())
132                 );
133             }
134             final boolean isCertificateConfigured = nsdCsarEtsiOption2Signer.isCertificateConfigured();
135             final String manifestPath = getManifestPath(nsdFileName);
136             final NsdCsarManifestBuilder manifestBuilder =
137                 createManifestBuilder(nsd, etsiVersion, manifestPath, nsdCsar.getFileMap().keySet(), isCertificateConfigured);
138
139             nsdCsar.addManifest(manifestBuilder);
140
141             if (isCertificateConfigured) {
142                 nsdCsarEtsiOption2Signer.signArtifacts(nsdCsar);
143             }
144
145             byte[] package1 = buildCsarPackage(nsdCsar.getFileMap());
146             if (isCertificateConfigured) {
147                 package1 = buildZipWithCsarAndSignature(nsdCsar.getFileName(), package1);
148                 nsdCsar.setSigned(true);
149             }
150             nsdCsar.setCsarPackage(package1);
151             LOGGER.debug("Successfully generated NSD CSAR package");
152             return nsdCsar;
153         } catch (final Exception exception) {
154             throw new NsdException("Could not generate the NSD CSAR file", exception);
155         }
156     }
157
158     private void loadComponentArtifacts(final Component component) {
159         final Map<String, ArtifactDefinition> allArtifactsMap = component.getAllArtifacts();
160         if (allArtifactsMap == null) {
161             return;
162         }
163         allArtifactsMap.keySet().forEach(key -> {
164             final ArtifactDefinition artifactDefinition = allArtifactsMap.get(key);
165             if (StringUtils.isNotEmpty(artifactDefinition.getEsId())) {
166                 final Optional<byte[]> artifactPayload = loadArtifactPayload(artifactDefinition.getEsId());
167                 if (artifactPayload.isPresent()) {
168                     artifactDefinition.setPayload(artifactPayload.get());
169                 } else {
170                     LOGGER.warn("Could not load component '{}' artifact '{}'", component.getName(), artifactDefinition.getArtifactName());
171                 }
172             }
173         });
174     }
175
176     private void loadComponentInstancesArtifacts(final Component component) {
177         final List<ComponentInstance> componentInstanceList = component.getComponentInstances();
178         if (CollectionUtils.isEmpty(componentInstanceList)) {
179             return;
180         }
181         for (final ComponentInstance componentInstance : componentInstanceList) {
182             final Map<String, ArtifactDefinition> deploymentArtifacts = componentInstance.getDeploymentArtifacts();
183             if (MapUtils.isEmpty(deploymentArtifacts)) {
184                 continue;
185             }
186             deploymentArtifacts.values().stream().filter(artifactDefinition -> StringUtils.isNotEmpty(artifactDefinition.getEsId()))
187                 .forEach(artifactDefinition -> {
188                     final Optional<byte[]> artifactPayload = loadArtifactPayload(artifactDefinition.getEsId());
189                     if (artifactPayload.isPresent()) {
190                         artifactDefinition.setPayload(artifactPayload.get());
191                     } else {
192                         LOGGER.warn("Could not load component '{}' instance '{}' artifact '{}'", component.getName(), componentInstance.getName(),
193                             artifactDefinition.getArtifactName());
194                     }
195                 });
196         }
197     }
198
199     private List<VnfDescriptor> generateVnfPackages(final Component component) throws NsdException {
200         final List<ComponentInstance> componentInstanceList = component.getComponentInstances();
201         if (CollectionUtils.isEmpty(componentInstanceList)) {
202             LOGGER.warn("Could not find any instance in service '{}'", component.getName());
203             return Collections.emptyList();
204         }
205         final List<VnfDescriptor> vnfDescriptorList = new ArrayList<>();
206         for (final ComponentInstance componentInstance : componentInstanceList) {
207             final String componentInstanceName = componentInstance.getName();
208             final ArtifactDefinition onboardedCsarArtifact = findOnboardedCsar(componentInstance).orElse(null);
209             if (onboardedCsarArtifact == null) {
210                 LOGGER.warn("Unable to generate VNF Package for component instance '{}', no onboarded package present", componentInstanceName);
211                 continue;
212             }
213             final Optional<VnfDescriptor> vnfPackage;
214             try {
215                 vnfPackage = vnfDescriptorGenerator.generate(componentInstanceName, onboardedCsarArtifact);
216             } catch (final Exception e) {
217                 final String errorMsg = String.format("Could not generate VNF package for component instance %s", componentInstanceName);
218                 throw new NsdException(errorMsg, e);
219             }
220             if (vnfPackage.isPresent()) {
221                 vnfDescriptorList.add(vnfPackage.get());
222             } else {
223                 LOGGER.warn("Unable to generate VNF Package for component instance '{}', no onboarded package present", componentInstanceName);
224             }
225         }
226         return vnfDescriptorList;
227     }
228
229     private Optional<ArtifactDefinition> findOnboardedCsar(final ComponentInstance componentInstance) {
230         final Map<String, ArtifactDefinition> artifactDefinitionMap = componentInstance.getDeploymentArtifacts();
231         if (artifactDefinitionMap == null || artifactDefinitionMap.isEmpty()) {
232             return Optional.empty();
233         }
234         return artifactDefinitionMap.values().stream().filter(artifactDefinition -> {
235             final String artifactType = (String) artifactDefinition.getToscaPresentationValue(JsonPresentationFields.ARTIFACT_TYPE);
236             return ONBOARDED_PACKAGE.getType().equals(artifactType) || ETSI_PACKAGE.getType().equals(artifactType);
237         }).findFirst();
238     }
239
240     private Map<String, byte[]> createEtsiSolNsdTypeEntries(final EtsiVersion etsiVersion) {
241         final EtsiVersion currentVersion = etsiVersion == null ? EtsiVersion.getDefaultVersion() : etsiVersion;
242
243         final PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
244         try {
245             final Resource[] resources =
246                 resolver.getResources(String.format("classpath:etsi-nfv-types/%s/*.*", currentVersion.getVersion()));
247             if (resources.length > 0) {
248                 final Map<String, byte[]> entryMap = new HashMap<>();
249                 for (final Resource resource : resources) {
250                     readResource(resource).ifPresent(resourceBytes -> {
251                         final String entryName = createDefinitionEntryName(resource.getFilename());
252                         entryMap.put(entryName, resourceBytes);
253                     });
254                 }
255                 return entryMap;
256             }
257         } catch (final IOException e) {
258             LOGGER.error("Could not find types files for the version '{}'", currentVersion.getVersion(), e);
259         }
260
261         return Collections.emptyMap();
262     }
263
264     private String createDefinitionEntryName(final String fileName) {
265         return DEFINITION + "/" + fileName;
266     }
267
268     private Optional<byte[]> readResource(final Resource resource) {
269         try {
270             return Optional.ofNullable(IOUtils.toByteArray(resource.getInputStream()));
271         } catch (final IOException exception) {
272             LOGGER.error("Error adding '{}' to NSD CSAR", resource.getFilename(), exception);
273         }
274         return Optional.empty();
275     }
276
277     private Nsd generateNsd(final Component component,
278                             final List<VnfDescriptor> vnfDescriptorList) throws NsdException {
279         final NsDescriptorGenerator nsDescriptorGenerator =
280             nsDescriptorGeneratorFactory.create();
281         return nsDescriptorGenerator.generate(component, vnfDescriptorList)
282             .orElseThrow(() ->
283                 new NsdException(String
284                     .format("Could not generate the Network Service Descriptor for component %s", component.getName()))
285             );
286     }
287
288     private Optional<ArtifactDefinition> getReferencedArtifact(final Component component,
289                                                                final String filePath) throws NsdException {
290         final Map<String, ArtifactDefinition> interfaceOperationArtifactsByName =
291             OperationArtifactUtil.getDistinctInterfaceOperationArtifactsByName(component);
292         final String[] pathComponents = filePath.split(SLASH);
293         final String artifactName = pathComponents[pathComponents.length - 1];
294         final ArtifactDefinition artifactDefinition = interfaceOperationArtifactsByName.get(artifactName);
295         if (artifactDefinition == null) {
296             throw new NsdException(String.format("Could not find artifact '%s'", filePath));
297         }
298         LOGGER.debug("ArtifactName {}, unique ID {}", artifactDefinition.getArtifactName(),
299             artifactDefinition.getUniqueId());
300         if (artifactDefinition.getPayloadData() == null) {
301             final Optional<byte[]> artifactPayload = loadArtifactPayload(artifactDefinition.getEsId());
302
303             if (artifactPayload.isEmpty()) {
304                 throw new NsdException(String.format("Could not load artifact '%s' payload", filePath));
305             }
306             artifactDefinition.setPayload(artifactPayload.get());
307         }
308
309         return Optional.of(artifactDefinition);
310     }
311
312     private Optional<byte[]> loadArtifactPayload(final String artifactCassandraId) {
313         final Either<DAOArtifactData, CassandraOperationStatus> artifactResponse = artifactCassandraDao
314             .getArtifact(artifactCassandraId);
315
316         if (artifactResponse.isRight()) {
317             LOGGER.debug("Failed to fetch artifact from Cassandra by id {} error {} ", artifactCassandraId,
318                 artifactResponse.right().value());
319             return Optional.empty();
320         }
321         final DAOArtifactData artifactData = artifactResponse.left().value();
322         return Optional.of(artifactData.getDataAsArray());
323     }
324
325     private String buildToscaMetaContent(final String nsdFileName) {
326         LOGGER.debug("Creating TOSCA.meta content");
327         final NsdToscaMetadataBuilder builder = new NsdToscaMetadataBuilder();
328
329         builder.withCsarVersion("1.1")
330             .withCreatedBy("ONAP")
331             .withToscaMetaVersion("1.0")
332             .withEntryDefinitions(getNsdPath(nsdFileName))
333             .withEntryManifest(getManifestPath(nsdFileName))
334             .withEntryChangeLog("ChangeLog.txt");
335
336         final String toscaMetadata = builder.build();
337         LOGGER.debug("Successfully created NS CSAR TOSCA.meta content:\n {}", toscaMetadata);
338         return toscaMetadata;
339     }
340
341     private NsdCsarManifestBuilder createManifestBuilder(final Nsd nsd, final EtsiVersion nsdVersion,
342                                                          final String manifestFilePath, final Set<String> filePath,
343                                                          final boolean addSignatureFiles) {
344         LOGGER.debug("Creating NS manifest file content");
345
346         final Set<String> filesToAdd = new HashSet<>(filePath);
347         if (addSignatureFiles) {
348             filePath.forEach(file -> filesToAdd.add(file + SIGNATURE_EXTENSION));
349         }
350         filesToAdd.add(manifestFilePath);
351
352         final NsdCsarManifestBuilder nsdCsarManifestBuilder = new NsdCsarManifestBuilder();
353         nsdCsarManifestBuilder.withDesigner(nsd.getDesigner())
354             .withInvariantId(nsd.getInvariantId())
355             .withName(nsd.getName())
356             .withNowReleaseDateTime()
357             .withFileStructureVersion(nsd.getVersion())
358             .withSources(filesToAdd);
359
360         final NsDescriptorVersionComparator nsdVersionComparator = new NsDescriptorVersionComparator();
361
362         if (nsdVersion != null && nsdVersionComparator.compare(nsdVersion, EtsiVersion.VERSION_3_3_1) >= 0) {
363             nsdCsarManifestBuilder.withCompatibleSpecificationVersion(nsdVersion.getVersion());
364         }
365
366         if (LOGGER.isDebugEnabled()) {
367             LOGGER.debug("Successfully created NS CSAR manifest file content:\n {}", nsdCsarManifestBuilder.build());
368         }
369         return nsdCsarManifestBuilder;
370     }
371
372     private String getManifestPath(final String nsdFileName) {
373         return nsdFileName + DOT + MANIFEST_EXT;
374     }
375
376     private String getNsdPath(final String nsdFileName) {
377         return DEFINITION + SLASH + nsdFileName + DOT_YAML;
378     }
379
380     private String getNsdFileName(final Component component) {
381         return component.getNormalizedName();
382     }
383
384     private byte[] buildCsarPackage(final Map<String, byte[]> nsdCsarFileMap) throws NsdException {
385         if (nsdCsarFileMap.isEmpty()) {
386             throw new NsdException("No files were provided to build the NSD CSAR package");
387         }
388         try (final ByteArrayOutputStream out = new ByteArrayOutputStream();
389             final ZipOutputStream zip = new ZipOutputStream(out)) {
390             for (final Entry<String, byte[]> entry : nsdCsarFileMap.entrySet()) {
391                 final String filePath = entry.getKey();
392                 final byte[] fileContent = entry.getValue();
393                 if (fileContent == null) {
394                     LOGGER.error("Could not add '{}' to NSD CSAR. File content is null", filePath);
395                     continue;
396                 }
397                 LOGGER.debug("Adding '{}' to NSD CSAR with content size: '{}'", filePath, fileContent.length);
398                 zip.putNextEntry(new ZipEntry(filePath));
399                 zip.write(fileContent);
400             }
401             zip.flush();
402             zip.finish();
403             LOGGER.debug("NSD CSAR zip file was successfully built");
404
405             return out.toByteArray();
406         } catch (final IOException e) {
407             throw new NsdException("Could not build the NSD CSAR zip file", e);
408         }
409     }
410
411     private byte[] buildZipWithCsarAndSignature(final String csarFileName, final byte[] csarPackageBytes) throws NsdException {
412         final byte[] signature;
413         try {
414             signature = nsdCsarEtsiOption2Signer.sign(csarPackageBytes);
415         } catch (final Exception e) {
416             throw new NsdException(String.format("Could not sign the CSAR '%s'", csarFileName), e);
417         }
418         final Optional<CertificateInfo> certificateInfoOpt = nsdCsarEtsiOption2Signer.getSigningCertificate();
419         if (certificateInfoOpt.isEmpty()) {
420             throw new NsdException(String.format("Could not sign the CSAR '%s'. No certificate configured.", csarFileName));
421         }
422         final CertificateInfo certificateInfo = certificateInfoOpt.get();
423         try (final ByteArrayOutputStream out = new ByteArrayOutputStream();
424             final ZipOutputStream zip = new ZipOutputStream(out)) {
425             zip.putNextEntry(new ZipEntry(csarFileName + CSAR_EXTENSION));
426             zip.write(csarPackageBytes);
427             zip.putNextEntry(new ZipEntry(csarFileName + CSAR_SIGNATURE_EXTENSION));
428             zip.write(signature);
429             final File certificateFile = certificateInfo.getCertificateFile();
430             zip.putNextEntry(new ZipEntry(csarFileName + "." + FilenameUtils.getExtension(certificateFile.getName())));
431             zip.write(Files.readAllBytes(certificateFile.toPath()));
432             zip.flush();
433             zip.finish();
434             LOGGER.debug("NSD signed CSAR zip file was successfully built");
435
436             return out.toByteArray();
437         } catch (final IOException e) {
438             throw new NsdException("Could not build the NSD signed CSAR zip file", e);
439         }
440     }
441
442 }
443
444