/******************************************************************************* * (c) Copyright 2016 Hewlett-Packard Development Company, L.P. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Apache License v2.0 which accompany this distribution. * * The Apache License is available at * http://www.apache.org/licenses/LICENSE-2.0 * *******************************************************************************/ package io.cloudslang.lang.runtime.bindings.scripts; import io.cloudslang.lang.entities.SystemProperty; import io.cloudslang.lang.entities.bindings.ScriptFunction; import io.cloudslang.lang.entities.bindings.values.PyObjectValue; import io.cloudslang.lang.entities.bindings.values.Value; import io.cloudslang.lang.entities.bindings.values.ValueFactory; import io.cloudslang.runtime.api.python.PythonEvaluationResult; import io.cloudslang.runtime.api.python.PythonRuntimeService; import java.io.Serializable; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Set; import org.apache.commons.lang3.StringUtils; import org.python.core.Py; import org.python.core.PyObject; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; /** * @author stoneo * @version $Id$ * @since 06/11/2014 */ @Component public class ScriptEvaluator extends ScriptProcessor { private static String LINE_SEPARATOR = System.lineSeparator(); private static final String SYSTEM_PROPERTIES_MAP = "sys_prop"; private static final String GET_FUNCTION_DEFINITION = "def get(key, default_value=None):" + LINE_SEPARATOR + " value = globals().get(key)" + LINE_SEPARATOR + " return default_value if value is None else value"; private static final String GET_SP_FUNCTION_DEFINITION = "def get_sp(key, default_value=None):" + LINE_SEPARATOR + " property_value = " + SYSTEM_PROPERTIES_MAP + ".get(key)" + LINE_SEPARATOR + " return default_value if property_value is None else property_value"; private static final String CHECK_EMPTY_FUNCTION_DEFINITION = "def check_empty(value_to_check, default_value=None):" + LINE_SEPARATOR + " return default_value if value_to_check is None else value_to_check"; @Autowired private PythonRuntimeService pythonRuntimeService; public Value evalExpr(String expr, Map<String, Value> context, Set<SystemProperty> systemProperties, Set<ScriptFunction> functionDependencies) { try { Map<String, Serializable> pythonContext = createPythonContext(context); boolean systemPropertiesDefined = functionDependencies.contains(ScriptFunction.GET_SYSTEM_PROPERTY); if (systemPropertiesDefined) { pythonContext.put(SYSTEM_PROPERTIES_MAP, (Serializable) prepareSystemProperties(systemProperties)); } PythonEvaluationResult result = pythonRuntimeService.eval( buildAddFunctionsScript(functionDependencies), expr, pythonContext); if (systemPropertiesDefined) { pythonContext.remove(SYSTEM_PROPERTIES_MAP); } return ValueFactory.create(result.getEvalResult(), getSensitive(result.getResultContext(), systemPropertiesDefined)); } catch (Exception exception) { throw new RuntimeException("Error in running script expression: '" + expr + "',\n\tException is: " + handleExceptionSpecialCases(exception.getMessage()), exception); } } private String buildAddFunctionsScript(Set<ScriptFunction> functionDependencies) { String functions = ""; for (ScriptFunction function : functionDependencies) { switch (function) { case GET: functions += GET_FUNCTION_DEFINITION; functions = appendDelimiterBetweenFunctions(functions); break; case GET_SYSTEM_PROPERTY: functions += GET_SP_FUNCTION_DEFINITION; functions = appendDelimiterBetweenFunctions(functions); break; case CHECK_EMPTY: functions += CHECK_EMPTY_FUNCTION_DEFINITION; functions = appendDelimiterBetweenFunctions(functions); break; default: throw new RuntimeException("Error adding function to context: '" + function.getValue() + "' is not valid."); } } return functions; } private String appendDelimiterBetweenFunctions(String text) { return text + LINE_SEPARATOR + LINE_SEPARATOR; } private Map<String, Value> prepareSystemProperties(Set<SystemProperty> properties) { Map<String, Value> processedSystemProperties = new HashMap<>(); for (SystemProperty property : properties) { processedSystemProperties.put(property.getFullyQualifiedName(), ValueFactory.createPyObjectValue(property.getValue())); } return processedSystemProperties; } private String handleExceptionSpecialCases(String message) { String processedMessage = message; if (StringUtils.isNotEmpty(message) && message.contains("get_sp") && message.contains("not defined")) { processedMessage = message + ". Make sure to use correct syntax for the function:" + " get_sp('fully.qualified.name', optional_default_value)."; } return processedMessage; } private boolean getSensitive(Map<String, Serializable> executionResultContext, boolean systemPropertiesInContext) { if (systemPropertiesInContext) { Map<String, Serializable> context = new HashMap<>(executionResultContext); PyObject rawSystemProperties = (PyObject) context.remove(SYSTEM_PROPERTIES_MAP); @SuppressWarnings("unchecked") Map<String, Value> systemProperties = Py.tojava(rawSystemProperties, Map.class); @SuppressWarnings("unchecked") Collection<Serializable> systemPropertyValues = (Collection) systemProperties.values(); return checkSensitivity(systemPropertyValues) || checkSensitivity(context.values()); } else { return (checkSensitivity(executionResultContext.values())); } } private boolean checkSensitivity(Collection<Serializable> values) { for (Serializable value : values) { if (value != null && value instanceof PyObjectValue) { PyObjectValue pyObjectValue = (PyObjectValue) value; if (pyObjectValue.isSensitive() && pyObjectValue.isAccessed()) { return true; } } } return false; } }