diff options
Diffstat (limited to 'csarvalidation/src/main/java')
42 files changed, 2129 insertions, 445 deletions
diff --git a/csarvalidation/src/main/java/org/onap/cvc/csar/CSARArchive.java b/csarvalidation/src/main/java/org/onap/cvc/csar/CSARArchive.java index 1c05948..05f2070 100644 --- a/csarvalidation/src/main/java/org/onap/cvc/csar/CSARArchive.java +++ b/csarvalidation/src/main/java/org/onap/cvc/csar/CSARArchive.java @@ -92,6 +92,9 @@ public class CSARArchive implements AutoCloseable { public static final String CSAR_ARCHIVE = "CSAR Archive"; public static final String DOESS_NOT_EXIST = " does not exist"; + public static final String CERT = ".cert"; + public static final String YAML = ".yaml"; + public static final String MF = ".mf"; public enum Mode { WITH_TOSCA_META_DIR, @@ -496,7 +499,7 @@ public class CSARArchive implements AutoCloseable { CSAR_ARCHIVE, -1, "Manifest file name should match the definition YAML name", - definitionYaml + ".mf", //fix the name part + definitionYaml + MF, //fix the name part manifest); this.setCode("0x0013"); @@ -521,13 +524,20 @@ public class CSARArchive implements AutoCloseable { CSAR_ARCHIVE, -1, "certificate file name should match the definition YAML name", - definitionYaml + ".cert", //fix the name part + definitionYaml + CERT, //fix the name part certificate); this.setCode("0x0015"); } } + public static class CSARErrorNoManifestsFound extends CSARError { + public CSARErrorNoManifestsFound() { + super("0x0016"); + this.message = "No manifest file found in CSAR!"; + } + } + /** * Holds the CSAR meta data values in both Modes * @@ -966,32 +976,29 @@ public class CSARArchive implements AutoCloseable { lineNo ++; line = line.trim(); - if (line.startsWith("#") || line.trim().isEmpty()) { - continue; - } - - String []lineTokens = line.split(":"); + if (!line.startsWith("#") && !line.trim().isEmpty()) { + String []lineTokens = line.split(":"); - if (lineTokens.length != 2) { - errors.add( - new CSARErrorIgnored( - line, - TOSCA_METADATA_TOSCA_META, - lineNo, - null)); - continue; - } + if (lineTokens.length != 2) { + errors.add( + new CSARErrorIgnored( + line, + TOSCA_METADATA_TOSCA_META, + lineNo, + null)); + continue; + } - String key = lineTokens[0].trim(); - String value = lineTokens[1].trim(); + String key = lineTokens[0].trim(); + String value = lineTokens[1].trim(); - if(key.equalsIgnoreCase(TOSCA_METADATA_TOSCA_META_TOSCA_META_FILE_VERSION)) { + if(key.equalsIgnoreCase(TOSCA_METADATA_TOSCA_META_TOSCA_META_FILE_VERSION)) { this.toscaMeta.setMetaDataFileVersion(value); - } else if(key.equalsIgnoreCase(TOSCA_METADATA_TOSCA_META_CSAR_VERSION)){ + } else if(key.equalsIgnoreCase(TOSCA_METADATA_TOSCA_META_CSAR_VERSION)){ this.toscaMeta.setCsarVersion(value); - } else if(key.equalsIgnoreCase(TOSCA_METADATA_TOSCA_META_CREATED_BY)) { + } else if(key.equalsIgnoreCase(TOSCA_METADATA_TOSCA_META_CREATED_BY)) { this.toscaMeta.setCompanyName(value); - } else if(key.equalsIgnoreCase(TOSCA_METADATA_TOSCA_META_ENTRY_DEFINITIONS)) { + } else if(key.equalsIgnoreCase(TOSCA_METADATA_TOSCA_META_ENTRY_DEFINITIONS)) { this.toscaMeta.setEntryDefinitionYaml(value); this.definitionYamlFile = new File(this.tempDir.toFile().getAbsolutePath() + File.separator + (this.toscaMeta.getEntryDefinitionYaml())); @@ -1001,7 +1008,7 @@ public class CSARArchive implements AutoCloseable { this.toscaMeta.getEntryDefinitionYaml(), lineNo)); } - } else if(key.equalsIgnoreCase(getEntryManifestParamName())) { + } else if(key.equalsIgnoreCase(getEntryManifestParamName())) { this.toscaMeta.setEntryManifestMf(value); this.manifestMfFile = this.tempDir.resolve(this.toscaMeta.getEntryManifestMf()).toFile(); if (!this.manifestMfFile.exists()) { @@ -1009,7 +1016,7 @@ public class CSARArchive implements AutoCloseable { this.toscaMeta.getEntryManifestMf(), lineNo, getEntryManifestParamName())); } - } else if(key.equalsIgnoreCase(getEntryChangeLogParamName())) { + } else if(key.equalsIgnoreCase(getEntryChangeLogParamName())) { this.toscaMeta.setEntryChangeLog(value); this.changeLogTxtFile = this.tempDir.resolve(this.toscaMeta.getEntryChangeLog()).toFile(); if (!this.changeLogTxtFile.exists()) { @@ -1017,37 +1024,38 @@ public class CSARArchive implements AutoCloseable { this.toscaMeta.getEntryChangeLog(), lineNo, getEntryChangeLogParamName())); } - } else if(key.equalsIgnoreCase(TOSCA_METADATA_TOSCA_META_ENTRY_TESTS)) { + } else if(key.equalsIgnoreCase(TOSCA_METADATA_TOSCA_META_ENTRY_TESTS)) { this.toscaMeta.setEntryTest(value); - this.testsFolder= this.tempDir.resolve(this.toscaMeta.getEntryTest()).toFile(); + this.testsFolder = this.tempDir.resolve(this.toscaMeta.getEntryTest()).toFile(); if (!this.testsFolder.exists() || !this.testsFolder.isDirectory()) { errors.add(new CSARErrorInvalidEntryValueTestsNotFound( this.toscaMeta.getEntryTest(), lineNo)); } - } else if(key.equalsIgnoreCase(TOSCA_METADATA_TOSCA_META_ENTRY_LICENSES)) { + } else if(key.equalsIgnoreCase(TOSCA_METADATA_TOSCA_META_ENTRY_LICENSES)) { this.toscaMeta.setEntryLicense(value); - this.licensesFolder= this.tempDir.resolve(this.toscaMeta.getEntryLicense()).toFile(); + this.licensesFolder = this.tempDir.resolve(this.toscaMeta.getEntryLicense()).toFile(); if (!this.licensesFolder.exists() || !this.licensesFolder.isDirectory()) { errors.add(new CSARErrorInvalidEntryValueLicenseNotFound( this.toscaMeta.getEntryLicense(), lineNo)); } - } else if(key.equalsIgnoreCase(getEntryCertificateParamName())) { + } else if(key.equalsIgnoreCase(getEntryCertificateParamName())) { this.toscaMeta.setEntryCertificate(value); - this.certificatesFile= this.tempDir.resolve(this.toscaMeta.getEntryCertificate()).toFile(); + this.certificatesFile = this.tempDir.resolve(this.toscaMeta.getEntryCertificate()).toFile(); if (!this.certificatesFile.exists()) { errors.add(new CSARErrorInvalidEntryValueCertificatesNotFound( this.toscaMeta.getEntryCertificate(), lineNo)); } - } else { + } else { errors.add( new CSARErrorIgnored( key, TOSCA_METADATA_TOSCA_META, lineNo, null)); + } } } @@ -1071,7 +1079,7 @@ public class CSARArchive implements AutoCloseable { } else { //definition files - File []files = this.tempDir.toFile().listFiles((dir, name) -> name.endsWith(".yaml")); + File []files = this.tempDir.toFile().listFiles((dir, name) -> name.endsWith(YAML)); if (files.length == 0) { errors.add( @@ -1088,9 +1096,12 @@ public class CSARArchive implements AutoCloseable { this.toscaMeta.setEntryDefinitionYaml(this.definitionYamlFile.getName()); //manifest - files = this.tempDir.toFile().listFiles((dir, name) -> name.endsWith(".mf")); + files = this.tempDir.toFile().listFiles((dir, name) -> name.endsWith(MF)); - if (files.length > 1) { + if (files.length == 0) { + errors.add(new CSARErrorNoManifestsFound()); + this.toscaMeta.setEntryManifestMf(null); + } else if (files.length > 1) { List<String> fileNames = new ArrayList<>(); for (File f: files) { fileNames.add(f.getName()); @@ -1102,9 +1113,9 @@ public class CSARArchive implements AutoCloseable { //name should match the definition yaml String defYaml = this.toscaMeta.getEntryDefinitionYaml().substring( - 0, this.toscaMeta.getEntryDefinitionYaml().lastIndexOf(".yaml")); + 0, this.toscaMeta.getEntryDefinitionYaml().lastIndexOf(YAML)); String mfFile = this.toscaMeta.getEntryManifestMf().substring( - 0, this.toscaMeta.getEntryManifestMf().lastIndexOf(".mf")); + 0, this.toscaMeta.getEntryManifestMf().lastIndexOf(MF)); if (!defYaml.equalsIgnoreCase(mfFile)) { errors.add(new CSARErrorMismatchDefinitionYamlVsManifestMf( @@ -1115,9 +1126,11 @@ public class CSARArchive implements AutoCloseable { } //certificate - files = this.tempDir.toFile().listFiles((dir, name) -> name.endsWith(".cert")); + files = this.tempDir.toFile().listFiles((dir, name) -> name.endsWith(CERT)); - if (files.length > 1) { + if (files.length == 0) { + this.toscaMeta.setEntryCertificate(null); + } else if (files.length > 1) { List<String> fileNames = new ArrayList<>(); for (File f: files) { fileNames.add(f.getName()); @@ -1130,9 +1143,9 @@ public class CSARArchive implements AutoCloseable { //name should match the definition yaml String defYaml = this.toscaMeta.getEntryDefinitionYaml().substring( - 0, this.toscaMeta.getEntryDefinitionYaml().lastIndexOf(".yaml")); + 0, this.toscaMeta.getEntryDefinitionYaml().lastIndexOf(YAML)); String certFile = this.toscaMeta.getEntryCertificate().substring( - 0, this.toscaMeta.getEntryCertificate().lastIndexOf(".cert")); + 0, this.toscaMeta.getEntryCertificate().lastIndexOf(CERT)); if (!defYaml.equalsIgnoreCase(certFile)) { errors.add(new CSARErrorMismatchDefinitionYamlVsCertificateCert( diff --git a/csarvalidation/src/main/java/org/onap/cvc/csar/VTPValidateCSAR.java b/csarvalidation/src/main/java/org/onap/cvc/csar/VTPValidateCSAR.java index 30eb043..74697f1 100644 --- a/csarvalidation/src/main/java/org/onap/cvc/csar/VTPValidateCSAR.java +++ b/csarvalidation/src/main/java/org/onap/cvc/csar/VTPValidateCSAR.java @@ -16,11 +16,12 @@ package org.onap.cvc.csar; +import com.google.gson.Gson; import java.util.ArrayList; +import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Properties; - import org.onap.cli.fw.cmd.OnapCommand; import org.onap.cli.fw.error.OnapCommandException; import org.onap.cli.fw.error.OnapCommandExecutionFailed; @@ -33,19 +34,20 @@ import org.onap.cvc.csar.CSARArchive.CSARError; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.gson.Gson; - /** * Validates CSAR */ @OnapCommandSchema(schema = "vtp-validate-csar.yaml") public class VTPValidateCSAR extends OnapCommand { + private static Gson gson = new Gson(); private static final Logger LOG = LoggerFactory.getLogger(VTPValidateCSAR.class); public static final String PNF_ATTRIBUTE_NAME = "pnf"; public static class CSARValidation { + public static class VNF { + private String name; private String vendor; private String version; @@ -55,30 +57,39 @@ public class VTPValidateCSAR extends OnapCommand { public String getName() { return name; } + public void setName(String name) { this.name = name; } + public String getVendor() { return vendor; } + public void setVendor(String vendor) { this.vendor = vendor; } + public String getVersion() { return version; } + public void setVersion(String version) { this.version = version; } + public String getType() { return type; } + public void setType(String type) { this.type = type; } + public String getMode() { return mode; } + public void setMode(String mode) { this.mode = mode; } @@ -89,35 +100,52 @@ public class VTPValidateCSAR extends OnapCommand { private String criteria; public static class Result { + private boolean passed; private String vnfreqName; private String description; private List<CSARError> errors = new ArrayList<>(); + private List<CSARError> warnings = new ArrayList<>(); + public boolean isPassed() { return passed; } + public void setPassed(boolean passed) { this.passed = passed; } + public String getVnfreqName() { return vnfreqName; } + public void setVnfreqName(String vnfreqName) { this.vnfreqName = vnfreqName; } + public List<CSARError> getErrors() { - return errors; + return Collections.unmodifiableList(errors); } - public void setErrors(List<CSARError> errors) { - this.errors = errors; + + public void addError(CSARError error) { + this.errors.add(error); } + public String getDescription() { return description; } + public void setDescription(String description) { this.description = description; } + public List<CSARError> getWarnings() { + return Collections.unmodifiableList(warnings); + } + + public void addErrorAsWarning(CSARError error) { + this.warnings.add(error); + } } private List<Result> results = new ArrayList<>(); @@ -176,9 +204,10 @@ public class VTPValidateCSAR extends OnapCommand { } private static Properties properties = new Properties(); + static { try { - properties.load(VTPValidateCSAR.class.getClass().getResourceAsStream("/vnfreqs.properties")); + properties.load(VTPValidateCSAR.class.getResourceAsStream("/vnfreqs.properties")); } catch (Exception e) { LOG.error(e.getMessage(), e); } @@ -191,23 +220,23 @@ public class VTPValidateCSAR extends OnapCommand { boolean isPnf = (boolean) getParametersMap().get(PNF_ATTRIBUTE_NAME).getValue(); boolean overallPass = true; - try(CSARArchive csar = isPnf ? new PnfCSARArchive(): new CSARArchive()){ + try (CSARArchive csar = isPnf ? new PnfCSARArchive() : new CSARArchive()) { csar.init(path); csar.parse(); CSARValidation validation = createCsarValidationFor(csar); String keyErrors = isPnf ? "pnferrors.ignored" : "vnferrors.ignored"; - List <String> ignoreCodes = this.getPropertiesList(keyErrors); + List<String> ignoreCodes = this.getPropertiesList(keyErrors); //Add SOL004 error codes CSARValidation.Result resultSOL004 = new CSARValidation.Result(); resultSOL004.setVnfreqName("SOL004"); resultSOL004.setDescription(csar.getSOL004Version()); - for (CSARError error: csar.getErrors()) { + for (CSARError error : csar.getErrors()) { if (!ignoreCodes.contains(error.getCode())) { - resultSOL004.getErrors().add(error); + resultSOL004.addError(error); overallPass = false; } } @@ -217,34 +246,11 @@ public class VTPValidateCSAR extends OnapCommand { //Run thru the vnfreqs requirement checks String keyReqs = isPnf ? "pnfreqs.enabled" : "vnfreqs.enabled"; - for (String vnfreq: this.getPropertiesList(keyReqs)) { + for (String vnfreq : this.getPropertiesList(keyReqs)) { CSARValidation.Result result = new CSARValidation.Result(); result.setVnfreqName(vnfreq); - try { - String command = "csar-validate-" + vnfreq; - OnapCommand cmd = OnapCommandRegistrar.getRegistrar().get(command, this.getInfo().getProduct()); - cmd.getParametersMap().get("csar").setValue(path); - setPnfValueIfAvailable(isPnf, cmd); - - result.setDescription(cmd.getDescription()); - cmd.execute(); - - for (CSARError error: (List<CSARError>) cmd.getResult().getOutput()) { - if (!ignoreCodes.contains(error.getCode()) && !ignoreCodes.contains(vnfreq + "-"+ error.getCode())) { - result.getErrors().add(error); - overallPass = false; - } - } - - result.setPassed(overallPass); - validation.getResults().add(result); - } catch (Exception e) { - result.setPassed(false); - overallPass = false; - result.getErrors().add(new CSARArchive.CSARErrorUnknown(e.getMessage())); - validation.getResults().add(result); - } + overallPass = validateVnfOrPnf(path, validation, ignoreCodes, vnfreq, result, isPnf, overallPass); } validation.setDate(new Date().toString()); @@ -257,6 +263,41 @@ public class VTPValidateCSAR extends OnapCommand { } } + private boolean validateVnfOrPnf(String path, CSARValidation validation, + List<String> ignoreCodes, String vnfreq, CSARValidation.Result result, boolean isPnf, boolean overallPass) { + try { + String command = "csar-validate-" + vnfreq; + OnapCommand cmd = OnapCommandRegistrar.getRegistrar().get(command, this.getInfo().getProduct()); + cmd.getParametersMap().get("csar").setValue(path); + setPnfValueIfAvailable(isPnf, cmd); + + result.setDescription(cmd.getDescription()); + cmd.execute(); + + for (CSARError error : (List<CSARError>) cmd.getResult().getOutput()) { + if (!isErrorIgnored(ignoreCodes, vnfreq, error)) { + result.addError(error); + overallPass = false; + } else { + result.addErrorAsWarning(error); + } + } + + result.setPassed(result.getErrors().isEmpty()); + validation.getResults().add(result); + } catch (Exception e) { + result.setPassed(false); + overallPass = false; + result.addError(new CSARArchive.CSARErrorUnknown(e.getMessage())); + validation.getResults().add(result); + } + return overallPass; + } + + private boolean isErrorIgnored(List<String> ignoreCodes, String vnfreq, CSARError error) { + return ignoreCodes.contains(error.getCode()) || ignoreCodes.contains(vnfreq + "-" + error.getCode()); + } + static CSARValidation createCsarValidationFor(CSARArchive csar) { //Fill up the basic details CSARValidation validation = new CSARValidation(); @@ -270,11 +311,11 @@ public class VTPValidateCSAR extends OnapCommand { private void setOperationResult(CSARValidation validation) throws Exception { //NOSONAR this.getResult().getRecordsMap().get("vnf").getValues().add( - gson.toJson(validation.getVnf())); + gson.toJson(validation.getVnf())); this.getResult().getRecordsMap().get("date").getValues().add(validation.getDate()); this.getResult().getRecordsMap().get("criteria").getValues().add(validation.getCriteria()); this.getResult().getRecordsMap().get("results").getValues().add( - gson.toJson(validation.getResults())); + gson.toJson(validation.getResults())); this.getResult().setOutput(gson.toJson(validation)); this.getResult().setType(OnapCommandResultType.TEXT); @@ -282,7 +323,7 @@ public class VTPValidateCSAR extends OnapCommand { private void setPnfValueIfAvailable(boolean isPnf, OnapCommand cmd) throws OnapCommandInvalidParameterValue { final OnapCommandParameter pnf = cmd.getParametersMap().get(PNF_ATTRIBUTE_NAME); - if(pnf!=null) { + if (pnf != null) { pnf.setValue(isPnf); } } @@ -290,7 +331,7 @@ public class VTPValidateCSAR extends OnapCommand { private List<String> getPropertiesList(String key) { String[] enabledReqs = properties.getProperty(key, "").split(","); List<String> list = new ArrayList<>(); - for(String req: enabledReqs) { + for (String req : enabledReqs) { if (!req.isEmpty()) { list.add(req); } diff --git a/csarvalidation/src/main/java/org/onap/cvc/csar/cc/VTPValidateCSARBase.java b/csarvalidation/src/main/java/org/onap/cvc/csar/cc/VTPValidateCSARBase.java index a5a13e3..b004858 100644 --- a/csarvalidation/src/main/java/org/onap/cvc/csar/cc/VTPValidateCSARBase.java +++ b/csarvalidation/src/main/java/org/onap/cvc/csar/cc/VTPValidateCSARBase.java @@ -34,7 +34,7 @@ public abstract class VTPValidateCSARBase extends OnapCommand { protected List<CSARError> errors = new ArrayList<>(); - protected abstract void validateCSAR(CSARArchive csar) throws Exception; + protected abstract void validateCSAR(CSARArchive csar) throws Exception; //NOSONAR protected abstract String getVnfReqsNo(); @@ -57,7 +57,7 @@ public abstract class VTPValidateCSARBase extends OnapCommand { this.validateCSAR(csar); } catch (Exception e) { - LOG.error(this.getVnfReqsNo() + ": Failed to validate CSAR" , e); + LOG.error("{}: Failed to validate CSAR {}", this.getVnfReqsNo(), e); throw new OnapCommandExecutionFailed(e.getMessage()); } diff --git a/csarvalidation/src/main/java/org/onap/cvc/csar/cc/sol001/VTPValidateCSARR02454.java b/csarvalidation/src/main/java/org/onap/cvc/csar/cc/sol001/VTPValidateCSARR02454.java index 7d0489e..4270d87 100644 --- a/csarvalidation/src/main/java/org/onap/cvc/csar/cc/sol001/VTPValidateCSARR02454.java +++ b/csarvalidation/src/main/java/org/onap/cvc/csar/cc/sol001/VTPValidateCSARR02454.java @@ -60,10 +60,11 @@ public class VTPValidateCSARR02454 extends VTPValidateCSARBase { } } - if (!vlExist) - this.errors.add(new CSARErrorEntryMissingSwImage( + if (!vlExist) { + this.errors.add(new CSARErrorEntryMissingSwImage( csar.getDefinitionYamlFile().getName(), "Software Image")); + } } } } diff --git a/csarvalidation/src/main/java/org/onap/cvc/csar/cc/sol001/VTPValidateCSARR35851.java b/csarvalidation/src/main/java/org/onap/cvc/csar/cc/sol001/VTPValidateCSARR35851.java index 52582a6..78be3e6 100644 --- a/csarvalidation/src/main/java/org/onap/cvc/csar/cc/sol001/VTPValidateCSARR35851.java +++ b/csarvalidation/src/main/java/org/onap/cvc/csar/cc/sol001/VTPValidateCSARR35851.java @@ -59,7 +59,7 @@ public class VTPValidateCSARR35851 extends VTPValidateCSARBase { yaml = (Map<String, ?>) yaml.get("topology_template"); Map<String, ?> nodeTmpls = (Map<String,?>) yaml.get("node_templates"); - boolean vlExist[] = new boolean[3]; + boolean[] vlExist = new boolean[3]; for (Object nodeO: nodeTmpls.values()) { Map<String, ?> node = (Map<String, ?>) nodeO; diff --git a/csarvalidation/src/main/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR01123.java b/csarvalidation/src/main/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR01123.java index 5afc1d9..a7c5737 100644 --- a/csarvalidation/src/main/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR01123.java +++ b/csarvalidation/src/main/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR01123.java @@ -1,5 +1,6 @@ /* * Copyright 2017 Huawei Technologies Co., Ltd. + * Modified 2020 Nokia. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +21,16 @@ import org.onap.cli.fw.schema.OnapCommandSchema; import org.onap.cvc.csar.CSARArchive; import org.onap.cvc.csar.CSARArchive.CSARErrorEntryMissing; import org.onap.cvc.csar.cc.VTPValidateCSARBase; +import org.onap.cvc.csar.parser.SourcesParser; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; @OnapCommandSchema(schema = "vtp-validate-csar-r01123.yaml") public class VTPValidateCSARR01123 extends VTPValidateCSARBase { @@ -27,19 +38,115 @@ public class VTPValidateCSARR01123 extends VTPValidateCSARBase { public static class CSARErrorEntryVNFProviderDetailsNotFound extends CSARErrorEntryMissing { public CSARErrorEntryVNFProviderDetailsNotFound() { super("VNF Vendor details", - CSARArchive.TOSCA_METADATA + " or " + CSARArchive.TOSCA_METADATA_TOSCA_META_ENTRY_DEFINITIONS + " file"); + CSARArchive.TOSCA_METADATA + " or " + CSARArchive.TOSCA_METADATA_TOSCA_META_ENTRY_DEFINITIONS + " file"); this.setCode("0x1000"); } } + public static class CSARErrorNotAllFilesLocatedInCSARWhereListedInManifest extends CSARErrorEntryMissing { + CSARErrorNotAllFilesLocatedInCSARWhereListedInManifest(List<String> fileInCsarThatAreNotLocatedInManifest) { + super("Source", + CSARArchive.TOSCA_METADATA); + this.setCode("0x1001"); + this.message = "file(s): [" + + String.join(", ", fileInCsarThatAreNotLocatedInManifest) + + "] available in CSAR, but cannot be found in Manifest as Source"; + } + } + + public static class CSARErrorNotAllFilesLocatedInManifestWhereListedInCsar extends CSARErrorEntryMissing { + CSARErrorNotAllFilesLocatedInManifestWhereListedInCsar(List<String> fileInCsarThatAreNotLocatedInManifest) { + super("Source", + CSARArchive.TOSCA_METADATA); + this.setCode("0x1002"); + this.message = "file(s): [" + + String.join(", ", fileInCsarThatAreNotLocatedInManifest) + + "] defined in Manifest as Source, but cannot be found in CSAR"; + } + } + @Override protected void validateCSAR(CSARArchive csar) throws Exception { + verifyThatProviderDataAreDefined(csar); + verifyPackageFileStructure(csar); + } + + private void verifyPackageFileStructure(CSARArchive csar) throws IOException { + Path rootFolder = getRootFolder(csar); + List<String> filesInCsar = getAllFilesInDirectory(rootFolder); + List<String> sourcesInManifest = getAllFilesFromManifestSources(csar.getManifest()); + + if (areAllFilesDefinedInManifest(filesInCsar, sourcesInManifest)) { + verifyThatAllFilesFromCsarAreDefinedInManifest(filesInCsar, sourcesInManifest); + verifyThatAllFilesDefinedInManifestAreAvailableInCsar(sourcesInManifest, filesInCsar); + } + } + + private void verifyThatProviderDataAreDefined(CSARArchive csar) { if (csar.getVendorName() == null || - csar.getVersion() == null) { + csar.getVersion() == null) { errors.add(new CSARErrorEntryVNFProviderDetailsNotFound()); } } + private Path getRootFolder(CSARArchive csar) throws IOException { + return csar.getWorkspace().getPathToCsarFolder() + .orElseThrow(() -> new IOException("Couldn't find CSAR root catalog")); + } + + + private void verifyThatAllFilesDefinedInManifestAreAvailableInCsar(List<String> sourcesInManifest, List<String> filesInCsar) { + if(!filesInCsar.containsAll(sourcesInManifest)){ + List<String> sourcesNotAvailableInCsarFile = fetchElementsNotAvailableAtSecondList(sourcesInManifest, filesInCsar); + errors.add(new CSARErrorNotAllFilesLocatedInManifestWhereListedInCsar(sourcesNotAvailableInCsarFile)); + } + } + + private void verifyThatAllFilesFromCsarAreDefinedInManifest(List<String> filesInCsar, List<String> sourcesInManifest) { + if(!sourcesInManifest.containsAll(filesInCsar) ){ + List<String> filesNotDefinedInManifestFile = fetchElementsNotAvailableAtSecondList(filesInCsar, sourcesInManifest); + errors.add(new CSARErrorNotAllFilesLocatedInCSARWhereListedInManifest(filesNotDefinedInManifestFile)); + } + } + + private List<String> fetchElementsNotAvailableAtSecondList(List<String> firstList, List<String> secondList) { + List<String> copyOfFirstList = new ArrayList<>(firstList); + copyOfFirstList.removeAll(secondList); + return copyOfFirstList; + } + + private boolean areAllFilesDefinedInManifest(List<String> filesInCsar, List<String> sourcesInManifest) { + return filesInCsar.size() != sourcesInManifest.size(); + } + + private List<String> getAllFilesFromManifestSources(CSARArchive.Manifest manifest) { + return manifest.getSources() + .stream() + .map(SourcesParser.Source::getValue) + .filter(filterOutManifestFile()) + .collect(Collectors.toList()); + } + + private List<String> getAllFilesInDirectory(Path rootPath) throws IOException { + try (Stream<Path> paths = Files.walk(rootPath, Integer.MAX_VALUE)) { + return paths + .filter(filterOutDirectories()) + .map(rootPath::relativize) + .map(String::valueOf) + .filter(filterOutManifestFile()) + .collect(Collectors.toList()); + } + } + + private Predicate<Path> filterOutDirectories() { + return path -> !Files.isDirectory(path); + } + + + private Predicate<String> filterOutManifestFile() { + return path -> !path.endsWith(".mf"); + } + @Override protected String getVnfReqsNo() { return "R01123"; diff --git a/csarvalidation/src/main/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR130206.java b/csarvalidation/src/main/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR130206.java index fefe65b..05feb54 100644 --- a/csarvalidation/src/main/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR130206.java +++ b/csarvalidation/src/main/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR130206.java @@ -26,6 +26,10 @@ import org.onap.cvc.csar.cc.VTPValidateCSARBase; import org.onap.cvc.csar.parser.ManifestFileModel; import org.onap.cvc.csar.parser.ManifestFileSplitter; import org.onap.cvc.csar.parser.SourcesParser; +import org.onap.cvc.csar.security.CertificateLoadingException; +import org.onap.cvc.csar.security.CmsSignatureData; +import org.onap.cvc.csar.security.CmsSignatureDataFactory; +import org.onap.cvc.csar.security.CmsSignatureLoadingException; import org.onap.cvc.csar.security.CmsSignatureValidator; import org.onap.cvc.csar.security.CmsSignatureValidatorException; import org.onap.cvc.csar.security.ShaHashCodeGenerator; @@ -50,23 +54,31 @@ public class VTPValidateCSARR130206 extends VTPValidateCSARBase { private static final Logger LOG = LoggerFactory.getLogger(VTPValidateCSARR130206.class); private static final String SHA_256 = "SHA-256"; private static final String SHA_512 = "SHA-512"; + private static final String EMPTY_STRING = ""; private final ShaHashCodeGenerator shaHashCodeGenerator = new ShaHashCodeGenerator(); private final ManifestFileSignatureValidator manifestFileSignatureValidator = new ManifestFileSignatureValidator(); public static class CSARErrorUnableToFindCertificate extends CSARArchive.CSARError { - CSARErrorUnableToFindCertificate(String paramName) { + CSARErrorUnableToFindCertificate() { super("0x4001"); - this.message = String.format("Unable to find cert file defined by %s!", paramName); + this.message = "Unable to find cert file!"; } } - public static class CSARErrorUnableToFindCmsSection extends CSARArchive.CSARError { + public static class CSARErrorUnableToFindCms extends CSARArchive.CSARError { - CSARErrorUnableToFindCmsSection() { + CSARErrorUnableToFindCms() { super("0x4002"); - this.message = "Unable to find CMS section in manifest!"; + this.message = "Unable to find cms signature!"; + } + } + + public static class CSARErrorUnableToLoadCms extends CSARArchive.CSARError { + CSARErrorUnableToLoadCms() { + super("0x4002"); + this.message = "Unable to load cms signature!"; } } @@ -106,7 +118,7 @@ public class VTPValidateCSARR130206 extends VTPValidateCSARBase { CSARErrorInvalidSignature() { super("0x4007"); - this.message = "File has invalid CMS signature!"; + this.message = "File has invalid signature!"; } } @@ -118,6 +130,61 @@ public class VTPValidateCSARR130206 extends VTPValidateCSARBase { } } + public static class CSARErrorUnableToFindEntryCertificate extends CSARArchive.CSARError { + + CSARErrorUnableToFindEntryCertificate() { + super("0x4009"); + this.message = "Unable to find cert file defined by ETSI-Entry-Certificate!"; + } + } + + public static class CSARErrorEntryCertificateIsDefinedDespiteTheCms extends CSARArchive.CSARError { + + CSARErrorEntryCertificateIsDefinedDespiteTheCms() { + super("0x4011"); + this.message = "ETSI-Entry-Certificate entry in Tosca.meta is defined despite the certificate is included in the signature container"; + } + } + + public static class CSARErrorEntryCertificateIsPresentDespiteTheCms extends CSARArchive.CSARError { + + CSARErrorEntryCertificateIsPresentDespiteTheCms() { + super("0x4012"); + this.message = "ETSI-Entry-Certificate certificate present despite the certificate is included in the signature container"; + } + } + + public static class CSARErrorRootCertificateIsPresentDespiteTheCms extends CSARArchive.CSARError { + + CSARErrorRootCertificateIsPresentDespiteTheCms() { + super("0x4013"); + this.message = "Certificate present in root catalog despite the certificate is included in the signature container"; + } + } + + public static class CSARErrorRootCertificateIsPresentDespiteTheEtsiEntryCertificate extends CSARArchive.CSARError { + + CSARErrorRootCertificateIsPresentDespiteTheEtsiEntryCertificate() { + super("0x4013"); + this.message = "Certificate present in root catalog despite the TOSCA.meta file"; + } + } + + public static class CSARErrorUnableToFindCertificateEntryInTosca extends CSARArchive.CSARError { + + CSARErrorUnableToFindCertificateEntryInTosca() { + super("0x4014"); + this.message = "Unable to find ETSI-Entry-Certificate in Tosca file"; + } + } + + public static class CSARWarningNoSecurity extends CSARArchive.CSARErrorWarning { + CSARWarningNoSecurity() { + super(EMPTY_STRING, EMPTY_STRING, -1, EMPTY_STRING); + this.message = "Warning. Consider adding package integrity and authenticity assurance according to ETSI NFV-SOL 004 Security Option 1"; + } + } + @Override protected void validateCSAR(CSARArchive csar) throws OnapCommandException { @@ -137,20 +204,146 @@ public class VTPValidateCSARR130206 extends VTPValidateCSARBase { } private void validate(CSARArchive csar, Path csarRootDirectory) throws IOException, NoSuchAlgorithmException { - final CSARArchive.Manifest manifest = csar.getManifest(); + if (containsCms(csar.getManifest())) { + validateCmsSignature(csar, csarRootDirectory); + } else if ( + ( containsToscaMeta(csar) && containsCertificateInTosca(csar.getToscaMeta()) ) || + containsCertificateInRootCatalog(csar) || + containsHashOrAlgorithm(csar.getManifest())) { + this.errors.add(new CSARErrorUnableToFindCms()); + } else { + this.errors.add(new CSARWarningNoSecurity()); + } + } + + private void validateCmsSignature(CSARArchive csar, Path csarRootDirectory) throws NoSuchAlgorithmException, IOException { + try { + CmsSignatureData signatureData = this.manifestFileSignatureValidator.createSignatureData(csar.getManifestMfFile()); + if (signatureData.getCertificate().isPresent()) { + validateCertificationUsingCmsCertificate(signatureData, csar, csarRootDirectory); + } else if (containsToscaMeta(csar)) { + validateCertificationUsingTosca(signatureData, csar, csarRootDirectory); + } else if (containsCertificateInRootCatalog(csar)) { + validateCertificationUsingCertificateFromRootDirectory(signatureData, csar, csarRootDirectory); + } else { + this.errors.add(new CSARErrorUnableToFindCertificate()); + } + } catch (CmsSignatureLoadingException e) { + LOG.error("Unable to load CMS!", e); + this.errors.add(new CSARErrorUnableToLoadCms()); + } + } + + private boolean containsCms(CSARArchive.Manifest manifest) { + String cms = manifest.getCms(); + return cms != null && !cms.equals(EMPTY_STRING); + } + + private boolean containsToscaMeta(CSARArchive archive) { + return archive.getToscaMetaFile() != null; + } + + private boolean containsCertificateInTosca(CSARArchive.TOSCAMeta toscaMeta) { + String certificate = toscaMeta.getEntryCertificate(); + return certificate != null && !certificate.equals(EMPTY_STRING); + } + + private boolean containsCertificateInRootCatalog(CSARArchive csar) { + File potentialCertificateFileInRootDirectory = getCertificateFromRootDirectory(csar); + return potentialCertificateFileInRootDirectory.exists(); + } - validateSecurityStructure(csar, csarRootDirectory); + private boolean containsHashOrAlgorithm(CSARArchive.Manifest manifest) { + return manifest.getSources().stream().anyMatch( + source -> + !source.getAlgorithm().equals(EMPTY_STRING) || + !source.getHash().equals(EMPTY_STRING) + ); + } + + private void validateCertificationUsingCmsCertificate(CmsSignatureData signatureData, CSARArchive csar, Path csarRootDirectory) + throws NoSuchAlgorithmException, IOException { + validateAllSources(csar, csarRootDirectory); + validateFileSignature(signatureData); + if (containsCertificateInTosca(csar.getToscaMeta())) { + errors.add(new CSARErrorEntryCertificateIsDefinedDespiteTheCms()); + if (csar.getFileFromCsar(csar.getToscaMeta().getEntryCertificate()).exists()) { + errors.add(new CSARErrorEntryCertificateIsPresentDespiteTheCms()); + } + } + if (containsCertificateInRootCatalog(csar)) { + errors.add(new CSARErrorRootCertificateIsPresentDespiteTheCms()); + } + } + + private void validateCertificationUsingTosca(CmsSignatureData signatureData, CSARArchive csar, Path csarRootDirectory) + throws NoSuchAlgorithmException, IOException { + validateAllSources(csar, csarRootDirectory); + if (loadCertificateFromTosca(signatureData, csar)) { + validateFileSignature(signatureData); + } + if (containsCertificateInRootCatalog(csar) && rootCertificateIsNotReferredAsToscaEtsiEntryCertificate(csar)) { + errors.add(new CSARErrorRootCertificateIsPresentDespiteTheEtsiEntryCertificate()); + } + } + + private boolean loadCertificateFromTosca(CmsSignatureData signatureData, CSARArchive csar) { + if(csar.getToscaMeta().getEntryCertificate() != null) { + try { + final Path absolutePathToEntryCertificate = csar.getFileFromCsar(csar.getToscaMeta().getEntryCertificate()).toPath(); + signatureData.loadCertificate(absolutePathToEntryCertificate); + return true; + } catch (CertificateLoadingException e) { + this.errors.add(new CSARErrorUnableToFindEntryCertificate()); + return false; + } + } else { + this.errors.add(new CSARErrorUnableToFindCertificateEntryInTosca()); + return false; + } + } + + private boolean rootCertificateIsNotReferredAsToscaEtsiEntryCertificate(CSARArchive csar) { + String pathToRootCertificate = getCertificateFromRootDirectory(csar).getPath(); + String pathToEntryEtsiCertificate = csar.getFileFromCsar(csar.getToscaMeta().getEntryCertificate()).getPath(); + return !pathToRootCertificate.equals(pathToEntryEtsiCertificate); + } + + private void validateCertificationUsingCertificateFromRootDirectory(CmsSignatureData signatureData, CSARArchive csar, Path csarRootDirectory) + throws NoSuchAlgorithmException, IOException { + validateAllSources(csar, csarRootDirectory); + if (loadCertificateFromRootDirectory(signatureData, csar)) { + validateFileSignature(signatureData); + } + } + + private boolean loadCertificateFromRootDirectory(CmsSignatureData signatureData, CSARArchive csar) { + try { + File certificateFileFromRootDirectory = getCertificateFromRootDirectory(csar); + signatureData.loadCertificate(certificateFileFromRootDirectory.toPath()); + return true; + } catch (CertificateLoadingException e) { + LOG.error("Uable to read ETSI entry certificate file!", e); + return false; + } + } + + private File getCertificateFromRootDirectory(CSARArchive csar) { + String nameOfCertificate = + csar.getManifestMfFile().getName().split("\\.")[0] + + ".cert"; + return csar.getFileFromCsar(nameOfCertificate); + } + + private void validateAllSources(CSARArchive csar, Path csarRootDirectory) + throws NoSuchAlgorithmException, IOException { + final CSARArchive.Manifest manifest = csar.getManifest(); validateSources(csarRootDirectory, manifest); final Map<String, Map<String, List<String>>> nonMano = manifest.getNonMano(); final List<SourcesParser.Source> sources = manifest.getSources(); validateNonManoCohesionWithSources(nonMano, sources); - - final File manifestMfFile = csar.getManifestMfFile(); - if (manifestMfFile != null) { - validateFileSignature(manifestMfFile); - } } private void validateNonManoCohesionWithSources(final Map<String, Map<String, List<String>>> nonMano, @@ -174,36 +367,13 @@ public class VTPValidateCSARR130206 extends VTPValidateCSARBase { } - private void validateFileSignature(File manifestMfFile) { - final boolean isValid = this.manifestFileSignatureValidator.isValid(manifestMfFile); + private void validateFileSignature(CmsSignatureData signatureData) { + final boolean isValid = this.manifestFileSignatureValidator.isValid(signatureData); if (!isValid) { this.errors.add(new CSARErrorInvalidSignature()); } } - private void validateSecurityStructure(CSARArchive csar, Path csarRootDirectory) { - final CSARArchive.Manifest manifest = csar.getManifest(); - final CSARArchive.TOSCAMeta toscaMeta = csar.getToscaMeta(); - final String entryCertificateParamName = csar.getEntryCertificateParamName(); - final Optional<File> entryCertificate = resolveCertificateFilePath(toscaMeta, csarRootDirectory); - if (!entryCertificate.isPresent() || !entryCertificate.get().exists()) { - this.errors.add(new CSARErrorUnableToFindCertificate(entryCertificateParamName)); - } - - if (manifest.getCms() == null || manifest.getCms().isEmpty()) { - this.errors.add(new CSARErrorUnableToFindCmsSection()); - } - } - - private Optional<File> resolveCertificateFilePath(CSARArchive.TOSCAMeta toscaMeta, Path csarRootDirectory) { - final String certificatePath = toscaMeta.getEntryCertificate(); - if (certificatePath == null) { - return Optional.empty(); - } else { - return Optional.of(csarRootDirectory.resolve(certificatePath).toFile()); - } - } - private void validateSources(Path csarRootDirectory, CSARArchive.Manifest manifest) throws NoSuchAlgorithmException, IOException { final List<SourcesParser.Source> sources = manifest.getSources(); @@ -256,18 +426,23 @@ public class VTPValidateCSARR130206 extends VTPValidateCSARBase { } - class ManifestFileSignatureValidator { + static class ManifestFileSignatureValidator { - private final Logger LOG = LoggerFactory.getLogger(ManifestFileSignatureValidator.class); private final ManifestFileSplitter manifestFileSplitter = new ManifestFileSplitter(); private final CmsSignatureValidator cmsSignatureValidator = new CmsSignatureValidator(); + private final CmsSignatureDataFactory cmsSignatureDataFactory = new CmsSignatureDataFactory(); + + CmsSignatureData createSignatureData(File manifestFile) throws CmsSignatureLoadingException { + ManifestFileModel mf = manifestFileSplitter.split(manifestFile); + return cmsSignatureDataFactory.createForFirstSigner( + toBytes(mf.getCMS(), mf.getNewLine()), + toBytes(mf.getData(), mf.getNewLine()) + ); + } - boolean isValid(File manifestFile) { + boolean isValid(CmsSignatureData signatureData) { try { - ManifestFileModel mf = manifestFileSplitter.split(manifestFile); - return cmsSignatureValidator.verifySignedData(toBytes(mf.getCMS(), mf.getNewLine()), - Optional.empty(), - toBytes(mf.getData(), mf.getNewLine())); + return cmsSignatureValidator.verifySignedData(signatureData); } catch (CmsSignatureValidatorException e) { LOG.error("Unable to verify signed data!", e); return false; @@ -279,4 +454,5 @@ public class VTPValidateCSARR130206 extends VTPValidateCSARBase { return updatedData.getBytes(Charset.defaultCharset()); } } + } diff --git a/csarvalidation/src/main/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR816745.java b/csarvalidation/src/main/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR816745.java new file mode 100644 index 0000000..b43dbba --- /dev/null +++ b/csarvalidation/src/main/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR816745.java @@ -0,0 +1,140 @@ +/* + * Copyright 2020 Nokia. + * + * 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.cvc.csar.cc.sol004; + +import org.onap.cli.fw.schema.OnapCommandSchema; +import org.onap.cvc.csar.CSARArchive; +import org.onap.cvc.csar.cc.VTPValidateCSARBase; +import org.onap.validation.yaml.YamlFileValidator; +import org.onap.validation.yaml.error.YamlDocumentValidationError; +import org.onap.validation.yaml.exception.YamlProcessingException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.yaml.snakeyaml.error.YAMLException; + +import java.nio.file.Path; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +@OnapCommandSchema(schema = "vtp-validate-csar-r816745.yaml") +public class VTPValidateCSARR816745 extends VTPValidateCSARBase { + + private static final Logger LOGGER = LoggerFactory.getLogger(VTPValidateCSARR816745.class); + + private static class CSARPmDictionaryValidationError extends CSARArchive.CSARError { + + CSARPmDictionaryValidationError(int documentNumber, String file, String path, String message) { + super("0x1000"); + this.message = String.format( + "Invalid YAML document in PM_Dictionary file. %n" + + "In document number %s (excluding document with schema) error occur. %n" + + "Path: %s%n" + + "%s", + documentNumber, path, message + ); + this.file = file; + } + + } + + private static class CSARPmDictionaryLoadingError extends CSARArchive.CSARError { + + CSARPmDictionaryLoadingError(String file, String message) { + super("0x2000"); + this.message = String.format( + "Fail to load PM_Dictionary With error: %s", + message + ); + this.file = file; + } + + } + + private static final String PM_DICTIONARY = "onap_pm_dictionary"; + private static final String SOURCE_ELEMENT_TAG = "Source"; + + @Override + protected void validateCSAR(CSARArchive csar) { + Map<String, Map<String, List<String>>> nonManoFields = csar.getManifest().getNonMano(); + String rootPath = csar.getWorkspace().getPathToCsarFolder().map(Path::toString).orElse("/"); + if (nonManoFields.containsKey(PM_DICTIONARY)) { + getLocationOfPmDictionaryFile(nonManoFields, csar.getManifestMfFile().getName()).ifPresent(pmDictionary -> + validateYamlFile(rootPath+"/",pmDictionary) + ); + } + } + + private Optional<String> getLocationOfPmDictionaryFile(Map<String, Map<String, List<String>>> nonManoFields, String manifestFileName) { + if(nonManoFields.get(PM_DICTIONARY).containsKey(SOURCE_ELEMENT_TAG)) { + return getPathToPmDictionary(nonManoFields, SOURCE_ELEMENT_TAG); + } else if(nonManoFields.get(PM_DICTIONARY).containsKey(SOURCE_ELEMENT_TAG.toLowerCase())) { + return getPathToPmDictionary(nonManoFields, SOURCE_ELEMENT_TAG.toLowerCase()); + } else { + addPmDictionaryLoadingError(manifestFileName, PM_DICTIONARY +" in manifest does not contains key 'Source'"); + return Optional.empty(); + } + } + + private Optional<String> getPathToPmDictionary(Map<String, Map<String, List<String>>> nonManoFields, String sourceElementTag) { + return Optional.ofNullable(nonManoFields.get(PM_DICTIONARY).get(sourceElementTag).get(0)); + } + + private void validateYamlFile(String rootPath, String artifactPath) { + try { + List<YamlDocumentValidationError> validationErrors = + new YamlFileValidator().validateYamlFileWithSchema(rootPath+artifactPath); + addAllErrorsReportedByVaidator(artifactPath, validationErrors); + } catch (YamlProcessingException | YAMLException e) { + LOGGER.error("Failed to load PM_Dictionary file.", e); + addPmDictionaryLoadingError(artifactPath, e); + } + + } + + private void addPmDictionaryLoadingError(String artifactPath, Exception e) { + addPmDictionaryLoadingError(artifactPath,e.getMessage()); + } + + private void addPmDictionaryLoadingError(String artifactPath, String message) { + errors.add(new CSARPmDictionaryLoadingError( + artifactPath, + message + )); + } + + private void addAllErrorsReportedByVaidator(String artifactPath, List<YamlDocumentValidationError> validationErrors) { + for(YamlDocumentValidationError validationError: validationErrors) { + addPmDictionaryValidationError(artifactPath, validationError); + } + } + + private void addPmDictionaryValidationError(String artifactPath, YamlDocumentValidationError validationError) { + errors.add(new CSARPmDictionaryValidationError( + validationError.getYamlDocumentNumber(), + artifactPath, + validationError.getPath(), + validationError.getMessage() + )); + } + + @Override + protected String getVnfReqsNo() { + return "R816745"; + } + +} diff --git a/csarvalidation/src/main/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR972082.java b/csarvalidation/src/main/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR972082.java index 1061480..27e3ce9 100644 --- a/csarvalidation/src/main/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR972082.java +++ b/csarvalidation/src/main/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR972082.java @@ -23,13 +23,14 @@ import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; import java.util.ArrayList; -import java.util.Arrays; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; +import java.util.stream.Collectors; + import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.EqualsAndHashCode; @@ -113,15 +114,24 @@ public class VTPValidateCSARR972082 extends VTPValidateCSARBase { } } + private static class InvalidFileExtensionError extends PnfCSARError { + + private InvalidFileExtensionError(final String fileName) { + super(ERROR_CODE, + String.format("Invalid. File extension %s is invalid", fileName), + UNKNOWN_LINE_NUMBER, + fileName); + } + } + private static class ValidateNonManoSection { + private static final String ATTRIBUTE_NAME = "onap_pnf_sw_information"; + private final CSARArchive csar; private final String fileName; private final Map<String, Map<String, List<String>>> nonMano; private final List<CSARError> errors = new ArrayList<>(); - private final List<String> attributeNames = Arrays.asList( - "onap_pnf_sw_information" - ); private ValidateNonManoSection(final CSARArchive csar, final String fileName, final Map<String, Map<String, List<String>>> nonMano) { @@ -141,28 +151,20 @@ public class VTPValidateCSARR972082 extends VTPValidateCSARBase { } private List<CSARError> validate() { - if (nonMano.keySet().stream().filter(Objects::nonNull).count() > 0) { - nonMano.keySet().stream().filter(Objects::nonNull).forEach(this::validateAttribute); + List<String> attributesNotNull = nonMano.keySet().stream() + .filter(Objects::nonNull) + .collect(Collectors.toList()); + if (!attributesNotNull.isEmpty()) { + attributesNotNull.forEach(this::validateAttribute); } else { - errors.add(new PnfCSARErrorEntryMissing( - attributeNames.toString(), - fileName, - UNKNOWN_LINE_NUMBER) - ); + errors.add(new PnfCSARErrorEntryMissing(ATTRIBUTE_NAME, fileName, UNKNOWN_LINE_NUMBER)); } return errors; } private void validateAttribute(final String nonManoAttributes) { - - if (!attributeNames.contains(nonManoAttributes)) { - errors.add(new PnfCSARErrorEntryMissing( - nonManoAttributes, - fileName, - UNKNOWN_LINE_NUMBER) - ); - } else { + if (ATTRIBUTE_NAME.equals(nonManoAttributes)) { validateSourceElementsUnderAttribute(nonManoAttributes); } } @@ -194,6 +196,9 @@ public class VTPValidateCSARR972082 extends VTPValidateCSARBase { if (StringUtils.isEmpty(swInformationFilePath)) { errors.add(new MissingSourceElementUnderAttributeError("", swInformationFilePath)); return; + } else if (!swInformationFilePath.matches(".*\\.yaml$")) { + errors.add(new InvalidFileExtensionError(swInformationFilePath)); + return; } final Optional<PnfSoftwareInformation> parsedYaml = parse(swInformationFilePath); if (!parsedYaml.isPresent()) { diff --git a/csarvalidation/src/main/java/org/onap/cvc/csar/parser/ManifestLine.java b/csarvalidation/src/main/java/org/onap/cvc/csar/parser/ManifestLine.java index 390c534..eefc771 100644 --- a/csarvalidation/src/main/java/org/onap/cvc/csar/parser/ManifestLine.java +++ b/csarvalidation/src/main/java/org/onap/cvc/csar/parser/ManifestLine.java @@ -54,4 +54,8 @@ public class ManifestLine { return line.trim().isEmpty(); } + boolean contains(String word) { + return line.contains(word); + } + } diff --git a/csarvalidation/src/main/java/org/onap/cvc/csar/parser/MetadataParser.java b/csarvalidation/src/main/java/org/onap/cvc/csar/parser/MetadataParser.java index b0c06ee..f529c45 100644 --- a/csarvalidation/src/main/java/org/onap/cvc/csar/parser/MetadataParser.java +++ b/csarvalidation/src/main/java/org/onap/cvc/csar/parser/MetadataParser.java @@ -85,7 +85,7 @@ public class MetadataParser { private boolean isNewSection(Pair<String, String> data) { String key = data.getKey().trim(); String value = data.getValue().trim(); - return key.matches("[a-zA-z_0-9]+") && (value.isEmpty() || ManifestLine.of(value).startsWith("#")); + return key.matches("[a-zA-Z_0-9]+") && (value.isEmpty() || ManifestLine.of(value).startsWith("#")); } private boolean isSourceSection(Pair<String, String> data) { diff --git a/csarvalidation/src/main/java/org/onap/cvc/csar/parser/NonManoArtifactsParser.java b/csarvalidation/src/main/java/org/onap/cvc/csar/parser/NonManoArtifactsParser.java index d27ef68..1aa7d32 100644 --- a/csarvalidation/src/main/java/org/onap/cvc/csar/parser/NonManoArtifactsParser.java +++ b/csarvalidation/src/main/java/org/onap/cvc/csar/parser/NonManoArtifactsParser.java @@ -26,10 +26,12 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import static org.onap.cvc.csar.parser.ManifestConsts.BEGIN_CMS_SECTION; import static org.onap.cvc.csar.parser.ManifestConsts.NON_MANO_ARTIFACT_SETS_TAG_SECTION; public class NonManoArtifactsParser { + public Optional<Pair<Map<String, Map<String, List<String>>>, List<CSARArchive.CSARError>>> parse(List<String> lines) { Map<String, Map<String, List<String>>> nonManoArtifacts = new HashMap<>(); List<CSARArchive.CSARError> errors = new ArrayList<>(); @@ -41,16 +43,17 @@ public class NonManoArtifactsParser { ManifestLine manifestLine = ManifestLine.of(line); if (manifestLine.startsWith(NON_MANO_ARTIFACT_SETS_TAG_SECTION)) { isNonManoArtifactsSectionAvailable = true; + } else if (manifestLine.contains(BEGIN_CMS_SECTION)) { + break; } else if (isNonManoArtifactsSectionAvailable) { Pair<String, String> data = manifestLine.parse(); if (isNewSection(data)) { attributeName = data.getKey(); nonManoArtifacts.put(attributeName, new HashMap<>()); - continue; - } - + } else { handleNonManoArtifactLine(nonManoArtifacts, attributeName, data); + } } } @@ -64,7 +67,7 @@ public class NonManoArtifactsParser { private boolean isNewSection(Pair<String, String> data) { String key = data.getKey().trim(); String value = data.getValue().trim(); - return key.matches("[a-zA-z_0-9]+") && (value.isEmpty() || ManifestLine.of(value).startsWith("#")); + return key.matches("[a-zA-Z_0-9]+") && (value.isEmpty() || ManifestLine.of(value).startsWith("#")); } private void handleNonManoArtifactLine( diff --git a/csarvalidation/src/main/java/org/onap/cvc/csar/security/CertificateLoadingException.java b/csarvalidation/src/main/java/org/onap/cvc/csar/security/CertificateLoadingException.java new file mode 100644 index 0000000..2be5be2 --- /dev/null +++ b/csarvalidation/src/main/java/org/onap/cvc/csar/security/CertificateLoadingException.java @@ -0,0 +1,25 @@ +/* + * Copyright 2020 Nokia + * <p> + * 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 + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * 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.cvc.csar.security; + +public class CertificateLoadingException extends RuntimeException { + + public CertificateLoadingException(String s, Throwable t) { + super(s, t); + } +} diff --git a/csarvalidation/src/main/java/org/onap/cvc/csar/security/CmsSignatureData.java b/csarvalidation/src/main/java/org/onap/cvc/csar/security/CmsSignatureData.java new file mode 100644 index 0000000..456f365 --- /dev/null +++ b/csarvalidation/src/main/java/org/onap/cvc/csar/security/CmsSignatureData.java @@ -0,0 +1,75 @@ +/* + * Copyright 2020 Nokia + * <p> + * 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 + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * 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.cvc.csar.security; + +import org.bouncycastle.cms.SignerInformation; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.util.Optional; + +public class CmsSignatureData { + + private X509Certificate certificate; + private final SignerInformation signerInformation; + + public CmsSignatureData(X509Certificate certificate, SignerInformation signerInformation) { + this.certificate = certificate; + this.signerInformation = signerInformation; + } + + public CmsSignatureData(SignerInformation signerInformation) { + this.signerInformation = signerInformation; + } + + public Optional<X509Certificate> getCertificate() { + return Optional.ofNullable(certificate); + } + + public SignerInformation getSignerInformation() { + return signerInformation; + } + + public void loadCertificate(Path pathToCertificate) throws CertificateLoadingException { + try { + loadCertificate(Files.readAllBytes(pathToCertificate)); + } catch (IOException e) { + final String errorMessage = String.format( + "Error during loading Certificate from given path: %s !" + ,pathToCertificate + ); + throw new CertificateLoadingException(errorMessage, e); + } + } + + public void loadCertificate(final byte[] certificate) throws CertificateLoadingException { + try (InputStream in = new ByteArrayInputStream(certificate)) { + CertificateFactory factory = CertificateFactory.getInstance("X.509"); + this.certificate = (X509Certificate) factory.generateCertificate(in); + } catch (IOException | CertificateException e) { + throw new CertificateLoadingException("Error during loading Certificate from bytes!", e); + } + } + +} diff --git a/csarvalidation/src/main/java/org/onap/cvc/csar/security/CmsSignatureDataFactory.java b/csarvalidation/src/main/java/org/onap/cvc/csar/security/CmsSignatureDataFactory.java new file mode 100644 index 0000000..2744bc6 --- /dev/null +++ b/csarvalidation/src/main/java/org/onap/cvc/csar/security/CmsSignatureDataFactory.java @@ -0,0 +1,91 @@ +/* + * Copyright 2020 Nokia + * <p> + * 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 + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * 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.cvc.csar.security; + +import org.bouncycastle.asn1.cms.ContentInfo; +import org.bouncycastle.cert.X509CertificateHolder; +import org.bouncycastle.cms.CMSException; +import org.bouncycastle.cms.CMSProcessableByteArray; +import org.bouncycastle.cms.CMSSignedData; +import org.bouncycastle.cms.CMSTypedData; +import org.bouncycastle.cms.SignerInformation; +import org.bouncycastle.openssl.PEMParser; +import org.bouncycastle.util.Store; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.charset.Charset; +import java.util.Collection; +import java.util.Optional; + +public class CmsSignatureDataFactory { + + public CmsSignatureData createForFirstSigner(final byte[] cmsSignature, final byte[] fileContent) + throws CmsSignatureLoadingException{ + + try (ByteArrayInputStream cmsSignatureStream = new ByteArrayInputStream(cmsSignature)) { + CMSSignedData signedData = getCMSSignedData(fileContent, cmsSignatureStream); + Collection<SignerInformation> signers = signedData.getSignerInfos().getSigners(); + Store<X509CertificateHolder> certificates = signedData.getCertificates(); + SignerInformation firstSigner = getFirstSigner(signers); + CmsSignatureData signatureData = new CmsSignatureData(firstSigner); + getFirstSignerCertificate(certificates, firstSigner).ifPresent( + signatureData::loadCertificate + ); + return signatureData; + } catch (CertificateLoadingException | IOException | CMSException e) { + throw new CmsSignatureLoadingException("Unexpected error occurred during signature validation!", e); + } + } + + private SignerInformation getFirstSigner(Collection<SignerInformation> signers) { + return signers.iterator().next(); + } + + private Optional<byte[]> getFirstSignerCertificate( + Store<X509CertificateHolder> certificates, + SignerInformation firstSigner) + throws IOException { + Collection<X509CertificateHolder> firstSignerCertificates = certificates.getMatches(firstSigner.getSID()); + Optional<byte[]> cert; + if (!firstSignerCertificates.isEmpty()) { + X509CertificateHolder firstSignerFirstCertificate = firstSignerCertificates.iterator().next(); + cert = Optional.of(firstSignerFirstCertificate.getEncoded()); + } else { + cert = Optional.empty(); + } + return cert; + } + + + private CMSSignedData getCMSSignedData(byte[] innerPackageFileCSAR, ByteArrayInputStream signatureStream) throws IOException, CmsSignatureLoadingException, CMSException { + ContentInfo signature = produceSignature(signatureStream); + CMSTypedData signedContent = new CMSProcessableByteArray(innerPackageFileCSAR); + return new CMSSignedData(signedContent, signature); + } + + private ContentInfo produceSignature(ByteArrayInputStream signatureStream) throws IOException, CmsSignatureLoadingException { + Object parsedObject = new PEMParser(new InputStreamReader(signatureStream, Charset.defaultCharset())).readObject(); + if (!(parsedObject instanceof ContentInfo)) { + throw new CmsSignatureLoadingException("Signature is not recognized!"); + } + return ContentInfo.getInstance(parsedObject); + } + +} diff --git a/csarvalidation/src/main/java/org/onap/cvc/csar/security/CmsSignatureLoadingException.java b/csarvalidation/src/main/java/org/onap/cvc/csar/security/CmsSignatureLoadingException.java new file mode 100644 index 0000000..0e203b2 --- /dev/null +++ b/csarvalidation/src/main/java/org/onap/cvc/csar/security/CmsSignatureLoadingException.java @@ -0,0 +1,29 @@ +/* + * Copyright 2020 Nokia + * <p> + * 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 + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * 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.cvc.csar.security; + +public class CmsSignatureLoadingException extends Exception { + + public CmsSignatureLoadingException(String s) { + super(s); + } + + public CmsSignatureLoadingException(String s, Throwable t) { + super(s, t); + } +} diff --git a/csarvalidation/src/main/java/org/onap/cvc/csar/security/CmsSignatureValidator.java b/csarvalidation/src/main/java/org/onap/cvc/csar/security/CmsSignatureValidator.java index b8b3714..5d7b879 100644 --- a/csarvalidation/src/main/java/org/onap/cvc/csar/security/CmsSignatureValidator.java +++ b/csarvalidation/src/main/java/org/onap/cvc/csar/security/CmsSignatureValidator.java @@ -17,30 +17,14 @@ package org.onap.cvc.csar.security; -import org.bouncycastle.asn1.cms.ContentInfo; -import org.bouncycastle.cert.X509CertificateHolder; import org.bouncycastle.cms.CMSException; -import org.bouncycastle.cms.CMSProcessableByteArray; -import org.bouncycastle.cms.CMSSignedData; import org.bouncycastle.cms.CMSSignerDigestMismatchException; -import org.bouncycastle.cms.CMSTypedData; -import org.bouncycastle.cms.SignerInformation; import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder; -import org.bouncycastle.openssl.PEMParser; import org.bouncycastle.operator.OperatorCreationException; -import org.bouncycastle.util.Store; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.nio.charset.Charset; -import java.security.cert.CertificateException; -import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; -import java.util.Collection; import java.util.Optional; public class CmsSignatureValidator { @@ -52,63 +36,30 @@ public class CmsSignatureValidator { final Optional<byte[]> certificate, final byte[] fileContent) throws CmsSignatureValidatorException { - try (ByteArrayInputStream cmsSignatureStream = new ByteArrayInputStream(cmsSignature)) { - CMSSignedData signedData = getCMSSignedData(fileContent, cmsSignatureStream); - Collection<SignerInformation> signers = signedData.getSignerInfos().getSigners(); - SignerInformation firstSigner = signers.iterator().next(); - - Store certificates = signedData.getCertificates(); - X509Certificate cert; - if (!certificate.isPresent()) { - X509CertificateHolder firstSignerFirstCertificate = getX509CertificateHolder(firstSigner, certificates); - cert = loadCertificate(firstSignerFirstCertificate.getEncoded()); - } else { - cert = loadCertificate(certificate.get()); + try { + CmsSignatureData signatureData = new CmsSignatureDataFactory().createForFirstSigner(cmsSignature, fileContent); + if( signatureData.getCertificate().isEmpty() ) { + signatureData.loadCertificate(certificate.orElseThrow(() -> new CmsSignatureValidatorException("No certificate found in cms signature and ETSI-Entry-Certificate doesn't exist"))); } + return verifySignedData(signatureData); + } catch ( CmsSignatureLoadingException e) { + throw new CmsSignatureValidatorException("Unexpected error occurred during signature validation!", e); + } + } - return firstSigner.verify(new JcaSimpleSignerInfoVerifierBuilder().build(cert)); + public boolean verifySignedData(final CmsSignatureData signatureData) throws CmsSignatureValidatorException { + try { + X509Certificate certificate = signatureData.getCertificate().orElseThrow(() -> new CMSException("No certificate found in signature data!")); + return signatureData.getSignerInformation().verify(new JcaSimpleSignerInfoVerifierBuilder().build(certificate)); } catch (CMSSignerDigestMismatchException e){ //message-digest attribute value does not match calculated value LOG.warn("CMS signer digest mismatch.", e); return false; } - catch (OperatorCreationException | IOException | CMSException e) { + catch (OperatorCreationException | CMSException e) { throw new CmsSignatureValidatorException("Unexpected error occurred during signature validation!", e); } } - private X509CertificateHolder getX509CertificateHolder(SignerInformation firstSigner, Store certificates) throws CmsSignatureValidatorException { - Collection<X509CertificateHolder> firstSignerCertificates = certificates.getMatches(firstSigner.getSID()); - if(!firstSignerCertificates.iterator().hasNext()){ - throw new CmsSignatureValidatorException("No certificate found in cms signature that should contain one!"); - } - return firstSignerCertificates.iterator().next(); - } - - private CMSSignedData getCMSSignedData(byte[] innerPackageFileCSAR, ByteArrayInputStream signatureStream) throws IOException, CmsSignatureValidatorException, CMSException { - ContentInfo signature = produceSignature(signatureStream); - CMSTypedData signedContent = new CMSProcessableByteArray(innerPackageFileCSAR); - return new CMSSignedData(signedContent, signature); - } - - private ContentInfo produceSignature(ByteArrayInputStream signatureStream) throws IOException, CmsSignatureValidatorException { - Object parsedObject = new PEMParser(new InputStreamReader(signatureStream, Charset.defaultCharset())).readObject(); - if (!(parsedObject instanceof ContentInfo)) { - throw new CmsSignatureValidatorException("Signature is not recognized!"); - } - return ContentInfo.getInstance(parsedObject); - } - - - private X509Certificate loadCertificate(byte[] certFile) throws CmsSignatureValidatorException { - try (InputStream in = new ByteArrayInputStream(certFile)) { - CertificateFactory factory = CertificateFactory.getInstance("X.509"); - return (X509Certificate) factory.generateCertificate(in); - } catch (CertificateException | IOException e) { - throw new CmsSignatureValidatorException("Error during loading Certificate from bytes!", e); - } - } - - } diff --git a/csarvalidation/src/main/java/org/onap/cvc/csar/security/CmsSignatureValidatorException.java b/csarvalidation/src/main/java/org/onap/cvc/csar/security/CmsSignatureValidatorException.java index 75cd8de..1f708fd 100644 --- a/csarvalidation/src/main/java/org/onap/cvc/csar/security/CmsSignatureValidatorException.java +++ b/csarvalidation/src/main/java/org/onap/cvc/csar/security/CmsSignatureValidatorException.java @@ -25,4 +25,5 @@ public class CmsSignatureValidatorException extends Exception { public CmsSignatureValidatorException(String s, Throwable t) { super(s, t); } + } diff --git a/csarvalidation/src/main/java/org/onap/validation/csar/CsarUtil.java b/csarvalidation/src/main/java/org/onap/validation/csar/CsarUtil.java index c498479..7d48b06 100644 --- a/csarvalidation/src/main/java/org/onap/validation/csar/CsarUtil.java +++ b/csarvalidation/src/main/java/org/onap/validation/csar/CsarUtil.java @@ -28,133 +28,135 @@ import java.io.InputStream; import java.io.OutputStream; import java.util.HashMap; import java.util.Enumeration; - import java.util.zip.ZipEntry; import java.util.zip.ZipFile; - - -public class CsarUtil { - - private static final Logger logger = LoggerFactory.getLogger(CsarUtil.class); - - public static String getUnzipDir(String dirName) { - File tmpDir = new File(File.separator + dirName); - return tmpDir.getAbsolutePath().replace(".csar", ""); - } - - /** - * unzip zip file. - * - * @param zipFileName - * file name to zip - * @param extPlace - * extPlace - * @return unzip file names in zip - * @throws IOException - * e1 - * @throws ValidationException - */ - public static HashMap<String, String> unzip(String zipFileName, String extPlace) throws IOException { - HashMap<String, String> unzipFileNames = new HashMap<>(); - - try(ZipFile zipFile = new ZipFile(zipFileName)) { - - Enumeration<?> fileEn = zipFile.entries(); - byte[] buffer = new byte[CommonConstants.BUFFER_SIZE]; - - while (fileEn.hasMoreElements()) { - InputStream input = null; - BufferedOutputStream bos = null; - try { - ZipEntry entry = (ZipEntry) fileEn.nextElement(); - if (entry.isDirectory()) { - continue; - } - - input = zipFile.getInputStream(entry); - File file = new File(extPlace, entry.getName()); - - //Currently it does not support xml based VNF descriptors. - //So skip and proceed to YAML defined files validation only. - if (file.getAbsolutePath().contains("xml"+System.getProperty("file.separator"))) { - continue; - } - - if (!file.getParentFile().exists()) { - FileUtil.createDirectory(file.getParentFile().getAbsolutePath()); - } - - bos = new BufferedOutputStream(new FileOutputStream(file)); - while (true) { - int length = input.read(buffer); - if (length == -1) { - break; - } - bos.write(buffer, 0, length); - } - - unzipFileNames.put(file.getName(), file.getAbsolutePath()); - - } finally { - closeOutputStream(bos); - closeInputStream(input); - } - } - } - return unzipFileNames; - } - - /** - * close InputStream. - * - * @param inputStream - * the inputstream to close - * @throws ValidationException - */ - public static void closeInputStream(InputStream inputStream) { - try { - if (inputStream != null) { - inputStream.close(); - } - } catch (Exception e1) { - logger.error("FILE_IO" + ":" + "close InputStream error! " +ErrorCodes.FILE_IO+" "+ e1.getMessage(), e1); - throw new ValidationException(ErrorCodes.FILE_IO); - } - } - - /** - * close OutputStream. - * - * @param outputStream - * the output stream to close - * @throws ValidationException - */ - public static void closeOutputStream(OutputStream outputStream) { - try { - if (outputStream != null) { - outputStream.close(); - } - } catch (Exception e1) { - logger.error("FILE_IO" + ":" + "close OutputStream error! " +ErrorCodes.FILE_IO, e1); - throw new ValidationException(ErrorCodes.FILE_IO); - - } - } - /** - * - * @param filePath - * @return HashMap<String, String> - */ - public static HashMap<String, String> csarExtract(String filePath) { - - try { - String tempfolder = CsarUtil.getUnzipDir(filePath); - return CsarUtil.unzip(filePath, tempfolder); - - } catch (IOException e1) { - logger.error("CSAR_EXTRACTION" + ":" + "CSAR extraction error ! " +ErrorCodes.FILE_IO+" "+ e1.getMessage(), e1); - throw new ValidationException(ErrorCodes.FILE_IO); - } - } +import java.util.Map; + +public final class CsarUtil { + + private static final Logger logger = LoggerFactory.getLogger(CsarUtil.class); + + private CsarUtil(){ + //It is made private in order to resolve: Utility classes should not have public constructors + } + public static String getUnzipDir(String dirName) { + File tmpDir = new File(File.separator + dirName); + return tmpDir.getAbsolutePath().replace(".csar", ""); + } + + /** + * unzip zip file. + * + * @param zipFileName + * file name to zip + * @param extPlace + * extPlace + * @return unzip file names in zip + * @throws IOException + * e1 + * @throws ValidationException + */ + public static Map<String, String> unzip(String zipFileName, String extPlace) throws IOException { + HashMap<String, String> unzipFileNames = new HashMap<>(); + InputStream input = null; + try(ZipFile zipFile = new ZipFile(zipFileName)) { + + Enumeration<?> fileEn = zipFile.entries(); + byte[] buffer = new byte[CommonConstants.BUFFER_SIZE]; + + while (fileEn.hasMoreElements()) { + ZipEntry entry = (ZipEntry) fileEn.nextElement(); + if (entry.isDirectory()) { + continue; + } + + input = zipFile.getInputStream(entry); + File file = new File(extPlace, entry.getName()); + + //Currently it does not support xml based VNF descriptors. + //So skip and proceed to YAML defined files validation only. + if (file.getAbsolutePath().contains("xml"+System.getProperty("file.separator"))) { + continue; + } + + updateUnzipFileNames(input, buffer, unzipFileNames, file); + } + } finally { + closeInputStream(input); + } + return unzipFileNames; + } + + private static void updateUnzipFileNames(InputStream input, byte[] buffer, HashMap<String, String> unzipFileNames, File file) throws IOException { + if (!file.getParentFile().exists()) { + FileUtil.createDirectory(file.getParentFile().getAbsolutePath()); + } + try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file))){ + while (true) { + int length = input.read(buffer); + if (length == -1) { + break; + } + bos.write(buffer, 0, length); + } + + unzipFileNames.put(file.getName(), file.getAbsolutePath()); + } + } + + /** + * close InputStream. + * + * @param inputStream + * the inputstream to close + * @throws ValidationException + */ + public static void closeInputStream(InputStream inputStream) { + try { + if (inputStream != null) { + inputStream.close(); + } + } catch (Exception e1) { + String errCodeMessage = ErrorCodes.FILE_IO+" "+ e1.getMessage(); + logger.error("FILE_IO:close InputStream error! {} {}", errCodeMessage, e1); + throw new ValidationException(ErrorCodes.FILE_IO); + } + } + + /** + * close OutputStream. + * + * @param outputStream + * the output stream to close + * @throws ValidationException + */ + public static void closeOutputStream(OutputStream outputStream) { + try { + if (outputStream != null) { + outputStream.close(); + } + } catch (Exception e1) { + logger.error("FILE_IO:close OutputStream error! {} {}", ErrorCodes.FILE_IO, e1); + throw new ValidationException(ErrorCodes.FILE_IO); + + } + } + /** + * + * @param filePath + * @return HashMap<String, String> + */ + public static Map<String, String> csarExtract(String filePath) { + + try { + String tempfolder = CsarUtil.getUnzipDir(filePath); + return CsarUtil.unzip(filePath, tempfolder); + + } catch (IOException e1) { + String errCodeMessage = ErrorCodes.FILE_IO+" "+ e1.getMessage(); + logger.error("CSAR_EXTRACTION:CSAR extraction error ! {} {}", errCodeMessage, e1); + throw new ValidationException(ErrorCodes.FILE_IO); + } + } } diff --git a/csarvalidation/src/main/java/org/onap/validation/csar/CsarValidator.java b/csarvalidation/src/main/java/org/onap/validation/csar/CsarValidator.java index e92f2b2..bc18f8e 100644 --- a/csarvalidation/src/main/java/org/onap/validation/csar/CsarValidator.java +++ b/csarvalidation/src/main/java/org/onap/validation/csar/CsarValidator.java @@ -49,14 +49,14 @@ public class CsarValidator { private static ValidatorSchemaLoader vsl; // Map of CSAR file and un-zipped file indices - private static HashMap<String, String> csarFiles; + private static Map<String, String> csarFiles; // Map of packageId and CSAR files - private static HashMap<String, HashMap<String, String>> csar = new HashMap<>(); + private static Map<String, Map<String, String>> csar = new HashMap<>(); - private static String MAINSERV_TEMPLATE = CommonConstants.MAINSERV_TEMPLATE; + private static String mainServiceTemplate = CommonConstants.MAINSERV_TEMPLATE; - private static String MAINSERV_MANIFEST; + private static String mainServiceManifest; /** * @param packageId @@ -65,10 +65,8 @@ public class CsarValidator { */ public CsarValidator(String packageId, String csarWithPath) throws IOException { - try (FileInputStream is = new FileInputStream(csarWithPath)) { - - } catch(FileNotFoundException e2) { - LOG.error(csarWithPath + ":CSAR is not found! " + ErrorCodes.RESOURCE_MISSING, e2); + if (!isCsarExist(csarWithPath)) { + LOG.error(csarWithPath + ":CSAR is not found! " + ErrorCodes.RESOURCE_MISSING); throw new ValidationException(ErrorCodes.RESOURCE_MISSING, "RESOURCE MISSING" + csarWithPath + ":CSAR is not found!"); } @@ -80,8 +78,8 @@ public class CsarValidator { LOG.debug("CSAR extracted sucessfully."); } } catch(Exception e1) { - LOG.error("INVALID_CSAR_CONTENT" + ":" + csarWithPath + ": CSAR is not a valid CSAR/ZIP file! " - + ErrorCodes.INVALID_CSAR_CONTENT, e1); + LOG.error("INVALID_CSAR_CONTENT:{}: CSAR is not a valid CSAR/ZIP file! {} {}", + csarWithPath, ErrorCodes.INVALID_CSAR_CONTENT, e1); throw new ValidationException(ErrorCodes.INVALID_CSAR_CONTENT, "INVALID_CSAR_CONTENT" + ":" + csarWithPath + ": CSAR is not a valid CSAR/ZIP file! "); } @@ -89,40 +87,50 @@ public class CsarValidator { try { vsl = new ValidatorSchemaLoader(); } catch(Exception e) { - LOG.error( - "SCHEMA_LOAD_ERROR" + ":" + "CSAR schema is not loaded correctly! " + ErrorCodes.SCHEMA_LOAD_ERROR, - e); + LOG.error("SCHEMA_LOAD_ERROR:CSAR schema is not loaded correctly! {} {}", ErrorCodes.SCHEMA_LOAD_ERROR, e); throw new ValidationException(ErrorCodes.SCHEMA_LOAD_ERROR, "SCHEMA_LOAD_ERROR" + ":" + "CSAR schema is not loaded correctly! "); } } + static class CsarValidatorSeam { + public String validateCsarMeta() { + return CsarValidator.validateCsarMeta(); + } + + public String validateAndScanToscaMeta(){ + return CsarValidator.validateAndScanToscaMeta(); + } + + public String validateMainService() { + return CsarValidator.validateMainService(); + } + + } + /** * @return true if all validations are successful */ public static String validateCsar() { + return CsarValidator.validateCsarContent(new CsarValidatorSeam()); + } - String vsm = validateCsarMeta(); + static String validateCsarContent(CsarValidatorSeam csarValidatorSeam) { - String vtm = validateAndScanToscaMeta(); + String vsm = csarValidatorSeam.validateCsarMeta(); - String vms = validateMainService(); + String vtm = csarValidatorSeam.validateAndScanToscaMeta(); - //String r02454 = r02454(); + String vms = csarValidatorSeam.validateMainService(); - if((CommonConstants.SUCCESS_STR != vsm) && (CommonConstants.SUCCESS_STR != vms)) { + if((!CommonConstants.SUCCESS_STR.equals(vsm)) && (!CommonConstants.SUCCESS_STR.equals(vms))) { return vsm + " OR " + vms; } - if(CommonConstants.SUCCESS_STR != vtm) { + if(!CommonConstants.SUCCESS_STR.equals(vtm)) { return vtm; } -/* - if (CommonConstants.SUCCESS_STR != r02454) { - return r02454; - } -*/ return CommonConstants.SUCCESS_STR; } @@ -134,23 +142,32 @@ public class CsarValidator { try { RandomAccessFile raf = new RandomAccessFile(csarWithPath, "r"); - try { - long n = raf.readInt(); + return checkCsarIntegrity(raf); + } catch(IOException e1) { + LOG.error("CSAR %s is not a valid CSAR/ZIP file! ", e1); + return false; + } + } - // Check for the CSAR's integrity - if(n != 0x504B0304) { - LOG.error("CSAR %s contents are not a valid! "); - return false; - } - } catch(FileNotFoundException e1) { - LOG.error("CSAR %s is not a valid CSAR/ZIP file! ", e1); + /** + * @param raf + * @throws IOException + */ + private static boolean checkCsarIntegrity(RandomAccessFile raf) throws IOException { + + try { + long n = raf.readInt(); + + // Check for the CSAR's integrity + if (n != 0x504B0304) { + LOG.error("CSAR %s contents are not a valid! "); return false; - } finally { - raf.close(); } - } catch(IOException e1) { + } catch (FileNotFoundException e1) { LOG.error("CSAR %s is not a valid CSAR/ZIP file! ", e1); return false; + } finally { + raf.close(); } return true; } @@ -192,7 +209,6 @@ public class CsarValidator { } } } - reader.close(); return CommonConstants.SUCCESS_STR; } } catch(IOException e2) { @@ -220,14 +236,14 @@ public class CsarValidator { } try { - MAINSERV_MANIFEST = checkAndGetMRF(cfile, "Entry-Manifest"); - if(MAINSERV_MANIFEST == null) { - MAINSERV_MANIFEST = CommonConstants.MAINSERV_MANIFEST; + mainServiceManifest = checkAndGetMRF(cfile, "Entry-Manifest"); + if(mainServiceManifest == null) { + mainServiceManifest = CommonConstants.MAINSERV_MANIFEST; } - MAINSERV_TEMPLATE = checkAndGetMRF(cfile, "Entry-Definitions"); - if(MAINSERV_TEMPLATE == null) { - MAINSERV_TEMPLATE = CommonConstants.MAINSERV_TEMPLATE; + mainServiceTemplate = checkAndGetMRF(cfile, "Entry-Definitions"); + if(mainServiceTemplate == null) { + mainServiceTemplate = CommonConstants.MAINSERV_TEMPLATE; } return CommonConstants.SUCCESS_STR; @@ -256,8 +272,7 @@ public class CsarValidator { } } } catch(IOException | NullPointerException e) { - LOG.error("CSAR_TOSCA_VALIDATION" + ":" + "Could not read file %s ! " + ErrorCodes.FILE_IO + " " - + ErrorCodes.RESOURCE_MISSING, e); + LOG.error("CSAR_TOSCA_VALIDATION:Could not read file {} {} ! {}", ErrorCodes.FILE_IO, ErrorCodes.RESOURCE_MISSING, e); throw new ValidationException(ErrorCodes.RESOURCE_MISSING); } @@ -276,9 +291,9 @@ public class CsarValidator { Arrays.asList("vnf_product_name", "vnf_provider_id", "vnf_package_version", "vnf_release_data_time"); @SuppressWarnings("unused") - boolean mfResult = checkEntryFor(CommonConstants.MAINSERV_MANIFEST, mListMetadata, key); + boolean mfResult = checkEntryFor(CommonConstants.MAINSERV_MANIFEST, mListMetadata, key); //NOSONAR - String mainServManifest = MAINSERV_MANIFEST; + String mainServManifest = mainServiceManifest; if(!Paths.get(mainServManifest).isAbsolute()) { mainServManifest = csarFiles.get(FilenameUtils.getName(mainServManifest)); } @@ -287,8 +302,8 @@ public class CsarValidator { // Do nothing for Rel-1 } - String mainservTemplate = MAINSERV_TEMPLATE; - if(!Paths.get(MAINSERV_TEMPLATE).isAbsolute()) { + String mainservTemplate = mainServiceTemplate; + if(!Paths.get(mainServiceTemplate).isAbsolute()) { mainservTemplate = csarFiles.get(FilenameUtils.getName(mainservTemplate)); } @@ -306,10 +321,10 @@ public class CsarValidator { * @return: boolean **/ public static String r02454() { - String mainservTemplate = MAINSERV_TEMPLATE; + String mainservTemplate = mainServiceTemplate; mainservTemplate = csarFiles.get(FilenameUtils.getName(mainservTemplate)); - //TODO: Fixme R3 only check the existence of swImage filed inside VNFD. + //TODO: Fixme R3 only check the existence of swImage filed inside VNFD. //NOSONAR File file = new File(mainservTemplate); try (BufferedReader reader = new BufferedReader(new FileReader(file))) { String tempString = null; @@ -345,8 +360,6 @@ public class CsarValidator { @SuppressWarnings("unchecked") private static boolean checkEntryFor(String cFile, List<String> attributes, String key) { - @SuppressWarnings("unused") - String tFileWithPath; if(!Paths.get(cFile).isAbsolute()) { cFile = csarFiles.get(FilenameUtils.getName(cFile)); @@ -358,14 +371,16 @@ public class CsarValidator { Yaml yaml = new Yaml(); Map<String, ?> values; + String exceptionMessage; try (InputStream input = new FileInputStream(new File(cFile))) { values = (Map<String, ?>)yaml.load(input); } catch(FileNotFoundException e) { - LOG.error("FILE_NOT_FOUND" + ":" + "Exception caught while trying to find the file ! " + e.getMessage(), e); + exceptionMessage = e.getMessage(); + LOG.error("FILE_NOT_FOUND:Exception caught while trying to find the file ! {} {}", exceptionMessage, e); return false; } catch(IOException e1) { - LOG.error("FILE_NOT_FOUND" + ":" + "Exception caught while trying to open the file ! " + e1.getMessage(), - e1); + exceptionMessage = e1.getMessage(); + LOG.error("FILE_NOT_FOUND:Exception caught while trying to open the file ! {} {}", exceptionMessage, e1); return false; } @@ -403,7 +418,6 @@ public class CsarValidator { if(StringUtils.isEmpty(cfile)) { return false; } else { - File file = new File(cfile); Yaml yaml = new Yaml(); @@ -411,9 +425,9 @@ public class CsarValidator { try (InputStream input = new FileInputStream(new File(cfile))) { toscaMeta = (Map<String, ?>)yaml.load(input); } catch(FileNotFoundException e) { - LOG.error("CSAR_TOSCA_LOAD" + ":" + "TOSCA metadata is not loaded by Yaml! " + ErrorCodes.FILE_IO, e); + LOG.error("CSAR_TOSCA_LOAD:TOSCA metadata is not loaded by Yaml! {} {}", ErrorCodes.FILE_IO, e); } catch(IOException e1) { - LOG.error("CSAR_TOSCA_LOAD" + ":" + "TOSCA metadata is not loaded by Yaml! " + ErrorCodes.FILE_IO, e1); + LOG.error("CSAR_TOSCA_LOAD:TOSCA metadata is not loaded by Yaml! {} {}", ErrorCodes.FILE_IO, e1); } if(toscaMeta != null) { return toscaMeta.keySet().containsAll((vsl.getToscaMeta().keySet())); @@ -422,19 +436,23 @@ public class CsarValidator { } } - public static HashMap<String, HashMap<String, String>> getCsar() { + public static Map<String, Map<String, String>> getCsar() { return csar; } - public static void setCsar(HashMap<String, HashMap<String, String>> csar) { + public static void setCsar(Map<String, Map<String, String>> csar) { CsarValidator.csar = csar; } - public static HashMap<String, String> getCsarFiles() { + public static Map<String, String> getCsarFiles() { return csarFiles; } - public static void setCsarFiles(HashMap<String, String> csarFiles) { + public static void setCsarFiles(Map<String, String> csarFiles) { CsarValidator.csarFiles = csarFiles; } + + public static boolean isCsarExist(String csarWithPath){ + return new File(csarWithPath).exists(); + } } diff --git a/csarvalidation/src/main/java/org/onap/validation/csar/FileUtil.java b/csarvalidation/src/main/java/org/onap/validation/csar/FileUtil.java index f7471bd..ccf1c71 100644 --- a/csarvalidation/src/main/java/org/onap/validation/csar/FileUtil.java +++ b/csarvalidation/src/main/java/org/onap/validation/csar/FileUtil.java @@ -23,13 +23,15 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.zip.ZipFile; - +import java.nio.file.Files; +import java.nio.file.Paths; public final class FileUtil { public static final Logger logger = LoggerFactory.getLogger(FileUtil.class); private static final int TRY_COUNT = 3; + public static final String FILE_IO_STR = "FILE_IO"; private FileUtil() { @@ -46,9 +48,7 @@ public final class FileUtil { int tryCount = 0; while (tryCount < TRY_COUNT) { tryCount++; - if (!folder.exists() && !folder.mkdirs()) { - continue; - } else { + if (folder.exists() || folder.mkdirs()) { return true; } } @@ -65,15 +65,16 @@ public final class FileUtil { String hintInfo = file.isDirectory() ? "dir " : "file "; boolean isFileDeleted = file.delete(); boolean isFileExist = file.exists(); + String fileAbsolutePath = file.getAbsolutePath(); if (!isFileExist) { if (isFileDeleted) { - logger.info("delete " + hintInfo + file.getAbsolutePath()); + logger.info("delete {}{}", hintInfo, fileAbsolutePath); } else { isFileDeleted = true; - logger.info("file not exist. no need delete " + hintInfo + file.getAbsolutePath()); + logger.info("file not exist. no need delete {}{}", hintInfo, fileAbsolutePath); } } else { - logger.info("fail to delete " + hintInfo + file.getAbsolutePath()); + logger.info("fail to delete {}{}", hintInfo, fileAbsolutePath); } return isFileDeleted; @@ -92,7 +93,8 @@ public final class FileUtil { inputStream.close(); } } catch (Exception e1) { - logger.error("FILE_IO" + ":" + "close InputStream error! "+ErrorCodes.FILE_IO+ " " + e1.getMessage(), e1); + String errCodeMessage = ErrorCodes.FILE_IO+ " " + e1.getMessage(); + logger.error("FILE_IO:close InputStream error! {}{}", errCodeMessage, e1); throw new ValidationException(ErrorCodes.FILE_IO); } } @@ -109,7 +111,8 @@ public final class FileUtil { outputStream.close(); } } catch (Exception e1) { - logger.error("FILE_IO" + ":" + "close OutputStream error! "+ErrorCodes.FILE_IO+ " " + e1.getMessage(), e1); + String errCodeMessage = ErrorCodes.FILE_IO+ " " + e1.getMessage(); + logger.error("FILE_IO:close OutputStream error! {}{}", errCodeMessage, e1); throw new ValidationException(ErrorCodes.FILE_IO); } } @@ -120,7 +123,8 @@ public final class FileUtil { ifs.close(); } } catch (Exception e1) { - logger.error("FILE_IO" + ":" + "close OutputStream error! "+ErrorCodes.FILE_IO+ " " + e1.getMessage(), e1); + String errCodeMessage = ErrorCodes.FILE_IO+ " " + e1.getMessage(); + logger.error("FILE_IO:close OutputStream error! {}{}", errCodeMessage, e1); throw new ValidationException(ErrorCodes.FILE_IO); } } @@ -137,7 +141,8 @@ public final class FileUtil { zipFile.close(); } } catch (IOException e1) { - logger.error("CLOSE_ZIPFILE" + ":" + "close ZipFile error! "+ErrorCodes.FILE_IO+ " " + e1.getMessage(), e1); + String errCodeMessage = ErrorCodes.FILE_IO+ " " + e1.getMessage(); + logger.error("CLOSE_ZIPFILE:close ZipFile error! {}{}", errCodeMessage, e1); throw new ValidationException(ErrorCodes.FILE_IO); } } @@ -172,6 +177,13 @@ public final class FileUtil { deleteDirectory(f); } } - return file.delete(); + boolean isFileDeleted=false; + try { + Files.delete(Paths.get(file.getPath())); + isFileDeleted=true; + } catch (IOException e) { + logger.error("fail to delete {} {} ", file.getAbsolutePath(), ", exception :: ", e); + } + return isFileDeleted; } } diff --git a/csarvalidation/src/main/java/org/onap/validation/csar/VTPValidateCSAR.java b/csarvalidation/src/main/java/org/onap/validation/csar/VTPValidateCSAR.java index b97b488..1624e46 100644 --- a/csarvalidation/src/main/java/org/onap/validation/csar/VTPValidateCSAR.java +++ b/csarvalidation/src/main/java/org/onap/validation/csar/VTPValidateCSAR.java @@ -30,7 +30,7 @@ import org.slf4j.LoggerFactory; */ @OnapCommandSchema(schema = "vtp-validate-csar-casablanca.yaml") public class VTPValidateCSAR extends OnapCommand { - private static final Logger LOG = LoggerFactory.getLogger(VTPValidateCSAR.class); + private static final Logger LOG = LoggerFactory.getLogger(VTPValidateCSAR.class); //NOSONAR @Override protected void run() throws OnapCommandException { diff --git a/csarvalidation/src/main/java/org/onap/validation/csar/ValidationException.java b/csarvalidation/src/main/java/org/onap/validation/csar/ValidationException.java index 7c124d7..ac4c1eb 100644 --- a/csarvalidation/src/main/java/org/onap/validation/csar/ValidationException.java +++ b/csarvalidation/src/main/java/org/onap/validation/csar/ValidationException.java @@ -36,7 +36,7 @@ public class ValidationException extends RuntimeException { return wrappedInfo(exception, null); } - public ValidationException(ErrorCodes errCode, String message) { + public ValidationException(ErrorCodes errCode, String message) { //NOSONAR super(message); } @@ -58,7 +58,8 @@ public class ValidationException extends RuntimeException { super(message, cause); this.errorCode = errorCode; } - + + @Override public String toString(){ return ("Exception Number = "+errorMessage) ; } diff --git a/csarvalidation/src/main/java/org/onap/validation/csar/ValidatorSchemaLoader.java b/csarvalidation/src/main/java/org/onap/validation/csar/ValidatorSchemaLoader.java index 0ed0bdc..b96275f 100644 --- a/csarvalidation/src/main/java/org/onap/validation/csar/ValidatorSchemaLoader.java +++ b/csarvalidation/src/main/java/org/onap/validation/csar/ValidatorSchemaLoader.java @@ -22,7 +22,6 @@ import java.io.*; import java.net.URISyntaxException; import java.util.*; import org.yaml.snakeyaml.Yaml; -import org.yaml.snakeyaml.scanner.ScannerException; public class ValidatorSchemaLoader { @@ -31,13 +30,13 @@ public class ValidatorSchemaLoader { private static final Logger LOG = LoggerFactory.getLogger(ValidatorSchemaLoader.class); // Map of Schema files - private Map<String, ?> toscaMeta; + private Map<String, Object> toscaMeta; - private Map<String, ?> csarentryd; + private Map<String, Object> csarentryd; - private Map<String, ?> mrfYaml; + private Map<String, Object> mrfYaml; - private Map<String, ?> mrfManifest; + private Map<String, Object> mrfManifest; // List of configured schemas static List<String> schemaFileList = new ArrayList<>(); @@ -47,28 +46,18 @@ public class ValidatorSchemaLoader { static HashMap<String, String> optionTwoSchema; - private String schemaFolder; - public ValidatorSchemaLoader() { - try { loadResources(); - } catch(FileNotFoundException e1) { - LOG.error("Schema file not found or schema repository corrupted", e1); - - } catch(URISyntaxException e) { - // TODO Auto-generated catch block - LOG.error("Illegal character in query at index", e); - } } - private Map<String, ?> readYaml(String fileName) { + private Map<String, Object> readYaml(String fileName) { Yaml yaml = new Yaml(); - return (Map<String, ?>)yaml.load(this.getClass().getResourceAsStream(fileName)); + return (Map<String, Object>)yaml.load(this.getClass().getResourceAsStream(fileName)); } @SuppressWarnings("unchecked") - private boolean loadResources() throws FileNotFoundException, URISyntaxException { + private boolean loadResources() { for (String metaFile: new String []{"TOSCA.meta", "CSAR.meta", "MRF.mf" }) { switch(metaFile) { case "TOSCA.meta": @@ -88,19 +77,19 @@ public class ValidatorSchemaLoader { return true; } - public Map<String, ?> getToscaMeta() { + public Map<String, Object> getToscaMeta() { return toscaMeta; } - public Map<String, ?> getCsarentryd() { + public Map<String, Object> getCsarentryd() { return csarentryd; } - public Map<String, ?> getMrfYaml() { + public Map<String, Object> getMrfYaml() { return mrfYaml; } - public Map<String, ?> getMrfManifest() { + public Map<String, Object> getMrfManifest() { return mrfManifest; } } diff --git a/csarvalidation/src/main/java/org/onap/validation/yaml/YamlFileValidator.java b/csarvalidation/src/main/java/org/onap/validation/yaml/YamlFileValidator.java new file mode 100644 index 0000000..2de4f48 --- /dev/null +++ b/csarvalidation/src/main/java/org/onap/validation/yaml/YamlFileValidator.java @@ -0,0 +1,72 @@ +/* + * Copyright 2020 Nokia + * + * 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.validation.yaml; + +import org.onap.validation.yaml.error.SchemaValidationError; +import org.onap.validation.yaml.error.YamlDocumentValidationError; +import org.onap.validation.yaml.exception.YamlProcessingException; +import org.onap.validation.yaml.model.YamlDocument; +import org.onap.validation.yaml.schema.YamlSchema; +import org.onap.validation.yaml.schema.YamlSchemaFactory; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public class YamlFileValidator { + + private static final int FIRST_DOCUMENT_INDEX = 1; + + public List<YamlDocumentValidationError> validateYamlFileWithSchema(String pathToFile) + throws YamlProcessingException { + + List<YamlDocument> documents = new YamlLoader().loadMultiDocumentYamlFile(pathToFile); + if(!documents.isEmpty()) { + return validateDocuments(documents); + } else { + throw new YamlProcessingException("PM_Dictionary YAML file is empty"); + } + } + + private List<YamlDocumentValidationError> validateDocuments(List<YamlDocument> documents) + throws YamlProcessingException { + + List<YamlDocumentValidationError> yamlFileValidationErrors = new ArrayList<>(); + YamlSchema schema = extractSchema(documents); + YamlValidator validator = new YamlValidator(schema); + + for (int index = FIRST_DOCUMENT_INDEX; index < documents.size(); index++) { + List<SchemaValidationError> validationErrors = validator.validate(documents.get(index)); + yamlFileValidationErrors.addAll(transformErrors(index,validationErrors)); + } + + return yamlFileValidationErrors; + } + + private List<YamlDocumentValidationError> transformErrors(int index, List<SchemaValidationError> validationErrors) { + return validationErrors + .stream() + .map(error->new YamlDocumentValidationError(index, error.getPath(), error.getMessage())) + .collect(Collectors.toList()); + } + + private YamlSchema extractSchema(List<YamlDocument> documents) throws YamlProcessingException { + return new YamlSchemaFactory().createTreeStructuredYamlSchema(documents.get(0)); + } + +} diff --git a/csarvalidation/src/main/java/org/onap/validation/yaml/YamlLoader.java b/csarvalidation/src/main/java/org/onap/validation/yaml/YamlLoader.java new file mode 100644 index 0000000..1a5eef9 --- /dev/null +++ b/csarvalidation/src/main/java/org/onap/validation/yaml/YamlLoader.java @@ -0,0 +1,61 @@ +/* + * Copyright 2020 Nokia + * + * 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.validation.yaml; + +import org.onap.validation.yaml.exception.YamlProcessingException; +import org.onap.validation.yaml.model.YamlDocument; +import org.onap.validation.yaml.model.YamlDocumentFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.yaml.snakeyaml.Yaml; + +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; + +class YamlLoader { + + private static final Logger LOGGER = LoggerFactory.getLogger(YamlLoader.class); + + List<YamlDocument> loadMultiDocumentYamlFile(URL path) + throws YamlDocumentFactory.YamlDocumentParsingException { + List<YamlDocument> documentsFromFile = new ArrayList<>(); + try (InputStream yamlStream = path.openStream()) { + for (Object yamlDocument : new Yaml().loadAll(yamlStream)) { + documentsFromFile.add( + new YamlDocumentFactory().createYamlDocument(yamlDocument) + ); + } + } catch (IOException e) { + LOGGER.error("Failed to load multi document YAML file",e); + } + return documentsFromFile; + } + + List<YamlDocument> loadMultiDocumentYamlFile(String path) + throws YamlProcessingException { + try { + return loadMultiDocumentYamlFile(new URL("file://" + path)); + } catch (MalformedURLException e) { + throw new YamlProcessingException("Fail to read file under given path.", e); + } + } +} diff --git a/csarvalidation/src/main/java/org/onap/validation/yaml/YamlValidator.java b/csarvalidation/src/main/java/org/onap/validation/yaml/YamlValidator.java new file mode 100644 index 0000000..9430df4 --- /dev/null +++ b/csarvalidation/src/main/java/org/onap/validation/yaml/YamlValidator.java @@ -0,0 +1,40 @@ +/* + * Copyright 2020 Nokia + * + * 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.validation.yaml; + +import org.onap.validation.yaml.exception.YamlProcessingException; +import org.onap.validation.yaml.error.SchemaValidationError; +import org.onap.validation.yaml.model.YamlDocument; +import org.onap.validation.yaml.process.YamlValidationProcess; +import org.onap.validation.yaml.schema.YamlSchema; + +import java.util.List; + +public class YamlValidator { + + private final YamlSchema schema; + + YamlValidator(YamlSchema schema) { + this.schema = schema; + } + + public List<SchemaValidationError> validate(YamlDocument document) throws YamlProcessingException { + return new YamlValidationProcess(schema,document).validate(); + } + +} diff --git a/csarvalidation/src/main/java/org/onap/validation/yaml/error/SchemaValidationError.java b/csarvalidation/src/main/java/org/onap/validation/yaml/error/SchemaValidationError.java new file mode 100644 index 0000000..6ffe6d4 --- /dev/null +++ b/csarvalidation/src/main/java/org/onap/validation/yaml/error/SchemaValidationError.java @@ -0,0 +1,36 @@ +/* + * Copyright 2020 Nokia + * + * 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.validation.yaml.error; + +public class SchemaValidationError { + private final String path; + private final String message; + + public String getPath() { + return path; + } + + public String getMessage() { + return message; + } + + public SchemaValidationError(String path, String message) { + this.path = path; + this.message = message; + } +} diff --git a/csarvalidation/src/main/java/org/onap/validation/yaml/error/YamlDocumentValidationError.java b/csarvalidation/src/main/java/org/onap/validation/yaml/error/YamlDocumentValidationError.java new file mode 100644 index 0000000..f04708f --- /dev/null +++ b/csarvalidation/src/main/java/org/onap/validation/yaml/error/YamlDocumentValidationError.java @@ -0,0 +1,42 @@ +/* + * Copyright 2020 Nokia + * + * 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.validation.yaml.error; + +public class YamlDocumentValidationError { + private final int yamlDocumentNumber; + private final String path; + private final String message; + + public YamlDocumentValidationError(int yamlDocumentNumber, String path, String message) { + this.yamlDocumentNumber = yamlDocumentNumber; + this.path = path; + this.message = message; + } + + public int getYamlDocumentNumber() { + return yamlDocumentNumber; + } + + public String getPath() { + return path; + } + + public String getMessage() { + return message; + } +} diff --git a/csarvalidation/src/main/java/org/onap/validation/yaml/exception/YamlProcessingException.java b/csarvalidation/src/main/java/org/onap/validation/yaml/exception/YamlProcessingException.java new file mode 100644 index 0000000..99c2437 --- /dev/null +++ b/csarvalidation/src/main/java/org/onap/validation/yaml/exception/YamlProcessingException.java @@ -0,0 +1,33 @@ +/* + * Copyright 2020 Nokia + * + * 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.validation.yaml.exception; + +public class YamlProcessingException extends Exception { + + public YamlProcessingException(String message, Throwable throwable) { + super(message, throwable); + } + + public YamlProcessingException(String message) { + super(message); + } + + public YamlProcessingException(Throwable throwable) { + super(throwable); + } +} diff --git a/csarvalidation/src/main/java/org/onap/validation/yaml/model/YamlDocument.java b/csarvalidation/src/main/java/org/onap/validation/yaml/model/YamlDocument.java new file mode 100644 index 0000000..557b6fd --- /dev/null +++ b/csarvalidation/src/main/java/org/onap/validation/yaml/model/YamlDocument.java @@ -0,0 +1,56 @@ +/* + * Copyright 2020 Nokia + * + * 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.validation.yaml.model; + +import java.util.Map; + +public class YamlDocument { + + private final Map<String, Object> yaml; + + YamlDocument(Map<String, Object> yaml) { + this.yaml = yaml; + } + + public Map<String, Object> getYaml() { + return yaml; + } + + public boolean containsKey(String key) { + return yaml.containsKey(key); + } + + public String getValue(String key) { + return yaml.get(key).toString(); + } + + public YamlParametersList getListOfValues(String key) { + return new YamlParameterListFactory().createYamlParameterList( + yaml.get(key) + ); + } + + public YamlDocument getSubStructure(String name) + throws YamlDocumentFactory.YamlDocumentParsingException { + return new YamlDocumentFactory().createYamlDocument( + yaml.get(name) + ); + } +} + + diff --git a/csarvalidation/src/main/java/org/onap/validation/yaml/model/YamlDocumentFactory.java b/csarvalidation/src/main/java/org/onap/validation/yaml/model/YamlDocumentFactory.java new file mode 100644 index 0000000..b56422c --- /dev/null +++ b/csarvalidation/src/main/java/org/onap/validation/yaml/model/YamlDocumentFactory.java @@ -0,0 +1,52 @@ +/* + * Copyright 2020 Nokia + * + * 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.validation.yaml.model; + +import org.onap.validation.yaml.exception.YamlProcessingException; + +import java.util.HashMap; +import java.util.Map; + +public class YamlDocumentFactory { + + public YamlDocument createYamlDocument(Object yaml) throws YamlDocumentParsingException { + try { + Map<String, Object> parsedYaml = transformMap((Map) yaml); + return new YamlDocument(parsedYaml); + } catch (ClassCastException e) { + throw new YamlDocumentParsingException( + String.format("Fail to parse given objects: %s as yaml document.", yaml), e + ); + } + } + + private Map<String, Object> transformMap(Map<Object, Object> yaml) { + Map<String, Object> parsedYaml = new HashMap<>(); + for (Map.Entry<Object, Object> entry: yaml.entrySet()) { + parsedYaml.put(entry.getKey().toString(), entry.getValue()); + } + return parsedYaml; + } + + public static class YamlDocumentParsingException extends YamlProcessingException { + YamlDocumentParsingException(String message, Throwable throwable) { + super(message, throwable); + } + } + +} diff --git a/csarvalidation/src/main/java/org/onap/validation/yaml/model/YamlParameterListFactory.java b/csarvalidation/src/main/java/org/onap/validation/yaml/model/YamlParameterListFactory.java new file mode 100644 index 0000000..5f41c5c --- /dev/null +++ b/csarvalidation/src/main/java/org/onap/validation/yaml/model/YamlParameterListFactory.java @@ -0,0 +1,42 @@ +/* + * Copyright 2020 Nokia + * + * 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.validation.yaml.model; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class YamlParameterListFactory { + + public YamlParametersList createEmptyYamlParameterList() { + return new YamlParametersList(Collections.emptyList()); + } + + public YamlParametersList createYamlParameterList(Object yaml) { + List<String> parametersList = new ArrayList<>(); + if( yaml instanceof List) { + for (Object element : (List) yaml) { + parametersList.add(element.toString()); + } + } else { + parametersList.add(yaml.toString()); + } + return new YamlParametersList(parametersList); + } + +} diff --git a/csarvalidation/src/main/java/org/onap/validation/yaml/model/YamlParametersList.java b/csarvalidation/src/main/java/org/onap/validation/yaml/model/YamlParametersList.java new file mode 100644 index 0000000..2b93c74 --- /dev/null +++ b/csarvalidation/src/main/java/org/onap/validation/yaml/model/YamlParametersList.java @@ -0,0 +1,34 @@ +/* + * Copyright 2020 Nokia + * + * 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.validation.yaml.model; + +import java.util.List; + +public class YamlParametersList { + + private final List<String> parameters; + + YamlParametersList(List<String> parameters) { + this.parameters = parameters; + } + + public List<String> getParameters() { + return parameters; + } + +} diff --git a/csarvalidation/src/main/java/org/onap/validation/yaml/process/YamlValidationProcess.java b/csarvalidation/src/main/java/org/onap/validation/yaml/process/YamlValidationProcess.java new file mode 100644 index 0000000..273014b --- /dev/null +++ b/csarvalidation/src/main/java/org/onap/validation/yaml/process/YamlValidationProcess.java @@ -0,0 +1,111 @@ +/* + * Copyright 2020 Nokia + * + * 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.validation.yaml.process; + +import org.onap.validation.yaml.exception.YamlProcessingException; +import org.onap.validation.yaml.error.SchemaValidationError; +import org.onap.validation.yaml.model.YamlDocument; +import org.onap.validation.yaml.schema.YamlSchema; +import org.onap.validation.yaml.schema.node.YamlSchemaNode; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + +public class YamlValidationProcess { + + private final Queue<YamlValidationStep> validationSteps; + private final List<SchemaValidationError> errors; + private final YamlSchema schema; + private final YamlDocument document; + + public YamlValidationProcess(YamlSchema schema, YamlDocument document) { + this.schema = schema; + this.document = document; + errors = new ArrayList<>(); + validationSteps = new LinkedList<>(); + } + + public List<SchemaValidationError> validate() throws YamlProcessingException { + validationSteps.add(new YamlValidationStep(schema.getRootNodes(), document)); + while (!validationSteps.isEmpty()) { + YamlValidationStep nextValidationNode = validationSteps.poll(); + validateStep(nextValidationNode); + } + return errors; + } + + private void validateStep(YamlValidationStep validationNode) + throws YamlProcessingException { + for (YamlSchemaNode schemaNode : validationNode.getSchemaNodes()) { + validateNode(validationNode.getDocument(), schemaNode); + } + } + + private void validateNode(YamlDocument document, YamlSchemaNode schemaNode) + throws YamlProcessingException { + + if (document.containsKey(schemaNode.getName())) { + if (schemaNode.isContainingSubStructure()) { + addNextLevelNodeToValidationNodesQueue(document, schemaNode); + } else if (!isValueOfNodeInAcceptedValuesList(document, schemaNode)) { + addIncorrectValueError(document, schemaNode); + } + } else if (schemaNode.isRequired()) { + addRequiredKeyNotFoundError(schemaNode); + } + } + + private boolean isValueOfNodeInAcceptedValuesList(YamlDocument document, YamlSchemaNode node) { + return node.getAcceptedValues().isEmpty() || + node.getAcceptedValues().containsAll( + document.getListOfValues(node.getName()).getParameters() + ); + } + + private void addNextLevelNodeToValidationNodesQueue(YamlDocument document, YamlSchemaNode node) + throws YamlProcessingException { + validationSteps.add( + new YamlValidationStep( + node.getNextNodes(), + document.getSubStructure(node.getName()) + ) + ); + } + + private void addRequiredKeyNotFoundError(YamlSchemaNode node) { + errors.add( + new SchemaValidationError( + node.getPath(), + String.format("Key not found: %s", node.getName()) + ) + ); + } + + private void addIncorrectValueError(YamlDocument document, YamlSchemaNode node) { + errors.add( + new SchemaValidationError( + node.getPath() + node.getName(), + String.format( + "Value(s) is/are not in array of accepted values.%n value(s): %s%n accepted value(s): %s", + document.getValue(node.getName()), node.getAcceptedValues()) + ) + ); + } +} diff --git a/csarvalidation/src/main/java/org/onap/validation/yaml/process/YamlValidationStep.java b/csarvalidation/src/main/java/org/onap/validation/yaml/process/YamlValidationStep.java new file mode 100644 index 0000000..eb5ab8e --- /dev/null +++ b/csarvalidation/src/main/java/org/onap/validation/yaml/process/YamlValidationStep.java @@ -0,0 +1,45 @@ +/* + * Copyright 2020 Nokia + * + * 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.validation.yaml.process; + +import org.onap.validation.yaml.model.YamlDocument; +import org.onap.validation.yaml.schema.node.YamlSchemaNode; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +class YamlValidationStep { + + private final List<YamlSchemaNode> schemaNodes; + private final YamlDocument document; + + YamlValidationStep(List<YamlSchemaNode> nodes, YamlDocument yaml) { + this.schemaNodes = new ArrayList<>(nodes); + this.document = yaml; + } + + List<YamlSchemaNode> getSchemaNodes() { + return Collections.unmodifiableList(schemaNodes); + } + + YamlDocument getDocument() { + return document; + } + +} diff --git a/csarvalidation/src/main/java/org/onap/validation/yaml/schema/YamlSchema.java b/csarvalidation/src/main/java/org/onap/validation/yaml/schema/YamlSchema.java new file mode 100644 index 0000000..69bb6cd --- /dev/null +++ b/csarvalidation/src/main/java/org/onap/validation/yaml/schema/YamlSchema.java @@ -0,0 +1,37 @@ +/* + * Copyright 2020 Nokia + * + * 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.validation.yaml.schema; + +import org.onap.validation.yaml.schema.node.YamlSchemaNode; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class YamlSchema { + + private final List<YamlSchemaNode> rootNodes; + + public List<YamlSchemaNode> getRootNodes() { + return Collections.unmodifiableList(rootNodes); + } + + YamlSchema(List<YamlSchemaNode> rootNodes) { + this.rootNodes = new ArrayList<>(rootNodes); + } +} diff --git a/csarvalidation/src/main/java/org/onap/validation/yaml/schema/YamlSchemaFactory.java b/csarvalidation/src/main/java/org/onap/validation/yaml/schema/YamlSchemaFactory.java new file mode 100644 index 0000000..df7d673 --- /dev/null +++ b/csarvalidation/src/main/java/org/onap/validation/yaml/schema/YamlSchemaFactory.java @@ -0,0 +1,59 @@ +/* + * Copyright 2020 Nokia + * + * 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.validation.yaml.schema; + +import org.onap.validation.yaml.exception.YamlProcessingException; +import org.onap.validation.yaml.model.YamlDocument; +import org.onap.validation.yaml.model.YamlDocumentFactory; +import org.onap.validation.yaml.schema.node.YamlSchemaNode; +import org.onap.validation.yaml.schema.node.YamlSchemaNodeFactory; + +import java.util.ArrayList; +import java.util.List; + +public class YamlSchemaFactory { + + + private static final String ROOT_PATH = "/"; + + public YamlSchema createTreeStructuredYamlSchema(YamlDocument schema) + throws YamlProcessingException { + + return new YamlSchema(getRootNodes(schema)); + } + + private List<YamlSchemaNode> getRootNodes(YamlDocument yamlDocument) + throws YamlProcessingException { + + List<YamlSchemaNode> nextNodes = new ArrayList<>(); + for(String nodeName: yamlDocument.getYaml().keySet()) { + nextNodes.add( + new YamlSchemaNodeFactory().createNode( + nodeName, + ROOT_PATH, + new YamlDocumentFactory().createYamlDocument( + yamlDocument.getYaml().get(nodeName) + ) + ) + ); + } + return nextNodes; + } + +} diff --git a/csarvalidation/src/main/java/org/onap/validation/yaml/schema/node/YamlSchemaBranchNode.java b/csarvalidation/src/main/java/org/onap/validation/yaml/schema/node/YamlSchemaBranchNode.java new file mode 100644 index 0000000..0f5b480 --- /dev/null +++ b/csarvalidation/src/main/java/org/onap/validation/yaml/schema/node/YamlSchemaBranchNode.java @@ -0,0 +1,80 @@ +/* + * Copyright 2020 Nokia + * + * 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.validation.yaml.schema.node; + +import org.onap.validation.yaml.exception.YamlProcessingException; +import org.onap.validation.yaml.model.YamlDocument; +import org.onap.validation.yaml.model.YamlDocumentFactory; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +public class YamlSchemaBranchNode extends YamlSchemaNode { + + private final YamlDocument nextNodesInLazyForm; + private Optional<List<YamlSchemaNode>> nextNodes; + + YamlSchemaBranchNode(String name, String path, boolean required, String comment, + YamlDocument nextNodesInLazyForm) { + super(name, path, required, comment); + this.nextNodesInLazyForm = nextNodesInLazyForm; + this.nextNodes = Optional.empty(); + } + + @Override + public boolean isContainingSubStructure() { + return true; + } + + @Override + public List<String> getAcceptedValues() { + return Collections.emptyList(); + } + + @Override + public synchronized List<YamlSchemaNode> getNextNodes() throws YamlSchemaProcessingException { + try { + return nextNodes.orElseGet(this::loadNextNodes); + } catch (YamlSchemaLazyLoadingException lazyLoadingException) { + throw new YamlSchemaProcessingException(lazyLoadingException); + } + } + + private List<YamlSchemaNode> loadNextNodes() { + try { + List<YamlSchemaNode> loadedNextNodes = new ArrayList<>(); + for (String key : nextNodesInLazyForm.getYaml().keySet()) { + YamlDocument substructure = new YamlDocumentFactory() + .createYamlDocument(nextNodesInLazyForm.getYaml().get(key)); + loadedNextNodes.add(new YamlSchemaNodeFactory().createNode(key, getPath() + getName() + "/", substructure)); + } + nextNodes = Optional.of(loadedNextNodes); + return loadedNextNodes; + } catch (YamlProcessingException e) { + throw new YamlSchemaLazyLoadingException("Lazy loading failed, due to yaml parsing exception.",e); + } + } + + static class YamlSchemaLazyLoadingException extends RuntimeException { + YamlSchemaLazyLoadingException(String message, Throwable throwable) { + super(message, throwable); + } + } +} diff --git a/csarvalidation/src/main/java/org/onap/validation/yaml/schema/node/YamlSchemaLeafNode.java b/csarvalidation/src/main/java/org/onap/validation/yaml/schema/node/YamlSchemaLeafNode.java new file mode 100644 index 0000000..c98f41e --- /dev/null +++ b/csarvalidation/src/main/java/org/onap/validation/yaml/schema/node/YamlSchemaLeafNode.java @@ -0,0 +1,50 @@ +/* + * Copyright 2020 Nokia + * + * 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.validation.yaml.schema.node; + +import org.onap.validation.yaml.model.YamlParametersList; + +import java.util.Collections; +import java.util.List; + +public class YamlSchemaLeafNode extends YamlSchemaNode { + + private final YamlParametersList acceptedValues; + + YamlSchemaLeafNode(String name, String path, boolean required, String comment, + YamlParametersList acceptedValues) { + super(name, path, required, comment); + this.acceptedValues = acceptedValues; + } + + @Override + public List<String> getAcceptedValues() { + return acceptedValues.getParameters(); + } + + @Override + public List<YamlSchemaNode> getNextNodes() { + return Collections.emptyList(); + } + + @Override + public boolean isContainingSubStructure() { + return false; + } + +} diff --git a/csarvalidation/src/main/java/org/onap/validation/yaml/schema/node/YamlSchemaNode.java b/csarvalidation/src/main/java/org/onap/validation/yaml/schema/node/YamlSchemaNode.java new file mode 100644 index 0000000..28913a2 --- /dev/null +++ b/csarvalidation/src/main/java/org/onap/validation/yaml/schema/node/YamlSchemaNode.java @@ -0,0 +1,66 @@ +/* + * Copyright 2020 Nokia + * + * 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.validation.yaml.schema.node; + +import org.onap.validation.yaml.exception.YamlProcessingException; + +import java.util.List; + +public abstract class YamlSchemaNode { + + private final String path; + private final String name; + private final boolean required; + private final String comment; + + + public String getName() { + return name; + } + + public String getPath() { + return path; + } + + public boolean isRequired() { + return required; + } + + public abstract List<String> getAcceptedValues(); + + public abstract List<YamlSchemaNode> getNextNodes() throws YamlSchemaProcessingException; + + public abstract boolean isContainingSubStructure(); + + public String getComment() { + return comment; + } + + YamlSchemaNode(String name, String path, boolean required, String comment) { + this.name = name; + this.path = path; + this.required = required; + this.comment = comment; + } + + static class YamlSchemaProcessingException extends YamlProcessingException { + YamlSchemaProcessingException(Throwable throwable) { + super(throwable); + } + } +} diff --git a/csarvalidation/src/main/java/org/onap/validation/yaml/schema/node/YamlSchemaNodeFactory.java b/csarvalidation/src/main/java/org/onap/validation/yaml/schema/node/YamlSchemaNodeFactory.java new file mode 100644 index 0000000..79a8f14 --- /dev/null +++ b/csarvalidation/src/main/java/org/onap/validation/yaml/schema/node/YamlSchemaNodeFactory.java @@ -0,0 +1,84 @@ +/* + * Copyright 2020 Nokia + * + * 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.validation.yaml.schema.node; + +import org.onap.validation.yaml.exception.YamlProcessingException; +import org.onap.validation.yaml.model.YamlDocument; +import org.onap.validation.yaml.model.YamlDocumentFactory; +import org.onap.validation.yaml.model.YamlParameterListFactory; +import org.onap.validation.yaml.model.YamlParametersList; + +import static org.onap.validation.yaml.model.YamlDocumentFactory.YamlDocumentParsingException; + +public class YamlSchemaNodeFactory { + + public static final String EMPTY_COMMENT = "no comment available"; + static final String STRUCTURE_KEY = "structure"; + static final String COMMENT_KEY = "comment"; + static final String VALUE_KET = "value"; + static final String PRESENCE_KEY = "presence"; + static final String PRESENCE_REQUIRED_KEY = "required"; + + public YamlSchemaNode createNode(String nodeName, String path, YamlDocument yamlDocument) + throws YamlProcessingException { + + YamlSchemaNode yamlSchemaNode; + if(isYamlContainingKey(yamlDocument, STRUCTURE_KEY)) { + yamlSchemaNode = new YamlSchemaBranchNode( + nodeName, path, getIsPresenceRequired(yamlDocument), getComment(yamlDocument), + getNextNodes(yamlDocument) + ); + } else { + yamlSchemaNode = new YamlSchemaLeafNode( + nodeName, path, getIsPresenceRequired(yamlDocument), getComment(yamlDocument), + getAcceptedValues(yamlDocument) + ); + } + return yamlSchemaNode; + } + + private YamlDocument getNextNodes(YamlDocument yamlDocument) + throws YamlDocumentParsingException { + return new YamlDocumentFactory().createYamlDocument(yamlDocument.getYaml().get(STRUCTURE_KEY)); + } + + private String getComment(YamlDocument yamlDocument) { + + return isYamlContainingKey(yamlDocument, COMMENT_KEY) + ? yamlDocument.getYaml().get(COMMENT_KEY).toString() + : EMPTY_COMMENT; + } + + private YamlParametersList getAcceptedValues(YamlDocument yamlDocument) { + + return isYamlContainingKey(yamlDocument, VALUE_KET) + ? new YamlParameterListFactory().createYamlParameterList(yamlDocument.getYaml().get(VALUE_KET)) + : new YamlParameterListFactory().createEmptyYamlParameterList(); + } + + private boolean getIsPresenceRequired(YamlDocument yamlDocument) { + + return isYamlContainingKey(yamlDocument, PRESENCE_KEY) + && yamlDocument.getYaml().get(PRESENCE_KEY).equals(PRESENCE_REQUIRED_KEY); + } + + private boolean isYamlContainingKey(YamlDocument yamlDocument, String structureKey) { + return yamlDocument.getYaml().containsKey(structureKey); + } + +} |