/* * Copyright 2017 Huawei Technologies Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.onap.cli.fw.http.schema; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.stream.Collectors; import org.onap.cli.fw.cmd.OnapCommand; import org.onap.cli.fw.cmd.OnapCommandType; import org.onap.cli.fw.conf.OnapCommandConfig; import org.onap.cli.fw.conf.OnapCommandConstants; import org.onap.cli.fw.error.OnapCommandException; import org.onap.cli.fw.error.OnapCommandInvalidSchema; import org.onap.cli.fw.error.OnapCommandNotFound; import org.onap.cli.fw.http.auth.OnapCommandHttpService; import org.onap.cli.fw.http.cmd.OnapHttpCommand; import org.onap.cli.fw.http.conf.OnapCommandHttpConstants; import org.onap.cli.fw.http.error.OnapCommandHttpInvalidResultMap; import org.onap.cli.fw.registrar.OnapCommandRegistrar; import org.onap.cli.fw.schema.OnapCommandSchemaLoader; import org.onap.cli.fw.utils.OnapCommandUtils; import com.fasterxml.jackson.databind.ObjectMapper; import net.minidev.json.JSONObject; public class OnapCommandSchemaHttpLoader { public static List loadHttpSchema(OnapHttpCommand cmd, String schemaName, boolean includeDefault, boolean validateSchema) throws OnapCommandException { try { List errors = new ArrayList<>(); if (includeDefault) { Map defaultParameterMap = OnapCommandSchemaLoader.validateSchemaVersion(OnapCommandHttpConstants.DEFAULT_PARAMETER_HTTP_FILE_NAME, cmd.getSchemaVersion()); //mrkanag default_parameter is supported only for parameters. if (defaultParameterMap.containsKey(OnapCommandConstants.INFO)) { defaultParameterMap.remove(OnapCommandConstants.INFO); } errors.addAll(OnapCommandSchemaLoader.parseSchema(cmd, defaultParameterMap, validateSchema)); } Map>> commandYamlMap = (Map>>)OnapCommandSchemaLoader.validateSchemaVersion(schemaName, cmd.getSchemaVersion()); errors.addAll(parseHttpSchema(cmd, commandYamlMap, validateSchema)); return errors; } catch (OnapCommandException e) { throw e; } catch (Exception e) { throw new OnapCommandInvalidSchema(schemaName, e); } } /** * Load the schema. * * @param cmd * OnapHttpCommand * @param schemaName * schema name * @throws OnapCommandException * on error */ public static ArrayList parseHttpSchema(OnapHttpCommand cmd, final Map values, boolean validate) throws OnapCommandException { ArrayList errorList = new ArrayList<>(); try { Map valMap = (Map) values.get(OnapCommandHttpConstants.HTTP); if (valMap != null) { if (validate) { OnapCommandUtils.validateTags(errorList, valMap, OnapCommandConfig.getCommaSeparatedList(OnapCommandHttpConstants.HTTP_SECTIONS), OnapCommandConfig.getCommaSeparatedList(OnapCommandHttpConstants.HTTP_MANDATORY_SECTIONS), OnapCommandHttpConstants.HTTP); errorList.addAll(validateHttpSchemaSection(values)); } for (Map.Entry entry1 : valMap.entrySet()) { String key1 = entry1.getKey(); switch (key1) { case OnapCommandHttpConstants.REQUEST: Map map = (Map) valMap.get(key1); for (Map.Entry entry2 : map.entrySet()) { try { String key2 = entry2.getKey(); switch (key2) { case OnapCommandHttpConstants.URI: Object obj = map.get(key2); cmd.getInput().setUri(obj.toString()); break; case OnapCommandHttpConstants.METHOD_TYPE: Object method = map.get(key2); cmd.getInput().setMethod(method.toString()); break; case OnapCommandHttpConstants.BODY: Object body = map.get(key2); cmd.getInput().setBody(body.toString()); break; case OnapCommandHttpConstants.HEADERS: Map head = (Map) map.get(key2); cmd.getInput().setReqHeaders(head); break; case OnapCommandHttpConstants.QUERIES: Map query = (Map) map.get(key2); cmd.getInput().setReqQueries(query); break; case OnapCommandHttpConstants.CONTEXT: Map context = (Map) map.get(key2); for (String key: context.keySet()) { switch (key) { case OnapCommandHttpConstants.CONTEXT_REMOVE_EMPTY_JSON_NODES: Boolean flag = (Boolean) context.get(OnapCommandHttpConstants.CONTEXT_REMOVE_EMPTY_JSON_NODES); cmd.getInput().getContext().put(OnapCommandHttpConstants.CONTEXT_REMOVE_EMPTY_JSON_NODES, flag.toString()); break; } } break; case OnapCommandHttpConstants.MULTIPART_ENTITY_NAME: Object multipartEntityName = map.get(key2); cmd.getInput().setMultipartEntityName(multipartEntityName.toString()); break; } }catch (Exception ex) { OnapCommandUtils.throwOrCollect(new OnapCommandInvalidSchema(cmd.getSchemaName(), ex), errorList, validate); } } break; case OnapCommandHttpConstants.SERVICE: Map serviceMap = (Map) valMap.get(key1); if (serviceMap != null) { if (validate) { OnapCommandUtils.validateTags(errorList, (Map) valMap.get(key1), OnapCommandConfig.getCommaSeparatedList(OnapCommandHttpConstants.SERVICE_PARAMS_LIST), OnapCommandConfig.getCommaSeparatedList(OnapCommandHttpConstants.SERVICE_PARAMS_MANDATORY_LIST), OnapCommandHttpConstants.SERVICE); HashMap validationMap = new HashMap<>(); validationMap.put(OnapCommandHttpConstants.AUTH, OnapCommandHttpConstants.AUTH_VALUES); validationMap.put(OnapCommandHttpConstants.MODE, OnapCommandHttpConstants.MODE_VALUES); for (String secKey : validationMap.keySet()) { if (serviceMap.containsKey(secKey)) { Object obj = serviceMap.get(secKey); if (obj == null) { errorList.add("Attribute '" + secKey + "' under '" + OnapCommandHttpConstants.SERVICE + "' is empty"); } else { String value = String.valueOf(obj); if (!OnapCommandConfig.getCommaSeparatedList(validationMap.get(secKey)).contains(value)) { errorList.add("Attribute '" + secKey + "' contains invalid value. Valide values are " + OnapCommandConfig.getCommaSeparatedList(validationMap.get(key1))); // } } } } } OnapCommandHttpService srv = new OnapCommandHttpService(); for (Map.Entry entry : serviceMap.entrySet()) { String key = entry.getKey(); switch (key) { case OnapCommandConstants.NAME: srv.setName(serviceMap.get(key)); break; case OnapCommandHttpConstants.VERSION: srv.setVersion(serviceMap.get(key).toString()); break; case OnapCommandHttpConstants.AUTH: Object obj = serviceMap.get(key); srv.setAuthType(obj.toString()); break; case OnapCommandHttpConstants.MODE: Object mode = serviceMap.get(key); srv.setMode(mode.toString()); break; } } cmd.setService(srv); } break; case OnapCommandHttpConstants.SUCCESS_CODES: if (validate) { validateHttpSccessCodes(errorList, (List) valMap.get(key1)); } cmd.setSuccessStatusCodes((ArrayList) valMap.get(key1)); break; case OnapCommandHttpConstants.RESULT_MAP: if (validate) { validateHttpResultMap(errorList, values); } cmd.setResultMap((Map) valMap.get(key1)); break; case OnapCommandHttpConstants.SAMPLE_RESPONSE: // (mrkanag) implement sample response handling break; } } } }catch (OnapCommandException e) { OnapCommandUtils.throwOrCollect(e, errorList, validate); } //Handle the parameters for auth: // for commands, copy params from login command to this command if (!cmd.getService().isNoAuth()) { if (cmd.getInfo().getCommandType().equals(OnapCommandType.AUTH)) { OnapCommandUtils.throwOrCollect(new OnapCommandInvalidSchema( cmd.getSchemaName(), "For auth type commands, http->service->auth should be none"), errorList, validate); } else { OnapCommand login = OnapCommandSchemaHttpLoader.findAuthCommand(cmd, "login"); OnapCommandUtils.copyParamSchemasFrom(login, cmd); } } else { //with service->auth: none, //normal cmd: ignore all auth parms if (!cmd.getInfo().getCommandType().equals(OnapCommandType.AUTH)) { cmd.getParametersMap().get(OnapCommandHttpConstants.DEAFULT_PARAMETER_USERNAME).setInclude(false); cmd.getParametersMap().get(OnapCommandHttpConstants.DEAFULT_PARAMETER_PASSWORD).setInclude(false); cmd.getParametersMap().get(OnapCommandHttpConstants.DEFAULT_PARAMETER_NO_AUTH).setInclude(false); } else { //auth: login and logout commands, ignore no-auth cmd.getParametersMap().get(OnapCommandHttpConstants.DEFAULT_PARAMETER_NO_AUTH).setInclude(false); //auth: only for logout commands, ignore username and password too if (!cmd.getName().endsWith(OnapCommandHttpConstants.AUTH_SERVICE_LOGIN)) { cmd.getParametersMap().get(OnapCommandHttpConstants.DEAFULT_PARAMETER_USERNAME).setInclude(false); cmd.getParametersMap().get(OnapCommandHttpConstants.DEAFULT_PARAMETER_PASSWORD).setInclude(false); } } } return errorList; } public static ArrayList validateHttpSchemaSection(Map values) { ArrayList errorList = new ArrayList<>(); Map map = (Map) values.get(OnapCommandHttpConstants.HTTP); Map requestMap = (Map) map.get(OnapCommandHttpConstants.REQUEST); if (requestMap != null && !requestMap.isEmpty()) { OnapCommandUtils.validateTags(errorList, requestMap, OnapCommandConfig.getCommaSeparatedList(OnapCommandHttpConstants.HTTP_REQUEST_PARAMS), OnapCommandConfig.getCommaSeparatedList(OnapCommandHttpConstants.HTTP_REQUEST_MANDATORY_PARAMS), OnapCommandHttpConstants.REQUEST); String method = (String) requestMap.get(OnapCommandHttpConstants.METHOD); if (method != null && !method.isEmpty()) { if (!OnapCommandConfig.getCommaSeparatedList(OnapCommandHttpConstants.HTTP_METHODS).contains(method.toLowerCase())) { errorList.add("Attribute '" + OnapCommandHttpConstants.METHOD + "' under '" + OnapCommandHttpConstants.REQUEST + "' is invalid, correct types are " + OnapCommandConfig.getCommaSeparatedList(OnapCommandHttpConstants.HTTP_METHODS).toString()); } } else { errorList.add("Http request method cann't be null or empty"); } Set requestParams = getRequestParams(values); Set uriParams = validateHttpUri(errorList, requestMap); Set bodyParams = validateHttpBody(errorList, requestMap); Set headerParams = validateHttpHeaders(requestMap); Set queryParams = validateHttpQueries(requestMap); HashSet totoalParams = new HashSet<>(uriParams); totoalParams.addAll(bodyParams); totoalParams.addAll(headerParams); totoalParams.addAll(queryParams); List nonDeclaredParams = totoalParams.stream().filter(param -> !requestParams.contains(param)) .collect(Collectors.toList()); nonDeclaredParams.stream().forEach(p -> errorList.add("The parameter '" + p + "' declared under 'parameters:' section is not mapped into request section.")); } else { errorList.add(OnapCommandUtils.emptySection(OnapCommandHttpConstants.REQUEST)); } return errorList; } public static void validateHttpSccessCodes(List errorList, List requestSuccessCodes) { if (requestSuccessCodes == null || requestSuccessCodes.isEmpty()) { errorList.add(OnapCommandHttpConstants.HTTP_SUCCESS_CODE_INVALID); return; } for (Object successCode : requestSuccessCodes) { Integer code = (Integer) successCode; if (code < 200 || code >= 300) { if ( code != 404) { errorList.add(OnapCommandHttpConstants.HTTP_SUCCESS_CODE_INVALID); } } } } public static void validateHttpResultMap(List errorList, Map values) throws OnapCommandException { Map valMap = (Map) values.get(OnapCommandHttpConstants.HTTP); List> attributes = (List>) ((Map)values.get(OnapCommandConstants.RESULTS)).get(OnapCommandConstants.ATTRIBUTES); Set resultMapParams = ((Map) valMap.get(OnapCommandHttpConstants.RESULT_MAP)).keySet(); Set resultAttNames = attributes.stream().map(map -> map.get(OnapCommandConstants.NAME)) .collect(Collectors.toSet()); List invaliResultMapParams = resultMapParams.stream() .filter(p -> !resultAttNames.contains(p)).collect(Collectors.toList()); List attributesMissing = resultAttNames.stream() .filter(p -> !resultMapParams.contains(p)).collect(Collectors.toList()); invaliResultMapParams.addAll(attributesMissing); if (!invaliResultMapParams.isEmpty()) { OnapCommandUtils.throwOrCollect(new OnapCommandHttpInvalidResultMap(invaliResultMapParams), errorList, true); } } public static Set validateHttpQueries(Map requestMap) { Map queries = (Map) requestMap.get(OnapCommandHttpConstants.QUERIES); Set queryParamNames = new HashSet<>(); if (queries != null) { for (Entry entry : queries.entrySet()) { OnapCommandUtils.parseParameters(String.valueOf(entry.getValue()), queryParamNames); } } return queryParamNames; } public static Set validateHttpHeaders(Map requestMap) { Map headers = (Map) requestMap.get(OnapCommandHttpConstants.HEADERS); Set headerParamNames = new HashSet<>(); if (headers != null) { for (Entry entry : headers.entrySet()) { OnapCommandUtils.parseParameters(String.valueOf(entry.getValue()), headerParamNames); } } return headerParamNames; } public static Set validateHttpBody(List errorList, Map requestMap) { Set bodyParamNames = new HashSet<>(); Object bodyString = requestMap.get(OnapCommandHttpConstants.BODY); if (bodyString == null) { return bodyParamNames; } String body = String.valueOf(bodyString); if (body == null || "".equals(body)) { errorList.add(OnapCommandHttpConstants.HTTP_BODY_JSON_EMPTY); } else { try { new ObjectMapper().readValue(body, JSONObject.class); } catch (IOException e1) { // NOSONAR errorList.add(OnapCommandHttpConstants.HTTP_BODY_FAILED_PARSING); } } OnapCommandUtils.parseParameters(body, bodyParamNames); return bodyParamNames; } public static Set validateHttpUri(List errorList, Map requestMap) { Set uriParamNames = new HashSet<>(); String uri = (String) requestMap.get(OnapCommandHttpConstants.URI); if (uri == null || uri.isEmpty()) { errorList.add(OnapCommandUtils.emptySection(OnapCommandHttpConstants.URI)); return uriParamNames; } OnapCommandUtils.parseParameters(uri, uriParamNames); return uriParamNames; } public static Set getRequestParams(Map yamlMap) { Set set = new HashSet<>(); @SuppressWarnings("unchecked") List> inputParams = (List>) yamlMap.get(OnapCommandConstants.PARAMETERS); if (inputParams != null) { for (Map map : inputParams) { for (Entry entry : map.entrySet()) { Object key = entry.getKey(); if (OnapCommandConstants.NAME.equals(key)) { set.add(String.valueOf(entry.getValue())); break; } } } } return set; } /** * * @param authAction login/logout * @return * @throws OnapCommandException */ public static OnapCommand findAuthCommand(OnapHttpCommand forCmd, String authAction) throws OnapCommandException { OnapCommand auth = null; try { //mrkanag: fix this to discover the auth command by matching info->product & service auth = OnapCommandRegistrar.getRegistrar().get( forCmd.getInfo().getService() + "-" + forCmd.getService().getAuthType() + "-" + authAction, forCmd.getInfo().getProduct()); } catch (OnapCommandNotFound e) { // NOSONAR auth = OnapCommandRegistrar.getRegistrar().get( forCmd.getService().getAuthType() + "-" + authAction, forCmd.getInfo().getProduct()); } return auth; } }