Add auth mode in service
[cli.git] / framework / src / main / java / org / onap / cli / fw / schema / AbstractSchemaValidate.java
1 /*
2  * Copyright 2017 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.schema;
18
19 import org.onap.cli.fw.error.OnapCommandInvalidSchema;
20 import org.onap.cli.fw.utils.OnapCommandUtils;
21 import org.springframework.core.io.Resource;
22 import org.yaml.snakeyaml.Yaml;
23 import org.yaml.snakeyaml.parser.ParserException;
24
25 import static org.onap.cli.fw.conf.Constants.*;
26
27 import java.io.File;
28 import java.io.FileInputStream;
29 import java.io.FileNotFoundException;
30 import java.io.IOException;
31 import java.io.InputStream;
32 import java.util.ArrayList;
33 import java.util.Arrays;
34 import java.util.Collections;
35 import java.util.HashMap;
36 import java.util.HashSet;
37 import java.util.List;
38 import java.util.Map;
39 import java.util.Map.Entry;
40 import java.util.Set;
41
42 /**
43  * Abstract schema validation class.
44  *
45  */
46 public abstract class AbstractSchemaValidate implements SchemaValidate {
47
48     /**
49      * Supported schema types.
50      *
51      */
52     protected enum SchemaType {
53         HTTP, BASIC
54     }
55
56     protected List<String> schemaErrors = new ArrayList<>();
57     protected Map<String, Object> yamlMap = new HashMap<>();
58     protected Map<String, Object> defaultYamlMap = new HashMap<>();
59
60     protected static final List<String> HTTP_SCHEMA_LIST = Arrays.asList(ONAP_CMD_SCHEMA_VERSION, NAME, DESCRIPTION,
61             SERVICE, PARAMETERS, RESULTS, HTTP);
62
63     protected static final List<String> HTTP_SCHEMA_MANDATORY_LIST = Arrays.asList(ONAP_CMD_SCHEMA_VERSION, NAME,
64             DESCRIPTION, SERVICE, HTTP);
65     protected static final List<String> BASIC_SCHEMA_LIST = Arrays.asList(ONAP_CMD_SCHEMA_VERSION, NAME, DESCRIPTION,
66             PARAMETERS, RESULTS);
67
68     protected static final List<String> BASIC_SCHEMA_MANDATORY_LIST = Arrays.asList(ONAP_CMD_SCHEMA_VERSION, NAME,
69             DESCRIPTION, PARAMETERS);
70
71     protected static final List<String> TOP_LEVEL_PARAMS_LIST = Arrays.asList(ONAP_CMD_SCHEMA_VERSION, NAME,
72             DESCRIPTION);
73
74     protected static final List<String> TOP_LEVEL_MANDATORY_LIST = Arrays.asList(ONAP_CMD_SCHEMA_VERSION, NAME,
75             DESCRIPTION);
76
77     protected static final List<String> SERVICE_PARAMS_LIST = Arrays.asList(NAME, VERSION, AUTH);
78
79     protected static final List<String> SERVICE_PARAMS_MANDATORY_LIST = Arrays.asList(NAME, VERSION);
80
81     protected static final List<String> INPUT_PARAMS_LIST = Arrays.asList(NAME, DESCRIPTION, TYPE, SHORT_OPTION,
82             LONG_OPTION, IS_OPTIONAL, DEFAULT_VALUE, IS_SECURED);
83
84     protected static final List<String> INPUT_PARAMS_MANDATORY_LIST = Arrays.asList(NAME, DESCRIPTION, TYPE);
85
86     protected static final List<String> PARAMETER_TYPES = Arrays.asList(PARAMETER_TYPE_JSON, PARAMETER_TYPE_YAML,
87             PARAMETER_TYPE_STRING, PARAMETER_TYPE_LONG, PARAMETER_TYPE_URL, PARAMETER_TYPE_BOOL, PARAMETER_TYPE_ARRAY,
88             PARAMETER_TYPE_MAP, PARAMETER_TYPE_BINARY);
89
90     protected static final List<String> RESULT_PARAMS_LIST = Arrays.asList(NAME, DESCRIPTION, TYPE, SHORT_OPTION,
91             LONG_OPTION, IS_OPTIONAL, DEFAULT_VALUE, IS_SECURED);
92
93     protected static final List<String> RESULT_PARAMS_MANDATORY_LIST = Arrays.asList(NAME, DESCRIPTION, TYPE);
94
95     protected static final List<String> HTTP_PARAMS_LIST = Arrays.asList(URI, METHOD, BODY, HEADERS, QUERIES);
96
97     protected static final List<String> HTTP_PARAMS_MANDATORY_LIST = Arrays.asList(URI, METHOD, BODY, HEADERS, QUERIES);
98
99     protected static final List<String> HTTP_MANDATORY_SECTIONS = Arrays.asList(REQUEST, SUCCESS_CODES);
100
101     protected static final List<String> HTTP_SECTIONS = Arrays.asList(REQUEST, SUCCESS_CODES, RESULT_MAP,
102             SAMPLE_RESPONSE);
103
104     protected static final List<String> HTTP_REQUEST_MANDATORY_PARAMS = Arrays.asList(URI, METHOD);
105
106     protected static final List<String> HTTP_REQUEST_PARAMS = Arrays.asList(URI, METHOD, BODY, HEADERS, QUERIES);
107
108     protected static final List<String> BOOLEAN_VALUES = Arrays.asList(BOOLEAN_TRUE, BOOLEAN_FALSE);
109     protected static final List<String> DIRECTIONS = Arrays.asList(DIRECTION_PORTRAIT, DIRECTION_LANDSCAPE);
110     protected static final List<String> RESULT_SCOPES = Arrays.asList(RESULT_SCOPE_SHORT, RESULT_SCOPE_LONG);
111
112     protected static final List<String> HTTP_METHODS = Arrays.asList(POST, GET, DELETE, PUT, HEAD);
113
114     /**
115      * Constructor.
116      *
117      * @param schemaFile
118      *            schemafile
119      * @throws OnapCommandInvalidSchema
120      *             exception
121      */
122     public AbstractSchemaValidate(File schemaFile) throws OnapCommandInvalidSchema {
123         loadYaml(schemaFile);
124         loadDefaultYaml();
125     }
126
127     /**
128      * Constructor.
129      *
130      * @param schemaFile
131      *            resourceName
132      * @throws OnapCommandInvalidSchema
133      *             exception
134      */
135     public AbstractSchemaValidate(String schemaFile) throws OnapCommandInvalidSchema {
136
137         try {
138             Resource res = OnapCommandUtils.getExternalResource(schemaFile, EXTERNAL_SCHEMA_PATH_PATERN);
139             InputStream inputStream;
140             if (res == null) {
141                 inputStream = OnapCommandUtils.class.getClassLoader().getResourceAsStream(schemaFile);
142             } else {
143                 inputStream = res.getInputStream();
144             }
145
146             if (inputStream != null) {
147                 loadYamlFromInputStream(schemaFile, inputStream);
148             } else {
149                 throw new OnapCommandInvalidSchema(schemaFile, SCHEMA_FILE_NOT_EXIST);
150             }
151
152         } catch (IOException e) {
153             throw new OnapCommandInvalidSchema(schemaFile, e);
154         }
155         loadDefaultYaml();
156     }
157
158     private final void loadYaml(File schemaFile) throws OnapCommandInvalidSchema {
159         if (!schemaFile.isFile()) {
160             throw new OnapCommandInvalidSchema(schemaFile.getName(), SCHEMA_FILE_NOT_EXIST);
161         }
162         String fileName = schemaFile.getName();
163
164         if (!fileName.endsWith(".yaml")) {
165             throw new OnapCommandInvalidSchema(fileName, SCHEMA_FILE_WRONG_EXTN);
166         }
167
168         try {
169             InputStream inputStream = new FileInputStream(schemaFile);
170             loadYamlFromInputStream(schemaFile.getName(), inputStream);
171         } catch (FileNotFoundException e) {
172             throw new OnapCommandInvalidSchema(fileName, e);
173         }
174     }
175
176     @SuppressWarnings("unchecked")
177     private final void loadYamlFromInputStream(String fileName, InputStream inputStream)
178             throws OnapCommandInvalidSchema {
179         try {
180             yamlMap = (Map<String, Object>) new Yaml().load(inputStream);
181         } catch (ParserException e) {
182             throw new OnapCommandInvalidSchema(fileName, e);
183         } finally {
184             if (inputStream != null) {
185                 try {
186                     inputStream.close();
187                 } catch (IOException e) {
188                     throw new OnapCommandInvalidSchema(fileName, e); // NOSONAR
189                 }
190             }
191         }
192
193         if (yamlMap == null) {
194             throw new OnapCommandInvalidSchema(fileName, SCHEMA_FILE_EMPTY);
195         }
196     }
197
198     @SuppressWarnings("unchecked")
199     private final void loadDefaultYaml() throws OnapCommandInvalidSchema {
200         InputStream inputStream = AbstractSchemaValidate.class.getClassLoader()
201                 .getResourceAsStream(DEFAULT_SCHEMA_FILE_NAME);
202         try {
203             defaultYamlMap = (Map<String, Object>) new Yaml().load(inputStream);
204         } catch (ParserException e) {
205             throw new OnapCommandInvalidSchema(DEFAULT_SCHEMA_FILE_NAME, e);
206         }
207
208         if (defaultYamlMap == null) {
209             throw new OnapCommandInvalidSchema(DEFAULT_SCHEMA_FILE_NAME, SCHEMA_FILE_EMPTY);
210         }
211     }
212
213     /*
214      * Validate method.
215      *
216      * @throws OnapCommandInvalidSchema exception
217      */
218     @Override
219     public List<String> validate() throws OnapCommandInvalidSchema {
220
221         SchemaType type;
222         Set<String> mainSections = yamlMap.keySet();
223         if (mainSections.containsAll(HTTP_SCHEMA_MANDATORY_LIST)) {
224             type = SchemaType.HTTP;
225         } else if (mainSections.containsAll(BASIC_SCHEMA_MANDATORY_LIST)) {
226             type = SchemaType.BASIC;
227         } else {
228             schemaErrors.add(SchemaValidate.invalidSections(mainSections, HTTP_SCHEMA_MANDATORY_LIST,
229                     BASIC_SCHEMA_MANDATORY_LIST));
230             return schemaErrors;
231         }
232
233         if (type.equals(SchemaType.BASIC)) {
234             validateTopLevelAttributes();
235             validateInputParameters();
236             validateResultParameters();
237         } else {
238             validateTopLevelAttributes();
239             validateServiceAttributes();
240             validateInputParameters();
241             validateResultParameters();
242             validateSpecificSchema(SchemaType.HTTP);
243         }
244         return schemaErrors;
245     }
246
247     private void validateResultAttributes(List<Map<String, Object>> resultAttributes) {
248         Set<String> resultParamNames = new HashSet<>();
249         for (Map<String, Object> attribute : resultAttributes) {
250
251             // Validate mandatory parameters
252             validateMandatoryParams(attribute, RESULT_PARAMS_LIST, RESULT_PARAMS_MANDATORY_LIST, ATTRIBUTES);
253
254             String name = String.valueOf(attribute.get(NAME));
255
256             if (resultParamNames.contains(name)) {
257                 schemaErrors.add(SchemaValidate.attributeNameExist(name, ATTRIBUTES));
258             } else {
259                 resultParamNames.add(name);
260             }
261
262             // Validate specific parameters
263             Object type = attribute.get(TYPE);
264             String value = String.valueOf(type);
265             if (!PARAMETER_TYPES.contains(value.toLowerCase())) {
266                 schemaErrors.add(SchemaValidate.invalidType(ATTRIBUTES, name, PARAMETER_TYPES));
267             }
268
269             Object scope = attribute.get(SCOPE);
270             if (scope == null) {
271                 schemaErrors.add(SchemaValidate.attributeScopeEmpty(name));
272             } else if (!RESULT_SCOPES.contains(scope)) {
273                 schemaErrors.add(SchemaValidate.invalidAttributeScope(name, RESULT_SCOPES));
274             }
275
276             Object isSecured = attribute.get(IS_SECURED);
277             if (isSecured != null) {
278                 String value2 = String.valueOf(isSecured);
279                 if (!validateBoolean(value2)) {
280                     schemaErrors.add(SchemaValidate.invalidBooleanValueMessage(ATTRIBUTES, IS_SECURED, value2));
281                 }
282             }
283         }
284
285     }
286
287     private void validateResultParameters() {
288         @SuppressWarnings("unchecked")
289         Map<String, Object> resultParams = (Map<String, Object>) yamlMap.get(RESULTS);
290
291         if (resultParams == null || resultParams.isEmpty()) {
292             return;
293         }
294
295         Object direction = resultParams.get(DIRECTION);
296
297         if (direction != null && !DIRECTIONS.contains(direction)) {
298             schemaErrors.add(SchemaValidate.invalidType(PARAMETERS, DIRECTION, DIRECTIONS));
299         }
300
301         @SuppressWarnings("unchecked")
302         List<Map<String, Object>> resultAttributes = (List<Map<String, Object>>) resultParams.get(ATTRIBUTES);
303         validateResultAttributes(resultAttributes);
304     }
305
306     /**
307      * Get all default short options.
308      *
309      * @return set
310      */
311     protected Set<String> getDefaultShortOptions() {
312
313         Set<String> set = new HashSet<>();
314
315         @SuppressWarnings("unchecked")
316         List<Map<String, Object>> inputParams = (List<Map<String, Object>>) defaultYamlMap.get(PARAMETERS);
317         for (Map<String, Object> parameter : inputParams) {
318             Object name = parameter.get(SHORT_OPTION);
319             if (name != null && !String.valueOf(name).isEmpty() && !"null".equals(name)) {
320                 set.add(String.valueOf(name));
321             }
322         }
323
324         return set;
325     }
326
327     /**
328      * Get all default long options.
329      *
330      * @return set
331      */
332     protected Set<String> getDefaultLongOptions() {
333
334         Set<String> set = new HashSet<>();
335
336         @SuppressWarnings("unchecked")
337         List<Map<String, Object>> inputParams = (List<Map<String, Object>>) defaultYamlMap.get(PARAMETERS);
338         for (Map<String, Object> parameter : inputParams) {
339             Object name = parameter.get(LONG_OPTION);
340             if (name != null && !String.valueOf(name).isEmpty() && !"null".equals(name)) {
341                 set.add(String.valueOf(name));
342             }
343         }
344
345         return set;
346     }
347
348     private void validateTopLevelAttributes() {
349         validateMandatoryParams(yamlMap, TOP_LEVEL_PARAMS_LIST, TOP_LEVEL_MANDATORY_LIST, "root level");
350     }
351
352     private void validateServiceAttributes() {
353
354         @SuppressWarnings("unchecked")
355         Map<String, Object> serviceMap = (Map<String, Object>) yamlMap.get(SERVICE);
356
357         if (serviceMap == null) {
358             schemaErrors.add(SchemaValidate.emptySection(SERVICE));
359             return;
360         }
361
362         validateMandatoryParams(serviceMap, SERVICE_PARAMS_LIST, SERVICE_PARAMS_MANDATORY_LIST, SERVICE);
363
364         // Validate specific parameters
365
366         if (serviceMap.containsKey(AUTH)) {
367             Object obj = serviceMap.get(AUTH);
368             if (obj == null) {
369                 schemaErrors.add(SchemaValidate.emptyValue(SERVICE, AUTH));
370             } else {
371                 String value = String.valueOf(obj);
372                 if (validateBoolean(value)) {
373                     schemaErrors.add(SchemaValidate.invalidBooleanValueMessage(SERVICE, AUTH, value));
374                 }
375             }
376         }
377
378     }
379
380     private void validateInputParameters() {
381
382         @SuppressWarnings("unchecked")
383         List<Map<String, Object>> inputParams = (List<Map<String, Object>>) yamlMap.get(PARAMETERS);
384         if (inputParams == null) {
385             return;
386         }
387         validateInputAttributes(inputParams);
388     }
389
390     protected abstract void validateSpecificSchema(SchemaType type) throws OnapCommandInvalidSchema;
391
392     private void validateInputAttributes(List<Map<String, Object>> inputParams) {
393         Set<String> inputParamNames = new HashSet<>();
394         Set<String> inputShortOptions = new HashSet<>();
395         Set<String> inputLongOptions = new HashSet<>();
396
397         Set<String> defaultShortOptions = getDefaultShortOptions();
398         Set<String> defaultLongOptions = getDefaultLongOptions();
399
400         for (Map<String, Object> parameter : inputParams) {
401
402             // Validate mandatory parameters
403             validateMandatoryParams(parameter, INPUT_PARAMS_LIST, INPUT_PARAMS_MANDATORY_LIST, PARAMETERS);
404
405             // Validate specific parameters
406
407             String name = String.valueOf(parameter.get(NAME));
408
409             if (inputParamNames.contains(name)) {
410                 schemaErrors.add(SchemaValidate.nameExist(name, PARAMETERS));
411             } else {
412                 inputParamNames.add(name);
413             }
414
415             String value = String.valueOf(parameter.get(TYPE));
416
417             if (!PARAMETER_TYPES.contains(value.toLowerCase())) {
418                 schemaErrors.add(SchemaValidate.invalidAttrType(name, PARAMETERS, PARAMETER_TYPES));
419             }
420
421             Object isOptional = parameter.get(IS_OPTIONAL);
422             if (isOptional != null) {
423                 String value1 = String.valueOf(isOptional);
424                 if (!validateBoolean(value1)) {
425                     schemaErrors.add(SchemaValidate.invalidBooleanValueMessage(name, IS_OPTIONAL, value1));
426                 }
427             }
428
429             Object isSecured = parameter.get(IS_SECURED);
430             if (isSecured != null) {
431                 String value2 = String.valueOf(isSecured);
432                 if (!validateBoolean(value2)) {
433                     schemaErrors.add(SchemaValidate.invalidBooleanValueMessage(name, IS_SECURED, value2));
434                 }
435             }
436
437             String shortOption = String.valueOf(parameter.get(SHORT_OPTION));
438             String longOption = String.valueOf(parameter.get(LONG_OPTION));
439
440             if (inputShortOptions.contains(shortOption)) {
441                 schemaErrors.add(SchemaValidate.optionExist(SHORT_OPTION, shortOption, name));
442             } else if (defaultShortOptions.contains(shortOption)) {
443
444                 schemaErrors
445                         .add(SchemaValidate.optionDefaultExist(SHORT_OPTION, shortOption, name, defaultShortOptions));
446
447             } else if (shortOption != null && !shortOption.isEmpty() && !"null".equals(shortOption)) {
448                 inputShortOptions.add(shortOption);
449             }
450
451             if (inputLongOptions.contains(longOption)) {
452                 schemaErrors.add(SchemaValidate.optionExist(LONG_OPTION, longOption, name));
453             } else if (defaultLongOptions.contains(longOption)) {
454
455                 schemaErrors.add(SchemaValidate.optionDefaultExist(LONG_OPTION, longOption, name, defaultLongOptions));
456             } else if (longOption != null && !longOption.isEmpty() && !"null".equals(shortOption)) {
457                 inputLongOptions.add(longOption);
458             }
459
460         }
461
462     }
463
464     /**
465      * Validate mandatory parameters.
466      *
467      * @param yamlMap
468      *            yaml map
469      * @param totalParams
470      *            list
471      * @param mandatoryParams
472      *            list
473      * @param section
474      *            section
475      */
476     protected void validateMandatoryParams(Map<String, Object> yamlMap, List<String> totalParams,
477             List<String> mandatoryParams, String section) {
478
479         for (String param : totalParams) {
480             boolean isMandatory = mandatoryParams.contains(param);
481             boolean isYamlContains = yamlMap.containsKey(param);
482             if (isMandatory) {
483                 if (!isYamlContains) {
484                     schemaErrors.add(SchemaValidate.mandatoryAttrMissing(param, section));
485                 } else {
486                     String value = String.valueOf(yamlMap.get(param));
487                     if (value == null || "".equals(value) || "null".equals(value)) {
488                         schemaErrors.add(SchemaValidate.mandatoryAttrEmpty(param, section));
489                     }
490                 }
491             }
492         }
493     }
494
495     /**
496      * Load result attributes.
497      *
498      * @return set
499      */
500     @SuppressWarnings("unchecked")
501     protected Set<String> getResultAttributes() {
502
503         Set<String> set = new HashSet<>();
504
505         List<Map<String, Object>> resultAttributes = yamlMap.get(RESULTS) != null
506                 ? (List<Map<String, Object>>) ((Map<String, Object>) yamlMap.get(RESULTS)).get(ATTRIBUTES)
507                 : Collections.emptyList();
508
509         if (resultAttributes != null) {
510             for (Map<String, Object> map : resultAttributes) {
511                 for (Entry<String, Object> entry : map.entrySet()) {
512                     Object key = entry.getKey();
513
514                     if (NAME.equals(key)) {
515                         set.add(String.valueOf(entry.getValue()));
516                         break;
517                     }
518                 }
519             }
520         }
521
522         return set;
523     }
524
525     /**
526      * Get request parameters.
527      *
528      * @return set
529      */
530     protected Set<String> getRequestParams() {
531
532         Set<String> set = new HashSet<>();
533
534         @SuppressWarnings("unchecked")
535         List<Map<String, Object>> inputParams = (List<Map<String, Object>>) yamlMap.get(PARAMETERS);
536
537         if (inputParams != null) {
538             for (Map<String, Object> map : inputParams) {
539                 for (Entry<String, Object> entry : map.entrySet()) {
540                     Object key = entry.getKey();
541
542                     if (NAME.equals(key)) {
543                         set.add(String.valueOf(entry.getValue()));
544                         break;
545                     }
546                 }
547             }
548         }
549
550         return set;
551     }
552
553     /**
554      * Validate Boolean.
555      *
556      * @param toValidate
557      *            string
558      * @return boolean
559      */
560     protected static boolean validateBoolean(String toValidate) {
561         return BOOLEAN_VALUES.contains(toValidate.toLowerCase());
562     }
563 }