summaryrefslogtreecommitdiffstats
path: root/openecomp-be/lib/openecomp-tosca-converter-lib/openecomp-tosca-converter-core/src/main/java
diff options
context:
space:
mode:
authorandre.schmid <andre.schmid@est.tech>2019-07-12 12:33:10 +0000
committerOren Kleks <orenkle@amdocs.com>2019-07-21 09:37:45 +0000
commit22360c78d550a25b9bdaea12cdb208371b69a488 (patch)
treee8dc4fdc7ab5933ba13e68f081f185a7153bb8ae /openecomp-be/lib/openecomp-tosca-converter-lib/openecomp-tosca-converter-core/src/main/java
parent9a3add2d727723877bbc6119d9b1f4a5d49fccc6 (diff)
Allow relative path for SOL004 descriptors import
Allow the use of relative path on SOL004 descriptors imports. Resolves imports with "/", "../" or "./" entries during validation and package processing. Validate if the reference is inside the package. Fix problem where imported descriptor files, described as a non string scalar yaml entry, were not being checked by the validator. Change-Id: Ie5a32736b6090b4adf178e8714f7460bcd068def Issue-ID: SDC-2422 Signed-off-by: andre.schmid <andre.schmid@est.tech>
Diffstat (limited to 'openecomp-be/lib/openecomp-tosca-converter-lib/openecomp-tosca-converter-core/src/main/java')
-rw-r--r--openecomp-be/lib/openecomp-tosca-converter-lib/openecomp-tosca-converter-core/src/main/java/org/openecomp/core/impl/AbstractToscaSolConverter.java48
-rw-r--r--openecomp-be/lib/openecomp-tosca-converter-lib/openecomp-tosca-converter-core/src/main/java/org/openecomp/core/impl/InvalidToscaDefinitionImportException.java55
-rw-r--r--openecomp-be/lib/openecomp-tosca-converter-lib/openecomp-tosca-converter-core/src/main/java/org/openecomp/core/impl/ToscaDefinitionImportHandler.java231
3 files changed, 296 insertions, 38 deletions
diff --git a/openecomp-be/lib/openecomp-tosca-converter-lib/openecomp-tosca-converter-core/src/main/java/org/openecomp/core/impl/AbstractToscaSolConverter.java b/openecomp-be/lib/openecomp-tosca-converter-lib/openecomp-tosca-converter-core/src/main/java/org/openecomp/core/impl/AbstractToscaSolConverter.java
index 6371ba67d9..f0d8bb2b3c 100644
--- a/openecomp-be/lib/openecomp-tosca-converter-lib/openecomp-tosca-converter-core/src/main/java/org/openecomp/core/impl/AbstractToscaSolConverter.java
+++ b/openecomp-be/lib/openecomp-tosca-converter-lib/openecomp-tosca-converter-core/src/main/java/org/openecomp/core/impl/AbstractToscaSolConverter.java
@@ -23,8 +23,6 @@
package org.openecomp.core.impl;
import org.onap.sdc.tosca.datatypes.model.ServiceTemplate;
-import org.openecomp.core.converter.ServiceTemplateReaderService;
-import org.openecomp.core.impl.services.ServiceTemplateReaderServiceImpl;
import org.openecomp.core.utilities.file.FileContentHandler;
import org.openecomp.sdc.logging.api.Logger;
import org.openecomp.sdc.logging.api.LoggerFactory;
@@ -35,12 +33,10 @@ import org.openecomp.sdc.tosca.datatypes.ToscaServiceModel;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.List;
import java.util.Map;
import java.util.Set;
import static org.openecomp.core.converter.datatypes.Constants.globalStName;
-import static org.openecomp.sdc.tosca.csar.CSARConstants.NON_FILE_IMPORT_ATTRIBUTES;
import static org.openecomp.sdc.tosca.csar.CSARConstants.TOSCA_META_ENTRY_DEFINITIONS;
import static org.openecomp.sdc.tosca.csar.CSARConstants.TOSCA_META_PATH_FILE_NAME;
@@ -68,8 +64,7 @@ public abstract class AbstractToscaSolConverter extends AbstractToscaConverter {
GlobalSubstitutionServiceTemplate gsst, String mServiceDefinitionFileName) {
if (mServiceDefinitionFileName != null) {
handleServiceTemplate(getSimpleName(mServiceDefinitionFileName), mServiceDefinitionFileName, csarFiles, serviceTemplates);
- String parentDir = mServiceDefinitionFileName.substring(0, mServiceDefinitionFileName.lastIndexOf("/"));
- handleImportDefinitions(mServiceDefinitionFileName, csarFiles, parentDir, gsst);
+ handleImportDefinitions(mServiceDefinitionFileName, csarFiles, gsst);
}
}
@@ -86,39 +81,16 @@ public abstract class AbstractToscaSolConverter extends AbstractToscaConverter {
}
}
- private void handleImportDefinitions(String fileName, Map<String, byte[]> csarFiles, String parentDir, GlobalSubstitutionServiceTemplate gsst) {
- handledDefinitionFilesList.add(fileName);
- ServiceTemplateReaderService readerService = new ServiceTemplateReaderServiceImpl(csarFiles.get(fileName));
- List<Object> imports = (readerService).getImports();
- for (Object o : imports) {
- String importPath = getImportedFilePath(o, parentDir);
- if (importPath != null && !handledDefinitionFilesList.contains(importPath)) {
- handleDefintionTemplate(importPath, csarFiles, gsst);
- if (importPath.contains("/")) {
- parentDir = importPath.substring(0, importPath.lastIndexOf("/"));
- }
- handleImportDefinitions(importPath, csarFiles, parentDir, gsst);
- }
+ private void handleImportDefinitions(final String fileName, final Map<String, byte[]> csarFiles
+ , final GlobalSubstitutionServiceTemplate gsst) {
+ final ToscaDefinitionImportHandler toscaDefinitionImportHandler = new ToscaDefinitionImportHandler(csarFiles, fileName);
+ if (toscaDefinitionImportHandler.hasError()) {
+ throw new InvalidToscaDefinitionImportException(toscaDefinitionImportHandler.getErrors());
}
- return;
- }
-
- private String getImportedFilePath(Object o, String parentDir) {
- if (o instanceof String) {
- String fileName = (String) o;
- if (!fileName.contains("/")) {
- fileName = parentDir + "/" + fileName;
- }
- return fileName;
- } else if (o instanceof Map) {
- Map<String, Object> o1 = (Map) o;
- for (Map.Entry<String, Object> entry : o1.entrySet()) {
- if (NON_FILE_IMPORT_ATTRIBUTES.stream().noneMatch(attr -> entry.getKey().equals(attr))) {
- getImportedFilePath(entry.getValue(), parentDir);
- }
- }
+ handledDefinitionFilesList.addAll(toscaDefinitionImportHandler.getHandledDefinitionFilesList());
+ for (final String file : handledDefinitionFilesList) {
+ handleDefintionTemplate(file, csarFiles, gsst);
}
- return null;
}
private String getMainServiceDefinitionFileName(FileContentHandler contentHandler) throws IOException {
@@ -134,7 +106,7 @@ public abstract class AbstractToscaSolConverter extends AbstractToscaConverter {
private String getSimpleName(String path) {
if (path != null && path.contains("/")) {
- path = path.substring(path.lastIndexOf("/") + 1);
+ path = path.substring(path.lastIndexOf('/') + 1);
}
return path;
}
diff --git a/openecomp-be/lib/openecomp-tosca-converter-lib/openecomp-tosca-converter-core/src/main/java/org/openecomp/core/impl/InvalidToscaDefinitionImportException.java b/openecomp-be/lib/openecomp-tosca-converter-lib/openecomp-tosca-converter-core/src/main/java/org/openecomp/core/impl/InvalidToscaDefinitionImportException.java
new file mode 100644
index 0000000000..ac2e5eccc2
--- /dev/null
+++ b/openecomp-be/lib/openecomp-tosca-converter-lib/openecomp-tosca-converter-core/src/main/java/org/openecomp/core/impl/InvalidToscaDefinitionImportException.java
@@ -0,0 +1,55 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2019 Nordix Foundation
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.core.impl;
+
+import java.util.List;
+import java.util.StringJoiner;
+import org.apache.commons.collections.CollectionUtils;
+import org.openecomp.sdc.datatypes.error.ErrorMessage;
+
+/**
+ * Runtime exception for errors in import statements inside a TOSCA definition yaml file.
+ */
+public class InvalidToscaDefinitionImportException extends RuntimeException {
+
+ private final String message;
+
+ /**
+ * Builds the exception message based on the provided validation error list.
+ * @param validationErrorList The error list
+ */
+ public InvalidToscaDefinitionImportException(final List<ErrorMessage> validationErrorList) {
+ final StringBuilder stringBuilder = new StringBuilder();
+ stringBuilder.append("The provided package is invalid as it contains descriptors import errors:\n");
+ if (CollectionUtils.isNotEmpty(validationErrorList)) {
+ final StringJoiner joiner = new StringJoiner(";\n");
+ validationErrorList.forEach(
+ errorMessage -> joiner.add(String.format("%s: %s", errorMessage.getLevel(), errorMessage.getMessage())));
+ message = stringBuilder.append(joiner.toString()).toString();
+ } else {
+ message = stringBuilder.toString();
+ }
+ }
+
+ @Override
+ public String getMessage() {
+ return message;
+ }
+}
diff --git a/openecomp-be/lib/openecomp-tosca-converter-lib/openecomp-tosca-converter-core/src/main/java/org/openecomp/core/impl/ToscaDefinitionImportHandler.java b/openecomp-be/lib/openecomp-tosca-converter-lib/openecomp-tosca-converter-core/src/main/java/org/openecomp/core/impl/ToscaDefinitionImportHandler.java
new file mode 100644
index 0000000000..8422c89f2e
--- /dev/null
+++ b/openecomp-be/lib/openecomp-tosca-converter-lib/openecomp-tosca-converter-core/src/main/java/org/openecomp/core/impl/ToscaDefinitionImportHandler.java
@@ -0,0 +1,231 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2019 Nordix Foundation
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.core.impl;
+
+import static org.openecomp.sdc.tosca.csar.CSARConstants.NON_FILE_IMPORT_ATTRIBUTES;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.apache.commons.io.FilenameUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.openecomp.core.converter.ServiceTemplateReaderService;
+import org.openecomp.core.impl.services.ServiceTemplateReaderServiceImpl;
+import org.openecomp.sdc.common.errors.Messages;
+import org.openecomp.sdc.datatypes.error.ErrorLevel;
+import org.openecomp.sdc.datatypes.error.ErrorMessage;
+
+/**
+ * Handles TOSCA definition imports, checking for import definition errors.
+ */
+public class ToscaDefinitionImportHandler {
+
+ private final Map<String, byte[]> fileMap;
+ private final Set<String> handledDefinitionFilesList = new LinkedHashSet<>();
+ private final List<ErrorMessage> validationErrorList = new ArrayList<>();
+ private String currentFile;
+
+ /**
+ * Reads the provided package structure starting from a main definition yaml file.
+ * @param fileStructureMap The package structure with file path and respective file byte
+ * @param mainDefinitionFilePath The main descriptor yaml file to start the reading
+ */
+ public ToscaDefinitionImportHandler(final Map<String, byte[]> fileStructureMap, final String mainDefinitionFilePath) {
+ this.fileMap = fileStructureMap;
+ handleImports(mainDefinitionFilePath);
+ }
+
+ /**
+ * Reads and validates the descriptor imports recursively.
+ * Starts from the provided descriptor and goes until the end of the import tree.
+ * Processes each file just once.
+ *
+ * @param fileName the descriptor file path
+ */
+ private void handleImports(final String fileName) {
+ currentFile = fileName;
+ if (!checkImportExists(fileName)) {
+ return;
+ }
+ final ServiceTemplateReaderService readerService;
+ try {
+ readerService = new ServiceTemplateReaderServiceImpl(fileMap.get(fileName));
+ } catch (final Exception ex) {
+ reportError(ErrorLevel.ERROR,
+ String.format(Messages.INVALID_YAML_FORMAT.getErrorMessage(), ex.getMessage()));
+ return;
+ }
+ handledDefinitionFilesList.add(fileName);
+ final List<Object> imports = readerService.getImports();
+ final List<String> extractImportFiles = extractFileImports(imports);
+ for (final String importedFile : extractImportFiles) {
+ final String resolvedPath = resolveImportPath(FilenameUtils.getPath(fileName), importedFile);
+ if (!handledDefinitionFilesList.contains(resolvedPath)) {
+ handleImports(resolvedPath);
+ }
+ }
+ }
+
+ /**
+ * Iterates reads each import statement in the given list.
+ * <pre>
+ * example of a descriptor.yaml import statement
+ * imports:
+ * - /Artifacts/anImportedDescriptor.yaml
+ * - anotherDescriptor: anotherImportedDescriptor.yaml
+ * - yetAnotherDescriptor:
+ * yetAnotherDescriptor: ../Definitions/yetAnotherDescriptor.yaml
+ * </pre>
+ * @param imports the import statements
+ * @return
+ * The list of import file paths found
+ */
+ private List<String> extractFileImports(final List<Object> imports) {
+ final List<String> importedFileList = new ArrayList<>();
+ imports.forEach(importObject -> importedFileList.addAll(readImportStatement(importObject)));
+
+ return importedFileList;
+ }
+
+ /**
+ * Reads an import statement which can be a value, a [key:value] or a [key:[key:value]].
+ * Ignores entries which contains the same keys as
+ * {@link org.openecomp.sdc.tosca.csar.CSARConstants#NON_FILE_IMPORT_ATTRIBUTES}.
+ * Reports invalid import statements.
+ * <pre>
+ * example of yaml imports statements:
+ * - /Artifacts/anImportedDescriptor.yaml
+ * - anotherDescriptor: anotherImportedDescriptor.yaml
+ * - yetAnotherDescriptor:
+ * yetAnotherDescriptor: ../Definitions/yetAnotherDescriptor.yaml
+ * </pre>
+ * @param importObject the object representing the yaml import statement
+ * @return
+ * The list of import file paths found
+ */
+ private List<String> readImportStatement(final Object importObject) {
+ final List<String> importedFileList = new ArrayList<>();
+ if (importObject instanceof String) {
+ importedFileList.add((String) importObject);
+ } else if (importObject instanceof Map) {
+ final Map<String, Object> importObjectMap = (Map) importObject;
+ for (final Map.Entry entry : importObjectMap.entrySet()) {
+ if (NON_FILE_IMPORT_ATTRIBUTES.stream().noneMatch(attr -> entry.getKey().equals(attr))) {
+ importedFileList.addAll(readImportStatement(entry.getValue()));
+ }
+ }
+ } else {
+ reportError(ErrorLevel.ERROR,
+ String.format(Messages.INVALID_IMPORT_STATEMENT.getErrorMessage(), currentFile, importObject));
+ }
+
+ return importedFileList;
+ }
+
+ /**
+ * Given a directory path, resolves the import path.
+ * @param directoryPath A directory path to resolve the import path
+ * @param importPath An import statement path
+ * @return
+ * The resolved path of the import, using as base the directory path
+ */
+ private String resolveImportPath(final String directoryPath, final String importPath) {
+ final String fixedParentDir;
+ if (StringUtils.isEmpty(directoryPath)) {
+ fixedParentDir = "/";
+ } else {
+ fixedParentDir = String.format("%s%s%s",
+ directoryPath.startsWith("/") ? "" : "/"
+ , directoryPath
+ , directoryPath.endsWith("/") ? "" : "/");
+ }
+
+ final URI parentDirUri = URI.create(fixedParentDir);
+
+ String resolvedImportPath = parentDirUri.resolve(importPath).toString();
+ if (resolvedImportPath.contains("../")) {
+ reportError(ErrorLevel.ERROR,
+ Messages.INVALID_IMPORT_STATEMENT.formatMessage(currentFile, importPath));
+ return null;
+ }
+ if (resolvedImportPath.startsWith("/")) {
+ resolvedImportPath = resolvedImportPath.substring(1);
+ }
+
+ return resolvedImportPath;
+ }
+
+ /**
+ * Checks if the given file path exists inside the file structure.
+ * Reports an error if the file was not found.
+ *
+ * @param filePath file path to check inside the file structure
+ * @return
+ * {@code true} if the file exists, {@code false} otherwise
+ */
+ private boolean checkImportExists(final String filePath) {
+ if (!fileMap.keySet().contains(filePath)) {
+ reportError(ErrorLevel.ERROR, Messages.MISSING_IMPORT_FILE.formatMessage(filePath));
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Gets all processed files during the import handling.
+ * @return
+ * A list containing the processed files paths
+ */
+ public Set<String> getHandledDefinitionFilesList() {
+ return handledDefinitionFilesList;
+ }
+
+ /**
+ * Adds an error to the validation error list.
+ *
+ * @param errorLevel the error level
+ * @param errorMessage the error message
+ */
+ private void reportError(final ErrorLevel errorLevel, final String errorMessage) {
+ validationErrorList.add(new ErrorMessage(errorLevel, errorMessage));
+ }
+
+ /**
+ * Gets the list of errors.
+ * @return
+ * The import validation errors detected
+ */
+ public List<ErrorMessage> getErrors() {
+ return validationErrorList;
+ }
+
+ /**
+ * Checks if the handler detected a import error.
+ * @return
+ * {@code true} if the handler detected any error, {@code false} otherwise.
+ */
+ public boolean hasError() {
+ return !validationErrorList.isEmpty();
+ }
+}