99ea9a5a1347972de6e531f19b1aaa90020ba925
[sdc.git] /
1 /*-
2  * ============LICENSE_START=======================================================
3  *  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  * SPDX-License-Identifier: Apache-2.0
19  * ============LICENSE_END=========================================================
20  */
21
22 package org.openecomp.sdc.tosca.csar;
23
24 import java.util.ArrayList;
25 import java.util.List;
26 import java.util.Map.Entry;
27 import java.util.Optional;
28 import org.apache.commons.lang.StringUtils;
29 import org.openecomp.sdc.common.errors.Messages;
30
31 /**
32  * Processes a SOL004 Manifest.
33  */
34 public class SOL004ManifestOnboarding extends AbstractOnboardingManifest {
35
36     @Override
37     protected void processMetadata() {
38         Optional<String> currentLine = getCurrentLine();
39         //SOL004 #4.3.2: The manifest file shall start with the package metadata
40         if (!currentLine.isPresent() || !isMetadata(currentLine.get())) {
41             reportError(Messages.MANIFEST_START_METADATA);
42             continueToProcess = false;
43             return;
44         }
45         while (continueToProcess) {
46             currentLine = readNextNonEmptyLine();
47             if (!currentLine.isPresent()) {
48                 continueToProcess = validateMetadata();
49                 return;
50             }
51             final String metadataLine = currentLine.get();
52             final String metadataEntry = readEntryName(metadataLine).orElse(null);
53             if (!isMetadataEntry(metadataEntry)) {
54                 if (metadata.size() < MAX_ALLOWED_MANIFEST_META_ENTRIES) {
55                     reportError(Messages.MANIFEST_METADATA_INVALID_ENTRY1, metadataLine);
56                     continueToProcess = false;
57                     return;
58                 }
59                 continueToProcess = validateMetadata();
60                 return;
61             }
62             final String metadataValue = readEntryValue(metadataLine).orElse(null);
63             addToMetadata(metadataEntry, metadataValue);
64             continueToProcess = isValid();
65         }
66         readNextNonEmptyLine();
67     }
68
69     @Override
70     protected void processBody() {
71         while (continueToProcess) {
72             final ManifestTokenType manifestTokenType = detectLineEntry().orElse(null);
73             if (manifestTokenType == null) {
74                 getCurrentLine().ifPresent(line -> reportInvalidLine());
75                 break;
76             }
77
78             switch (manifestTokenType) {
79                 case CMS_BEGIN:
80                     readCmsSignature();
81                     break;
82                 case NON_MANO_ARTIFACT_SETS:
83                     processNonManoArtifactEntry();
84                     break;
85                 case SOURCE:
86                     processSource();
87                     break;
88                 default:
89                     getCurrentLine().ifPresent(line -> reportInvalidLine());
90                     continueToProcess = false;
91                     break;
92             }
93         }
94     }
95
96     /**
97      * Processes the {@link ManifestTokenType#NON_MANO_ARTIFACT_SETS} entry.
98      */
99     private void processNonManoArtifactEntry() {
100         Optional<String> currentLine = readNextNonEmptyLine();
101         while (currentLine.isPresent()) {
102             final ManifestTokenType manifestTokenType = detectLineEntry().orElse(null);
103             if (manifestTokenType == ManifestTokenType.CMS_BEGIN) {
104                 return;
105             }
106             if (manifestTokenType != null) {
107                 reportError(Messages.MANIFEST_INVALID_NON_MANO_KEY, manifestTokenType.getToken());
108                 continueToProcess = false;
109                 return;
110             }
111             final String nonManoKey = readCurrentEntryName().orElse(null);
112             if (nonManoKey == null) {
113                 reportError(Messages.MANIFEST_INVALID_NON_MANO_KEY, currentLine.get());
114                 continueToProcess = false;
115                 return;
116             }
117             readNextNonEmptyLine();
118             final List<String> nonManoSourceList = readNonManoSourceList();
119             if (!isValid()) {
120                 continueToProcess = false;
121                 return;
122             }
123             if (nonManoSourceList.isEmpty()) {
124                 reportError(Messages.MANIFEST_EMPTY_NON_MANO_KEY, nonManoKey);
125                 continueToProcess = false;
126                 return;
127             }
128             if (nonManoSources.get(nonManoKey) == null) {
129                 nonManoSources.put(nonManoKey, nonManoSourceList);
130             } else {
131                 nonManoSources.get(nonManoKey).addAll(nonManoSourceList);
132             }
133             currentLine = getCurrentLine();
134         }
135     }
136
137     /**
138      * Processes {@link ManifestTokenType#SOURCE} entries in {@link ManifestTokenType#NON_MANO_ARTIFACT_SETS}.
139      *
140      * @return A list of sources paths
141      */
142     private List<String> readNonManoSourceList() {
143         final List<String> nonManoSourceList = new ArrayList<>();
144         while (getCurrentLine().isPresent()) {
145             final ManifestTokenType manifestTokenType = detectLineEntry().orElse(null);
146             if (manifestTokenType != ManifestTokenType.SOURCE) {
147                 break;
148             }
149
150             final String value = readCurrentEntryValue().orElse(null);
151             if (!StringUtils.isEmpty(value)) {
152                 nonManoSourceList.add(value);
153             } else {
154                 reportError(Messages.MANIFEST_EMPTY_NON_MANO_SOURCE);
155                 break;
156             }
157
158             readNextNonEmptyLine();
159         }
160         return nonManoSourceList;
161     }
162
163     /**
164      * Reads a manifest CMS signature.
165      */
166     private void readCmsSignature() {
167         if (cmsSignature != null) {
168             reportError(Messages.MANIFEST_SIGNATURE_DUPLICATED);
169             continueToProcess = false;
170             return;
171         }
172         final StringBuilder cmsSignatureBuilder = new StringBuilder();
173
174         cmsSignatureBuilder.append(currentLine).append("\n");
175         Optional<String> currentLine = readNextNonEmptyLine();
176         if(!getCurrentLine().isPresent()) {
177             return;
178         }
179         while (currentLine.isPresent()) {
180             if (detectLineEntry().orElse(null) == ManifestTokenType.CMS_END) {
181                 cmsSignatureBuilder.append(currentLine.get());
182                 break;
183             }
184             cmsSignatureBuilder.append(currentLine.get()).append("\n");
185             currentLine = readNextNonEmptyLine();
186         }
187
188         if (currentLine.isPresent()) {
189             cmsSignature = cmsSignatureBuilder.toString();
190             readNextNonEmptyLine();
191         }
192
193         if (getCurrentLine().isPresent()) {
194             reportError(Messages.MANIFEST_SIGNATURE_LAST_ENTRY);
195             continueToProcess = false;
196         }
197     }
198
199     /**
200      * Detects the current line manifest token.
201      *
202      * @return the current line manifest token.
203      */
204     private Optional<ManifestTokenType> detectLineEntry() {
205         final Optional<String> currentLine = getCurrentLine();
206         if (currentLine.isPresent()) {
207             final String line = currentLine.get();
208             final String entry = readEntryName(line).orElse(null);
209             if (entry == null) {
210                 return ManifestTokenType.parse(line);
211             } else {
212                 return ManifestTokenType.parse(entry);
213             }
214         }
215         return Optional.empty();
216     }
217
218     /**
219      * Validates the manifest metadata content, reporting errors found.
220      *
221      * @return {@code true} if the metadata content is valid, {@code false} otherwise.
222      */
223     private boolean validateMetadata() {
224         if (metadata.isEmpty()) {
225             reportError(Messages.MANIFEST_NO_METADATA);
226             return false;
227         }
228
229         final Entry<String, String> firstManifestEntry = metadata.entrySet().iterator().next();
230         final ManifestTokenType firstManifestEntryTokenType =
231             ManifestTokenType.parse(firstManifestEntry.getKey()).orElse(null);
232         if (firstManifestEntryTokenType == null) {
233             reportError(Messages.MANIFEST_METADATA_INVALID_ENTRY1, firstManifestEntry.getKey());
234             return false;
235         }
236         for (final Entry<String, String> manifestEntry : metadata.entrySet()) {
237             final ManifestTokenType manifestEntryTokenType = ManifestTokenType.parse(manifestEntry.getKey())
238                 .orElse(null);
239             if (manifestEntryTokenType == null) {
240                 reportError(Messages.MANIFEST_METADATA_INVALID_ENTRY1, manifestEntry.getKey());
241                 return false;
242             }
243             if ((firstManifestEntryTokenType.isMetadataVnfEntry() && !manifestEntryTokenType.isMetadataVnfEntry())
244                 || (firstManifestEntryTokenType.isMetadataPnfEntry() && !manifestEntryTokenType.isMetadataPnfEntry())) {
245                 reportError(Messages.MANIFEST_METADATA_UNEXPECTED_ENTRY_TYPE);
246                 return false;
247             }
248         }
249
250         if (metadata.entrySet().size() != MAX_ALLOWED_MANIFEST_META_ENTRIES) {
251             reportError(Messages.MANIFEST_METADATA_DOES_NOT_MATCH_LIMIT, MAX_ALLOWED_MANIFEST_META_ENTRIES);
252             return false;
253         }
254
255         return true;
256     }
257
258     /**
259      * Processes a Manifest {@link ManifestTokenType#SOURCE} entry.
260      */
261     private void processSource() {
262         final Optional<String> currentLine = getCurrentLine();
263         if (!currentLine.isPresent()) {
264             return;
265         }
266         final ManifestTokenType manifestTokenType = detectLineEntry().orElse(null);
267         if (manifestTokenType != ManifestTokenType.SOURCE) {
268             return;
269         }
270
271         final String sourceLine = currentLine.get();
272         final String sourcePath = readEntryValue(sourceLine).orElse(null);
273
274         if (sourcePath == null) {
275             reportError(Messages.MANIFEST_EXPECTED_SOURCE_PATH);
276             return;
277         }
278         sources.add(sourcePath);
279         readNextNonEmptyLine();
280         readAlgorithmEntry(sourcePath);
281         readSignatureEntry(sourcePath);
282     }
283
284     /**
285      * Processes entries  {@link ManifestTokenType#ALGORITHM} and {@link ManifestTokenType#HASH} of a {@link
286      * ManifestTokenType#SOURCE} entry.
287      *
288      * @param sourcePath the source path related to the algorithm entry.
289      */
290     private void readAlgorithmEntry(final String sourcePath) {
291         Optional<String> currentLine =  getCurrentLine();
292         if (!currentLine.isPresent()) {
293             return;
294         }
295         final ManifestTokenType manifestTokenType = detectLineEntry().orElse(null);
296         if (manifestTokenType == ManifestTokenType.HASH) {
297             reportError(Messages.MANIFEST_EXPECTED_ALGORITHM_BEFORE_HASH);
298             continueToProcess = false;
299             return;
300         }
301         if (manifestTokenType != ManifestTokenType.ALGORITHM) {
302             return;
303         }
304         final String algorithmLine = currentLine.get();
305         final String algorithmType = readEntryValue(algorithmLine).orElse(null);
306         if (algorithmType == null) {
307             reportError(Messages.MANIFEST_EXPECTED_ALGORITHM_VALUE);
308             continueToProcess = false;
309             return;
310         }
311
312         currentLine = readNextNonEmptyLine();
313         if (!currentLine.isPresent() || detectLineEntry().orElse(null) != ManifestTokenType.HASH) {
314             reportError(Messages.MANIFEST_EXPECTED_HASH_ENTRY);
315             continueToProcess = false;
316             return;
317         }
318
319         final String hashLine = currentLine.get();
320         final String hash = readEntryValue(hashLine).orElse(null);
321         if (hash == null) {
322             reportError(Messages.MANIFEST_EXPECTED_HASH_VALUE);
323             continueToProcess = false;
324             return;
325         }
326         sourceAndChecksumMap.put(sourcePath, new AlgorithmDigest(algorithmType, hash));
327         readNextNonEmptyLine();
328     }
329
330     /**
331      * Processes entries  {@link ManifestTokenType#SIGNATURE} and {@link ManifestTokenType#CERTIFICATE} of a {@link
332      * ManifestTokenType#SOURCE} entry.
333      *
334      * @param sourcePath the source path related to the algorithm entry.
335      */
336     private void readSignatureEntry(final String sourcePath) {
337         Optional<String> currentLine =  getCurrentLine();
338         if (!currentLine.isPresent()) {
339             return;
340         }
341         final ManifestTokenType manifestTokenType = detectLineEntry().orElse(null);
342         if (manifestTokenType == ManifestTokenType.CERTIFICATE) {
343             reportError(Messages.MANIFEST_EXPECTED_SIGNATURE_BEFORE_CERTIFICATE);
344             continueToProcess = false;
345             return;
346         }
347         if (manifestTokenType != ManifestTokenType.SIGNATURE) {
348             return;
349         }
350         final String signatureLine = currentLine.get();
351         final String signatureFile = readEntryValue(signatureLine).orElse(null);
352         if (signatureFile == null) {
353             reportError(Messages.MANIFEST_EXPECTED_SIGNATURE_VALUE);
354             continueToProcess = false;
355             return;
356         }
357
358         currentLine = readNextNonEmptyLine();
359         if (!currentLine.isPresent() || detectLineEntry().orElse(null) != ManifestTokenType.CERTIFICATE) {
360             sourceAndSignatureMap.put(sourcePath, new SignatureData(signatureFile, null));
361             return;
362         }
363
364         final String certLine = currentLine.get();
365         final String certFile = readEntryValue(certLine).orElse(null);
366         if (certFile == null) {
367             reportError(Messages.MANIFEST_EXPECTED_CERTIFICATE_VALUE);
368             continueToProcess = false;
369             return;
370         }
371         sourceAndSignatureMap.put(sourcePath, new SignatureData(signatureFile, certFile));
372         readNextNonEmptyLine();
373     }
374
375 }