Update vulnerable package dependencies
[sdc.git] / openecomp-be / lib / openecomp-tosca-lib / src / main / java / org / openecomp / sdc / tosca / csar / AbstractOnboardingManifest.java
1 /*
2  * Copyright © 2016-2017 European Support Limited
3  * Modification Copyright (C) 2019 Nordix Foundation.
4  * Modification 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  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 package org.openecomp.sdc.tosca.csar;
19
20 import com.google.common.collect.ImmutableList;
21 import com.google.common.collect.ImmutableMap;
22 import java.io.BufferedReader;
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.io.InputStreamReader;
26 import java.nio.charset.StandardCharsets;
27 import java.util.ArrayList;
28 import java.util.Collections;
29 import java.util.HashMap;
30 import java.util.Iterator;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.Optional;
34 import org.apache.commons.collections.MapUtils;
35 import org.apache.commons.lang3.StringUtils;
36 import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
37 import org.openecomp.sdc.common.errors.Messages;
38 import org.openecomp.sdc.logging.api.Logger;
39 import org.openecomp.sdc.logging.api.LoggerFactory;
40
41 abstract class AbstractOnboardingManifest implements Manifest {
42
43     protected static final Logger LOGGER = LoggerFactory.getLogger(AbstractOnboardingManifest.class);
44     protected static final int MAX_ALLOWED_MANIFEST_META_ENTRIES = 4;
45     protected Map<String, String> metadata;
46     protected List<String> sources;
47     protected Map<String, List<String>> nonManoSources;
48     protected Map<String, AlgorithmDigest> sourceAndChecksumMap = new HashMap<>();
49     protected Map<String, SignatureData> sourceAndSignatureMap = new HashMap<>();
50     protected String cmsSignature;
51     protected List<String> errors;
52     protected boolean continueToProcess;
53     protected String currentLine;
54     protected Iterator<String> linesIterator;
55     protected int currentLineNumber;
56
57     protected AbstractOnboardingManifest() {
58         errors = new ArrayList<>();
59         sources = new ArrayList<>();
60         metadata = new HashMap<>();
61         nonManoSources = new HashMap<>();
62     }
63
64     @Override
65     public Optional<ResourceTypeEnum> getType() {
66         if (!isValid()) {
67             return Optional.empty();
68         }
69         final String firstKey = metadata.keySet().iterator().next();
70         final ManifestTokenType manifestTokenType = ManifestTokenType.parse(firstKey).orElse(null);
71         if (manifestTokenType == null) {
72             return Optional.empty();
73         }
74         if (manifestTokenType.isMetadataPnfEntry()) {
75             return Optional.of(ResourceTypeEnum.PNF);
76         }
77         return Optional.of(ResourceTypeEnum.VF);
78     }
79
80     @Override
81     public void parse(final InputStream manifestAsStream) {
82         try {
83             final ImmutableList<String> lines = readAllLines(manifestAsStream);
84             continueToProcess = true;
85             currentLineNumber = 0;
86             processManifest(lines);
87         } catch (IOException e) {
88             LOGGER.error(e.getMessage(), e);
89             errors.add(Messages.MANIFEST_PARSER_INTERNAL.getErrorMessage());
90         }
91     }
92
93     /**
94      * Process the manifest lines, reporting an error when detected.
95      *
96      * @param lines the manifest lines
97      */
98     protected void processManifest(final ImmutableList<String> lines) {
99         if (isEmptyManifest(lines)) {
100             return;
101         }
102         linesIterator = lines.iterator();
103         readNextNonEmptyLine();
104         if (!getCurrentLine().isPresent()) {
105             errors.add(Messages.MANIFEST_EMPTY.getErrorMessage());
106             return;
107         }
108         processMetadata();
109         processBody();
110     }
111
112     /**
113      * Process the metadata part of the Manifest file.
114      */
115     protected abstract void processMetadata();
116
117     /**
118      * Process the other parts from manifest different than metadata.
119      */
120     protected abstract void processBody();
121
122     /**
123      * Read the manifest as a list of lines.
124      *
125      * @param manifestAsStream The manifest file input stream
126      * @return The manifest as a list of string
127      * @throws IOException when the input stream is null or a read problem happened.
128      */
129     protected ImmutableList<String> readAllLines(final InputStream manifestAsStream) throws IOException {
130         if (manifestAsStream == null) {
131             throw new IOException("Manifest Input Stream cannot be null.");
132         }
133         final ImmutableList.Builder<String> builder = ImmutableList.builder();
134         try (final BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(manifestAsStream, StandardCharsets.UTF_8.newDecoder()))) {
135             bufferedReader.lines().forEach(builder::add);
136         }
137         return builder.build();
138     }
139
140     /**
141      * Checks if the line is a {@link ManifestTokenType#METADATA} entry.
142      *
143      * @param line The line to check
144      * @return {@code true} if the line is a 'metadata' entry, {@code false} otherwise.
145      */
146     protected boolean isMetadata(final String line) {
147         return line.trim().equals(ManifestTokenType.METADATA.getToken() + ManifestTokenType.ATTRIBUTE_VALUE_SEPARATOR.getToken());
148     }
149
150     /**
151      * Checks if the the entry is a valid metadata entry.
152      *
153      * @param metadataEntry the entry to be evaluated
154      * @return {@code true} if the entry is a valid metadata entry, {@code false} otherwise.
155      */
156     protected boolean isMetadataEntry(final String metadataEntry) {
157         final Optional<ManifestTokenType> manifestTokenType = ManifestTokenType.parse(metadataEntry);
158         return manifestTokenType.map(ManifestTokenType::isMetadataEntry).orElse(false);
159     }
160
161     /**
162      * Checks if the manifest is empty
163      *
164      * @param lines the manifest parsed as a string lines list
165      * @return {@code true} if the manifest is empty, {@code false} otherwise.
166      */
167     protected boolean isEmptyManifest(final ImmutableList<String> lines) {
168         if (lines == null || lines.isEmpty()) {
169             errors.add(Messages.MANIFEST_EMPTY.getErrorMessage());
170             return true;
171         }
172         return false;
173     }
174
175     /**
176      * Reports a manifest invalid line error occurred in the current line.
177      */
178     protected void reportInvalidLine() {
179         reportInvalidLine(currentLineNumber, getCurrentLine().orElse(""));
180     }
181
182     /**
183      * Reports a manifest invalid line error.
184      *
185      * @param lineNumber the line number
186      * @param line       the line
187      */
188     protected void reportInvalidLine(final int lineNumber, final String line) {
189         errors.add(Messages.MANIFEST_INVALID_LINE.formatMessage(lineNumber, line));
190     }
191
192     /**
193      * Reports a manifest error occurred in the current line.
194      *
195      * @param message The error message
196      * @param params  The message params
197      */
198     protected void reportError(final Messages message, final Object... params) {
199         reportError(currentLineNumber, getCurrentLine().orElse(""), message, params);
200     }
201
202     /**
203      * Reports a manifest error occurred in the specified line.
204      *
205      * @param lineNumber The line number
206      * @param line       The line
207      * @param message    The error message
208      * @param params     The message params
209      */
210     protected void reportError(final int lineNumber, final String line, final Messages message, final Object... params) {
211         errors.add(Messages.MANIFEST_ERROR_WITH_LINE.formatMessage(message.formatMessage(params), lineNumber, line));
212     }
213
214     /**
215      * Checks if the manifest is valid.
216      *
217      * @return {@code true} if the manifest is valid, {@code false} otherwise.
218      */
219     public boolean isValid() {
220         return errors.isEmpty();
221     }
222
223     /**
224      * Reads the next non empty line in the manifest. Updates the current line and line number.
225      *
226      * @return the next non empty line. If there is no more lines, an empty value.
227      */
228     protected Optional<String> readNextNonEmptyLine() {
229         while (linesIterator.hasNext()) {
230             final String line = linesIterator.next().trim();
231             currentLineNumber++;
232             if (!line.isEmpty()) {
233                 currentLine = line;
234                 return getCurrentLine();
235             }
236             currentLine = null;
237         }
238         if (getCurrentLine().isPresent()) {
239             currentLineNumber++;
240             currentLine = null;
241         }
242         return getCurrentLine();
243     }
244
245     /**
246      * Gets the current line.
247      *
248      * @return the current line.
249      */
250     protected Optional<String> getCurrentLine() {
251         return Optional.ofNullable(currentLine);
252     }
253
254     /**
255      * Reads the current line entry name. The entry name and value must be separated by {@link ManifestTokenType#ATTRIBUTE_VALUE_SEPARATOR}.
256      *
257      * @return the entry value
258      */
259     protected Optional<String> readCurrentEntryName() {
260         final Optional<String> line = getCurrentLine();
261         if (line.isPresent()) {
262             return readEntryName(line.get());
263         }
264         return Optional.empty();
265     }
266
267     /**
268      * Read a entry name. The entry name and value must be separated by {@link ManifestTokenType#ATTRIBUTE_VALUE_SEPARATOR}.
269      *
270      * @param line the entry line
271      * @return returns the entry name
272      */
273     protected Optional<String> readEntryName(final String line) {
274         if (StringUtils.isEmpty(line)) {
275             return Optional.empty();
276         }
277         if (!line.contains(ManifestTokenType.ATTRIBUTE_VALUE_SEPARATOR.getToken())) {
278             return Optional.empty();
279         }
280         final String attribute = line.substring(0, line.indexOf(ManifestTokenType.ATTRIBUTE_VALUE_SEPARATOR.getToken())).trim();
281         if (StringUtils.isEmpty(attribute)) {
282             return Optional.empty();
283         }
284         return Optional.of(attribute);
285     }
286
287     /**
288      * Reads the current line entry value. The entry name and value must be separated by {@link ManifestTokenType#ATTRIBUTE_VALUE_SEPARATOR}.
289      *
290      * @return the entry value
291      */
292     protected Optional<String> readCurrentEntryValue() {
293         final Optional<String> line = getCurrentLine();
294         if (line.isPresent()) {
295             return readEntryValue(line.get());
296         }
297         return Optional.empty();
298     }
299
300     /**
301      * Reads a entry value. The entry name and value must be separated by {@link ManifestTokenType#ATTRIBUTE_VALUE_SEPARATOR}.
302      *
303      * @param line the entry line
304      * @return the entry value
305      */
306     protected Optional<String> readEntryValue(final String line) {
307         if (StringUtils.isEmpty(line)) {
308             return Optional.empty();
309         }
310         if (!line.contains(ManifestTokenType.ATTRIBUTE_VALUE_SEPARATOR.getToken())) {
311             return Optional.empty();
312         }
313         final String value = line.substring(line.indexOf(ManifestTokenType.ATTRIBUTE_VALUE_SEPARATOR.getToken()) + 1).trim();
314         if (StringUtils.isEmpty(value)) {
315             return Optional.empty();
316         }
317         return Optional.of(value);
318     }
319
320     /**
321      * Adds a entry to the metadata map. Only accepts new entries. If the entry is duplicated a manifest error is reported.
322      *
323      * @param entry the metadata entry
324      * @param value the entry value
325      * @return {@code true} if the entry was added, {@code false} otherwise.
326      */
327     protected boolean addToMetadata(final String entry, final String value) {
328         if (metadata.containsKey(entry)) {
329             reportError(Messages.MANIFEST_METADATA_DUPLICATED_ENTRY, entry);
330             return false;
331         }
332         metadata.put(entry, value);
333         return true;
334     }
335
336     public List<String> getErrors() {
337         return ImmutableList.copyOf(errors);
338     }
339
340     public Map<String, String> getMetadata() {
341         if (!isValid()) {
342             return Collections.emptyMap();
343         }
344         return ImmutableMap.copyOf(metadata);
345     }
346
347     public List<String> getSources() {
348         if (!isValid()) {
349             return Collections.emptyList();
350         }
351         return ImmutableList.copyOf(sources);
352     }
353
354     public Map<String, List<String>> getNonManoSources() {
355         if (!isValid()) {
356             return Collections.emptyMap();
357         }
358         return ImmutableMap.copyOf(nonManoSources);
359     }
360
361     @Override
362     public boolean isSigned() {
363         return getCmsSignature().isPresent();
364     }
365
366     @Override
367     public Optional<String> getCmsSignature() {
368         return Optional.ofNullable(cmsSignature);
369     }
370
371     @Override
372     public Optional<Map<String, AlgorithmDigest>> getSourceAndChecksumMap() {
373         if (MapUtils.isEmpty(sourceAndChecksumMap)) {
374             return Optional.empty();
375         }
376         return Optional.of(ImmutableMap.copyOf(sourceAndChecksumMap));
377     }
378 }