sonar security issue fix- Make sure that hashing data is safe here
[cli.git] / framework / src / main / java / org / onap / cli / fw / store / OnapCommandArtifactStore.java
1 /*
2  * Copyright 2019 Huawei Technologies Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
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
17 package org.onap.cli.fw.store;
18
19 import java.io.File;
20 import java.io.IOException;
21 import java.nio.file.Files;
22 import java.nio.file.Paths;
23 import java.security.MessageDigest;
24 import java.security.NoSuchAlgorithmException;
25 import java.text.SimpleDateFormat;
26 import java.util.ArrayList;
27 import java.util.Date;
28 import java.util.HashMap;
29 import java.util.List;
30 import java.util.Locale;
31 import java.util.Map;
32 import java.util.TimeZone;
33
34 import javax.xml.bind.DatatypeConverter;
35
36 import org.apache.commons.io.FileUtils;
37 import org.onap.cli.fw.conf.OnapCommandConfig;
38 import org.onap.cli.fw.conf.OnapCommandConstants;
39 import org.onap.cli.fw.error.OnapCommandArtifactAlreadyExist;
40 import org.onap.cli.fw.error.OnapCommandArtifactContentChecksumNotMatch;
41 import org.onap.cli.fw.error.OnapCommandArtifactContentNotExist;
42 import org.onap.cli.fw.error.OnapCommandArtifactNotFound;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
45
46 import com.google.gson.Gson;
47 import com.google.gson.GsonBuilder;
48 import com.google.gson.stream.JsonReader;
49 import java.io.FileReader;
50
51 public class OnapCommandArtifactStore {
52     private static Logger log = LoggerFactory.getLogger(OnapCommandArtifactStore.class);
53     private static Gson gson = new GsonBuilder().serializeNulls().create();
54
55     private static boolean storeReady = false;
56
57     private SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS", Locale.US);
58
59     private static String SEPARATOR = "__";
60
61     public static class Artifact {
62         private String name;
63         private String description;
64         private String categoty;
65         private String checksum;
66         private long size;
67         private String createAt;
68         private String lastUpdatedAt;
69         private String path;
70         private Map<String, String> metadata = new HashMap<>();
71         public String getName() {
72             return name;
73         }
74         public void setName(String name) {
75             this.name = name;
76         }
77         public String getChecksum() {
78             return checksum;
79         }
80         public void setChecksum(String checksum) {
81             this.checksum = checksum;
82         }
83
84         public String getDescription() {
85             return description;
86         }
87         public void setDescription(String description) {
88             this.description = description;
89         }
90         public String getCategoty() {
91             return categoty;
92         }
93         public void setCategoty(String categoty) {
94             this.categoty = categoty;
95         }
96
97         public long getSize() {
98             return size;
99         }
100         public void setSize(long l) {
101             this.size = l;
102         }
103         public String getCreateAt() {
104             return createAt;
105         }
106         public void setCreateAt(String createAt) {
107             this.createAt = createAt;
108         }
109         public String getLastUpdatedAt() {
110             return lastUpdatedAt;
111         }
112         public void setLastUpdatedAt(String lastUpdatedAt) {
113             this.lastUpdatedAt = lastUpdatedAt;
114         }
115         public String getPath() {
116             return path;
117         }
118         public void setPath(String path) {
119             this.path = path;
120         }
121         public Map<String, String> getMetadata() {
122             return metadata;
123         }
124         public void setMetadata(Map<String, String> metadata) {
125             this.metadata = metadata;
126         }
127
128     }
129    static {
130         try {
131             FileUtils.forceMkdir(new File(getBasePath()));
132             storeReady = true;
133         } catch (IOException e) {
134             log.error("Failed to create the data store results");
135         }
136     }
137
138     private static OnapCommandArtifactStore store = null;
139
140     private OnapCommandArtifactStore() {
141         this.dateFormatter.setTimeZone(TimeZone.getTimeZone("UTC"));
142     }
143
144     public static OnapCommandArtifactStore getStore() {
145         if (store == null) {
146             store = new OnapCommandArtifactStore();
147         }
148
149         return store;
150     }
151
152     private static String getBasePath() {
153         return OnapCommandConfig.getPropertyValue(OnapCommandConstants.OPEN_CLI_DATA_DIR) +
154                 File.separator + "artifacts";
155     }
156
157     private static String getArtifactPath(String name, String category) {
158         return getBasePath() + File.separator + name + SEPARATOR + category + ".json";
159     }
160
161     private String getChecksum(String storePath) throws IOException, NoSuchAlgorithmException {
162         byte[] b = Files.readAllBytes(Paths.get(storePath));
163         byte[] hash = MessageDigest.getInstance("MD5").digest(b); //NOSONAR
164         return DatatypeConverter.printHexBinary(hash);
165     }
166
167     public Artifact createArtifact(Artifact artifact) throws OnapCommandArtifactContentNotExist, OnapCommandArtifactAlreadyExist, OnapCommandArtifactContentChecksumNotMatch {
168         if (!new File(artifact.getPath()).exists()) {
169             throw new OnapCommandArtifactContentNotExist(artifact.getPath());
170         }
171
172         String storePath = getArtifactPath(artifact.getName(), artifact.getCategoty());
173         File aFile = new File(storePath);
174         if (aFile.exists()) {
175             throw new OnapCommandArtifactAlreadyExist(artifact.getName(), artifact.getCategoty());
176         }
177
178         try {
179             String actual = this.getChecksum(artifact.getPath());
180             artifact.setChecksum(actual);
181
182             artifact.setSize(new File(artifact.getPath()).length() / 1024);
183
184             artifact.setCreateAt(dateFormatter.format(new Date()));
185             artifact.setLastUpdatedAt(artifact.getCreateAt());
186
187             FileUtils.writeStringToFile(new File(storePath), gson.toJson(artifact));
188         } catch (Exception e) { // NOSONAR
189             //It is expected that this never occurs
190             log.error("Failed to store the artifact at " + storePath);
191         }
192
193         return artifact;
194     }
195
196     public Artifact getArtifact(String name, String category) throws OnapCommandArtifactNotFound {
197         String storePath = getArtifactPath(name, category);
198         File aFile = new File(storePath);
199         if (!aFile.exists()) {
200             throw new OnapCommandArtifactNotFound(name, category);
201         }
202
203         try {
204             return gson.fromJson(FileUtils.readFileToString(aFile), Artifact.class);
205         } catch (Exception e) { // NOSONAR
206             //It is expected that this never occurs
207             log.error("Failed to retrieve the artifact at " + storePath);
208         }
209         return null;
210     }
211
212
213     public List<Artifact> listArtifact(String category, String namePattern) throws OnapCommandArtifactNotFound {
214         List<Artifact> artifacts = new ArrayList<>();
215
216         String searchPattern = "";
217         if (namePattern != null && !namePattern.isEmpty()) {
218             searchPattern += namePattern;
219         } else {
220             searchPattern += "*";
221         }
222
223         searchPattern += SEPARATOR;
224
225         if (category != null && !category.isEmpty()) {
226             searchPattern += category;
227         } else {
228             searchPattern += "*";
229         }
230
231         searchPattern += ".json";
232
233         final String SP = searchPattern;
234
235         for (File file: new File(getBasePath()).listFiles(/*new FilenameFilter() {
236             @Override
237             public boolean accept(File dir, String name) {
238                 return name.matches(SP);
239             }
240         }*/)) {
241             try (JsonReader jsonReader = new JsonReader(new FileReader(file))){
242                 artifacts.add(gson.fromJson(jsonReader, Artifact.class));
243             } catch (Exception e) { // NOSONAR
244                 //It is expected that this never occurs
245                 log.error("While seraching Failed to retrieve the artifact at " + file.getAbsolutePath());
246             }
247         }
248
249         return artifacts;
250     }
251
252     public void deleteArtifact(String name, String category) throws OnapCommandArtifactNotFound {
253         String storePath = getArtifactPath(name, category);
254         File aFile = new File(storePath);
255         if (!aFile.exists()) {
256             throw new OnapCommandArtifactNotFound(name, category);
257         }
258         if(!aFile.delete()){
259             log.error("Failed to delete the artifact " + aFile.getAbsolutePath());
260         }
261     }
262
263     public Artifact updateArtifact(String name, String category, Artifact artifact) throws OnapCommandArtifactNotFound, OnapCommandArtifactContentNotExist, OnapCommandArtifactAlreadyExist {
264         Artifact existing = this.getArtifact(name, category);
265         String existingStorePath = getArtifactPath(name, category);
266
267         String newStorePath = getArtifactPath(artifact.getName(), artifact.getCategoty());
268         if ( !existingStorePath.equalsIgnoreCase(newStorePath) && new File(newStorePath).exists()) {
269             throw new OnapCommandArtifactAlreadyExist(artifact.getName(), artifact.getCategoty());
270         }
271
272         try {
273             if (artifact.getName() == null) {
274                 artifact.setName(existing.getName());
275             }
276
277             if (artifact.getDescription() == null) {
278                 artifact.setDescription(existing.getDescription());
279             }
280
281             if (artifact.getCategoty() == null) {
282                 artifact.setCategoty(existing.getCategoty());
283             }
284
285             if (artifact.getPath()!= null) {
286                 if (!new File(artifact.getPath()).exists()) {
287                     throw new OnapCommandArtifactContentNotExist(artifact.getPath());
288                 }
289                 String actual = this.getChecksum(artifact.getPath());
290                 if (!existing.getChecksum().equals(actual)) {
291                     artifact.setChecksum(actual);
292                     artifact.setSize(new File(artifact.getPath()).length() / 1024);
293                 }
294             } else {
295                 artifact.setPath(existing.getPath());
296             }
297
298             artifact.setCreateAt(existing.getCreateAt());
299             artifact.setLastUpdatedAt(dateFormatter.format(new Date()));
300             if (artifact.getMetadata().size() > 0) {
301                 //update to existing one
302                 for (Map.Entry<String, String> entry: artifact.getMetadata().entrySet()) {
303                     if (entry.getValue() == null || entry.getValue().isEmpty() || entry.getValue().equalsIgnoreCase("null")) {
304                         existing.getMetadata().remove(entry.getKey());
305                     } else
306                         existing.getMetadata().put(entry.getKey(), entry.getValue());
307                 }
308
309                 artifact.setMetadata(existing.getMetadata());
310             }
311
312             FileUtils.writeStringToFile(new File(newStorePath), gson.toJson(artifact));
313
314             if (!newStorePath.equalsIgnoreCase(existingStorePath)) {
315                 this.deleteArtifact(name, category);
316             }
317         } catch (Exception e) { // NOSONAR
318             //It is expected that this never occurs
319             log.error("Failed to update the artifact at " + existingStorePath);
320         }
321         return artifact;
322     }
323 }