From 91d04c64771832a0b8815ffbe1f0f9920320d94d Mon Sep 17 00:00:00 2001 From: Pamela Dragosh Date: Tue, 14 Feb 2017 19:41:00 -0500 Subject: Initial OpenECOMP policy/engine commit Change-Id: I7dbff37733b661643dd4d1caefa3d7dccc361b6e Signed-off-by: Pamela Dragosh --- .../openecomp/policy/elk/converter/Xacml2Elk.java | 978 +++++++++++++++++++++ 1 file changed, 978 insertions(+) create mode 100644 ecomp-sdk-app/src/main/java/org/openecomp/policy/elk/converter/Xacml2Elk.java (limited to 'ecomp-sdk-app/src/main/java/org/openecomp/policy/elk/converter/Xacml2Elk.java') diff --git a/ecomp-sdk-app/src/main/java/org/openecomp/policy/elk/converter/Xacml2Elk.java b/ecomp-sdk-app/src/main/java/org/openecomp/policy/elk/converter/Xacml2Elk.java new file mode 100644 index 000000000..f7aa34ff2 --- /dev/null +++ b/ecomp-sdk-app/src/main/java/org/openecomp/policy/elk/converter/Xacml2Elk.java @@ -0,0 +1,978 @@ +/*- + * ============LICENSE_START======================================================= + * ECOMP Policy Engine + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.policy.elk.converter; + + +import io.searchbox.core.Update; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.StringReader; +import java.io.StringWriter; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBElement; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; +import javax.xml.bind.Unmarshaller; +import javax.xml.transform.Result; +import javax.xml.transform.stream.StreamResult; + +import oasis.names.tc.xacml._3_0.core.schema.wd_17.AdviceExpressionType; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeAssignmentExpressionType; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeValueType; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.EffectType; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.ObligationExpressionType; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.PolicyType; + +import org.apache.commons.io.IOUtils; +import org.eclipse.persistence.jaxb.JAXBContextFactory; +import org.eclipse.persistence.jaxb.MarshallerProperties; +import org.eclipse.persistence.jaxb.json.JsonSchemaOutputResolver; + +import com.att.research.xacml.api.Advice; +import com.att.research.xacml.api.AttributeAssignment; +import com.att.research.xacml.api.Identifier; +import com.att.research.xacml.api.Obligation; +import org.openecomp.policy.xacml.api.XACMLErrorConstants; +import com.att.research.xacml.util.XACMLPolicyScanner; +import com.att.research.xacml.util.XACMLProperties; +import com.att.research.xacml.util.XACMLPolicyScanner.CallbackResult; +import com.att.research.xacml.util.XACMLPolicyScanner.SimpleCallback; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.dataformat.xml.XmlMapper; +import com.fasterxml.jackson.module.jaxb.JaxbAnnotationModule; +import com.fasterxml.jackson.module.jsonSchema.JsonSchema; +import com.fasterxml.jackson.module.jsonSchema.factories.SchemaFactoryWrapper; + +import org.kohsuke.args4j.CmdLineException; +import org.kohsuke.args4j.CmdLineParser; +import org.kohsuke.args4j.Option; +import org.openecomp.policy.elk.client.ElkConnector; +import org.openecomp.policy.elk.client.ElkConnector.PolicyBodyType; +import org.openecomp.policy.rest.XACMLRestProperties; +import org.openecomp.policy.common.logging.flexlogger.FlexLogger; +import org.openecomp.policy.common.logging.flexlogger.Logger; + + +@SuppressWarnings("unused") +public class Xacml2Elk { + public static final String URLID_ATTRIBUTE = "URLID"; + public static final String BODY_ATTRIBUTE = "body"; + + protected static final Logger logger = FlexLogger.getLogger(Xacml2Elk.class); + protected static JAXBContext jaxbContext = jaxbContext(); + + protected static String toConfigsWebDirectory(String policyType) + throws IllegalArgumentException { + if (policyType == null || policyType.isEmpty()) + throw new IllegalArgumentException("Unexpected policy type: " + policyType); + + ElkConnector.PolicyType type = ElkConnector.PolicyType.valueOf(policyType); + switch(type) { + case Config: + return type.name(); + case Action: + return type.name(); + case Decision: + return type.name(); + case Config_Fault: + case Config_PM: + case Config_FW: + case Config_MS: + return ElkConnector.PolicyType.Config.name(); + default: + throw new IllegalArgumentException("Unexpected policy type: " + policyType); + } + } + + protected synchronized static JAXBContext jaxbContext() { + if (jaxbContext != null) { + return jaxbContext; + } + + try { + jaxbContext = JAXBContextFactory.createContext(new Class[] {PolicyType.class}, null); + } catch (JAXBException e) { + logger.error(XACMLErrorConstants.ERROR_SYSTEM_ERROR + ":" + + "JAXB Context cannot be created"); + return null; + } + + return jaxbContext; + } + + protected static class CLIOptions { + @Option(name="-t", usage="policy type", aliases={"-type", "--type"}, required=true) + protected String type; + + @Option(name="-n", usage="policy name", aliases={"-name", "--name"}, required=true) + protected String name; + + @Option(name="-o", usage="git repository owner", aliases={"-owner", "--owner"}, required=true) + protected String owner; + + @Option(name="-s", usage="enclosing scope", aliases={"-scope", "--scope"}, required=true) + protected String scope; + + @Option(name="-x", usage="xacml input file", aliases={"-xacml", "--xacml"}, required=true, metaVar="") + protected File xacmlFile; + + @Option(name="-p", usage="payload body type", aliases={"-payloadType", "--payloadType"}, required=false, metaVar="", depends={"-b"}) + protected PolicyBodyType bodyType; + + @Option(name="-b", usage="payload body", aliases={"-body", "--body"}, required=false, metaVar="", depends={"-p"}) + protected String body; + + @Option(name="-d", usage="elk record output directory", aliases={"-d", "--directory"}, required=true, metaVar="") + protected File elkDirectory; + + @Option(name = "-h", aliases = {"-help", "--help"}, usage = "print this message") + private boolean help = false; + }; + + class AttributeAssignmentFinderProcessor extends SimpleCallback { + protected final String attributeId; + protected String attributeValue = null; + + public AttributeAssignmentFinderProcessor(String attributeId) { + this.attributeId = attributeId; + } + + public String getAttributeValue() { + return attributeValue; + } + + public boolean isAttributeValue() { + return (this.attributeValue != null && !this.attributeValue.isEmpty()); + } + + private boolean processAssignments( + List assignments) { + if (logger.isTraceEnabled()) + logger.trace("ENTER"); + + for (AttributeAssignmentExpressionType assignment : assignments) { + if (!assignment.getAttributeId().equals(attributeId)) { + if (logger.isDebugEnabled()) + logger.debug("Ignoring: " + assignment.getAttributeId()); + continue; + } + + if (logger.isDebugEnabled()) + logger.debug("Found Attribute ID: " + assignment.getAttributeId()); + + JAXBElement jaxbExp = assignment.getExpression(); + Object assignmentObject = jaxbExp.getValue(); + if (assignmentObject instanceof AttributeValueType) { + AttributeValueType avt = (AttributeValueType) assignmentObject; + if (avt.getContent().size() <= 0) { + logger.warn("Ignoring: " + assignment.getAttributeId() + ": No values"); + continue; + } + + this.attributeValue = avt.getContent().get(0).toString(); + if (logger.isInfoEnabled()) + logger.info("Found: " + this.attributeValue); + + return true; + } + } + return false; + } + + @Override + public CallbackResult onAdvice(Object parent, AdviceExpressionType expression, Advice advice) { + if (logger.isTraceEnabled()) + logger.trace("ENTER"); + + List assignments = + expression.getAttributeAssignmentExpression(); + + if (assignments != null) { + boolean found = processAssignments(assignments); + if (found) + return CallbackResult.STOP; + } + + return super.onAdvice(parent, expression, advice); + } + + @Override + public CallbackResult onObligation(Object parent, ObligationExpressionType expression, Obligation obligation) { + if (logger.isTraceEnabled()) + logger.trace("ENTER"); + + List assignments = + expression.getAttributeAssignmentExpression(); + + if (assignments != null) { + boolean found = processAssignments(assignments); + if (found) + return CallbackResult.STOP; + } + + return super.onObligation(parent, expression, obligation); + + } + } + + final protected String type; + final protected String name; + final protected String owner; + final protected String scope; + final protected File xacmlFile; + final protected File elkDirectory; + + final protected JAXBElement policy; + + protected PolicyBodyType bodyType; + protected String body; + + + public Xacml2Elk(String type, String name, + String owner, String scope, + File xacmlFile, File elkDirectory) + throws IllegalArgumentException { + + this.type = type; + this.name = name; + this.owner = owner; + this.scope = scope; + this.xacmlFile = xacmlFile; + this.elkDirectory = elkDirectory; + + this.policy = jaxbXacml(xacmlFile); + + this.body = ""; + this.bodyType = PolicyBodyType.none; + bodyFromXacml(); + } + + public Xacml2Elk(CLIOptions args) throws IllegalArgumentException { + this.type = args.type; + this.name = args.name; + this.owner = args.owner; + this.scope = args.scope; + this.xacmlFile = args.xacmlFile; + this.elkDirectory = args.elkDirectory; + + this.policy = jaxbXacml(xacmlFile); + + if (args.body == null || args.body.isEmpty()) { + this.body = ""; + this.bodyType = PolicyBodyType.none; + bodyFromXacml(); + } else { + this.body = args.body; + this.bodyType = args.bodyType; + } + } + + public Xacml2Elk(String type, String name, String owner, + String scope, File xacmlFile, PolicyBodyType bodyType, + String body, File destinationDir) + throws IllegalArgumentException { + this.type = type; + this.name = name; + this.owner = owner; + this.scope = scope; + this.xacmlFile = xacmlFile; + this.bodyType = bodyType; + this.body = body; + this.elkDirectory = destinationDir; + + this.policy = jaxbXacml(xacmlFile); + } + + public Xacml2Elk(File xacmlFile, boolean skipBody) + throws IllegalArgumentException { + this.policy = jaxbXacml(xacmlFile); + PolicyType jPolicy = this.policy.getValue(); + + this.xacmlFile = xacmlFile; + + this.type = ElkConnector.toPolicyType(xacmlFile.getName()).name(); + String fileName = xacmlFile.getName().replaceFirst(this.type + "_", ""); + if (fileName.indexOf(".") > 0) + this.name = fileName.substring(0, fileName.lastIndexOf(".")); + else + this.name = fileName; + + this.owner = "admin"; + this.scope = getScope(xacmlFile.getParent()); + this.elkDirectory = null; + + this.body = ""; + this.bodyType = PolicyBodyType.none; + if (!skipBody) { + bodyFromXacml(); + } + } + + @SuppressWarnings("unchecked") + protected JAXBElement jaxbXacml(File xacmlFile) throws IllegalArgumentException { + Path xacmlPath = xacmlFile.toPath(); + if (!Files.isReadable(xacmlPath) || !Files.isRegularFile(xacmlPath)) { + if (logger.isWarnEnabled()) { + logger.warn("Error: " + xacmlPath + " is invalid."); + } + throw new IllegalArgumentException("Error: " + xacmlPath + " is invalid."); + } + + try { + Unmarshaller u = jaxbContext.createUnmarshaller(); + return (JAXBElement) u.unmarshal(xacmlFile); + } catch (Exception e) { + if (logger.isWarnEnabled()) { + logger.warn(XACMLErrorConstants.ERROR_DATA_ISSUE + " - error: " + xacmlPath + " is invalid."); + } + throw new IllegalArgumentException(xacmlFile.getAbsolutePath() + " does not contain valid XACML"); + } + } + + public JAXBElement getPolicy() { + return policy; + } + + protected String getScope(String xacmlDirPath) { + if (logger.isTraceEnabled()) logger.trace("ENTER"); + + xacmlDirPath = xacmlDirPath.replaceAll("//", "/"); + xacmlDirPath = xacmlDirPath.replaceAll("\\\\", "/"); + xacmlDirPath = xacmlDirPath.replace('\\', '/'); + + String ws = XACMLProperties.getProperty(XACMLRestProperties.PROP_ADMIN_WORKSPACE); + String adminRepo = XACMLProperties.getProperty(XACMLRestProperties.PROP_ADMIN_REPOSITORY); + Path wsPath = Paths.get(ws, "admin", adminRepo); + File repoDir = wsPath.toFile(); + String repoPath = repoDir.getPath(); + repoPath = repoPath.replaceAll("//", "/"); + repoPath = repoPath.replaceAll("\\\\", "/"); + repoPath = repoPath.replace('\\', '/'); + + int startIndex = xacmlDirPath.indexOf(repoPath.toString()) + repoPath.toString().length() + 1; + String scope = xacmlDirPath.substring(startIndex, xacmlDirPath.length()); + + if (logger.isInfoEnabled()) + logger.info("xacml-policy-path=" + xacmlDirPath + "|" + + "repository-path=" + repoPath + "|" + + "scope=" + scope); + + return scope; + } + + @SuppressWarnings("deprecation") + private boolean bodyFromXacml() { + if (logger.isTraceEnabled()) + logger.trace("ENTER"); + + String urlAttribute = URLID_ATTRIBUTE; + try { + switch (ElkConnector.toPolicyType(this.type)) { + case Action: + urlAttribute = BODY_ATTRIBUTE; + break; + case Decision: + case none: + /* no body attached to decision policies */ + if (logger.isInfoEnabled()) + logger.info("No body attached for this type of policy: " + this.xacmlFile.getAbsolutePath()); + return false; + default: + /* a flavour of a config policy - default is fine */ + break; + } + } catch (IllegalArgumentException iae) { + if (logger.isWarnEnabled()) { + logger.warn(this.type + " cannot be converted to a valid type: " + iae.getMessage(), iae); + } + return false; + } + + AttributeAssignmentFinderProcessor + processor = new AttributeAssignmentFinderProcessor(urlAttribute); + XACMLPolicyScanner xacmlScanner = + new XACMLPolicyScanner(this.policy.getValue(), processor); + xacmlScanner.scan(); + if (!processor.isAttributeValue()) { + if (logger.isInfoEnabled()) + logger.info(urlAttribute + " not found in " + this.xacmlFile.getAbsolutePath()); + return false; + } + + String configsUrl = XACMLProperties.getProperty(XACMLRestProperties.PROP_CONFIG_URL); + if (configsUrl == null || configsUrl.isEmpty() || !configsUrl.startsWith("http")) { + if (logger.isWarnEnabled()) { + logger.warn(XACMLRestProperties.PROP_CONFIG_URL + " property is not set."); + } + configsUrl = XACMLProperties.getProperty(XACMLRestProperties.PROP_PAP_URL); + if (configsUrl == null || configsUrl.isEmpty() || !configsUrl.startsWith("http")) { + if (logger.isWarnEnabled()) { + logger.warn(XACMLRestProperties.PROP_PAP_URL + " property is not set."); + } + return false; + } else { + configsUrl = configsUrl.replaceFirst("/pap", ""); + } + } + + if (!configsUrl.endsWith("/")) { + configsUrl += "/"; + } + + String urlXacml = processor.getAttributeValue(); + if (logger.isDebugEnabled()) { + logger.debug("configs url is " + configsUrl + "and url in xacml is " + urlXacml); + } + + if (urlXacml.startsWith("http")) { + // assuming this an unescaped url + } else if (urlXacml.startsWith("$URLConfig/")) { + urlXacml = urlXacml.replace("$URLConfig/", configsUrl); + } else if (urlXacml.startsWith("$URL/")) { + urlXacml = urlXacml.replace("$URL/", configsUrl); + } else{ + if (logger.isWarnEnabled()) { + logger.warn("XACML url is not in the expected format: " + urlXacml); + } + return false; + } + + if (urlXacml.endsWith(".properties")) { + this.bodyType = PolicyBodyType.properties; + } else if (urlXacml.endsWith(".json")) { + this.bodyType = PolicyBodyType.json; + } else if (urlXacml.endsWith(".xml")) { + this.bodyType = PolicyBodyType.xml; + } else if (urlXacml.endsWith(".txt")) { + this.bodyType = PolicyBodyType.txt; + } + + if (logger.isDebugEnabled()) { + logger.debug("converted url from xacml is " + urlXacml + ", body-type is " + this.bodyType); + } + + InputStream inConfigs = null; + try { + URL url = new URL(urlXacml); + URLConnection connection = url.openConnection(); + inConfigs = connection.getInputStream(); + String encoding = connection.getContentEncoding(); + encoding = (encoding == null ? "UTF-8" : encoding); + this.body = IOUtils.toString(inConfigs, encoding); + if (logger.isInfoEnabled()) { + logger.info("The following document of type " + this.bodyType.toString() + + " has been fetched from " + urlXacml + System.lineSeparator() + + this.body); + } + try { + inConfigs.close(); + } catch (IOException e) { + // ignore + logger.warn("Unexpected error closing stream to " + urlXacml, e); + } + return true; + } catch (Exception e) { + if (logger.isWarnEnabled()) { + logger.warn(XACMLErrorConstants.ERROR_SYSTEM_ERROR + + "- XACML url is not in the expected format: " + e.getMessage() + + ": " + urlXacml, e); + } + // continue + } finally { + if (inConfigs != null) { + try { + inConfigs.close(); + } catch (IOException e) { + // ignore + logger.warn("Unexpected error closing stream to " + urlXacml, e); + } + } + } + + // if retrieval through PAP url was not possible, try to retrieve it from + // filesystem instead. + + if (this.body == null || this.body.isEmpty()) { + Path webappsPath = Paths.get(XACMLProperties.getProperty(XACMLRestProperties.PROP_PAP_WEBAPPS)); + if (webappsPath == null) { + logger.error("Invalid Webapps Path Location property : " + + XACMLRestProperties.PROP_PAP_WEBAPPS); + return false; + } + String waPath = webappsPath.toFile().getAbsolutePath(); + + String typeDir = null; + try { + typeDir = Xacml2Elk.toConfigsWebDirectory(this.type); + } catch (IllegalArgumentException iae) { + if (logger.isWarnEnabled()) { + logger.warn(XACMLErrorConstants.ERROR_SYSTEM_ERROR + + "- " + this.type + + " cannot be converted to body-directory: " + iae.getMessage(), + iae); + } + this.bodyType = PolicyBodyType.none; + return false; + } + + String scopePrefix = this.scope.replace('/', '.'); + Path bodyPath = Paths.get(waPath, + typeDir, + scopePrefix + "." + this.type + "_" + + this.name + "." + this.bodyType.name()); + File bodyFile = bodyPath.toFile(); + if (Files.notExists(bodyPath)) { + if (logger.isWarnEnabled()) { + logger.warn(XACMLErrorConstants.ERROR_SYSTEM_ERROR + + "The following document of type " + this.bodyType.toString() + + " does not exist at filesystem location " + bodyFile.getAbsolutePath()); + } + this.bodyType = PolicyBodyType.none; + return false; + } else { + if (logger.isInfoEnabled()) { + logger.info("The following document of type " + this.bodyType.toString() + + " will be fetched from filesystem location " + bodyFile.getAbsolutePath()); + } + } + + try { + inConfigs = new FileInputStream(bodyFile); + this.body = IOUtils.toString(inConfigs); + inConfigs.close(); + if (logger.isInfoEnabled()) { + logger.info("The document of type " + this.bodyType.toString() + + " has been found at filesystem location " + bodyFile.getAbsolutePath()); + } + } catch (Exception e) { + if (logger.isWarnEnabled()) { + logger.warn(XACMLErrorConstants.ERROR_SYSTEM_ERROR + + "- XACML Body File cannot be read: " + bodyFile.getAbsolutePath(), e); + } + this.bodyType = PolicyBodyType.none; + return false; + } finally { + if (inConfigs != null) { + try { + inConfigs.close(); + } catch (IOException e) { + // ignore + logger.warn("Unexpected error closing stream to " + urlXacml, e); + } + } else { + return false; + } + } + } + return true; + } + + public boolean attachJsonBody(JsonNode jPolicy) { + if (this.body == null) { + if (logger.isWarnEnabled()) { + logger.warn(XACMLErrorConstants.ERROR_SYSTEM_ERROR + + "- JSON Body expected but none provided from " + + this.scope + ":" + this.type + ":" + this.name); + } + return true; + } + + ObjectNode jPolicyRoot = (ObjectNode) jPolicy; + + // verify the json is valid + final ObjectMapper mapper = new ObjectMapper(); + JsonNode jBodyContent; + try { + jBodyContent = mapper.readTree(this.body); + } catch (IOException e) { + logger.warn(XACMLErrorConstants.ERROR_SYSTEM_ERROR + + "- JSON Body is invalid in " + + this.scope + ":" + this.type + ":" + this.name + ":" + + e.getMessage() + System.lineSeparator() + this.body, e); + return false; + } + + String jBodyName = this.type + "_" + "Body"; + ObjectNode jBodyContainer = mapper.createObjectNode(); + jBodyContainer.set(jBodyName, jBodyContent); + + jPolicyRoot.set("Body", jBodyContainer); + + if (logger.isDebugEnabled()) + logger.debug("Attaching JSON to " + + this.scope + ":" + + this.type + ":" + this.name + ":" + + jBodyName + System.lineSeparator() + + jBodyContent); + + return true; + } + + @SuppressWarnings("unchecked") + public boolean attachXmlBody(JsonNode jPolicy) { + if (this.body == null) { + if (logger.isWarnEnabled()) { + logger.warn(XACMLErrorConstants.ERROR_SYSTEM_ERROR + + "- JSON Body expected but none provided from " + + this.scope + ":" + this.type + ":" + this.name); + } + return true; + } + + XmlMapper xmlMapper = new XmlMapper(); + xmlMapper.setConfig(xmlMapper.getSerializationConfig().withRootName("")); + Map map; + try { + map = xmlMapper.readValue(this.body, Map.class); + } catch (IOException e) { + logger.warn(XACMLErrorConstants.ERROR_SYSTEM_ERROR + + "- XML Body is invalid in " + + this.scope + ":" + this.type + ":" + this.name + ":" + + e.getMessage() + System.lineSeparator() + this.body, e); + return false; + } + + final ObjectMapper mapper = new ObjectMapper(); + String jXmlBody; + try { + jXmlBody = mapper.writeValueAsString(map); + } catch (JsonProcessingException e) { + logger.warn(XACMLErrorConstants.ERROR_SYSTEM_ERROR + + "- Cannot convert XML Body to JSON in " + + this.scope + ":" + this.type + ":" + this.name + ":" + + e.getMessage() + System.lineSeparator() + this.body, e); + return false; + } + + if (logger.isDebugEnabled()) + logger.debug("XML-to-JSON Body conversion: " + this.scope + ":" + + this.type + ":" + this.name +jXmlBody); + + JsonNode jBodyContent; + try { + jBodyContent = mapper.readTree(jXmlBody); + } catch (IOException e) { + logger.warn(XACMLErrorConstants.ERROR_SYSTEM_ERROR + + "- JSON Body (converted from XML) is invalid in " + + this.scope + ":" + this.type + ":" + this.name + ":" + + e.getMessage() + System.lineSeparator() + jXmlBody, e); + return false; + } + + ObjectNode jPolicyRoot = (ObjectNode) jPolicy; + + String jBodyName = this.type + "_" + "Body"; + ObjectNode jBodyContainer = mapper.createObjectNode(); + jBodyContainer.set(jBodyName, jBodyContent); + + jPolicyRoot.set("Body", jBodyContainer); + + + if (logger.isDebugEnabled()) + logger.debug("Attaching JSON to " + + this.scope + ":" + + this.type + ":" + this.name + ":" + + jBodyName + System.lineSeparator() + + jBodyContent); + + return true; + } + + protected boolean attachPropertiesBody(JsonNode jPolicy) { + if (this.body == null) { + if (logger.isWarnEnabled()) { + logger.warn(XACMLErrorConstants.ERROR_SYSTEM_ERROR + + "- JSON Body expected but none provided from " + + this.scope + ":" + this.type + ":" + this.name); + } + return true; + } + + final Properties propBody = new Properties(); + try { + propBody.load(new StringReader(this.body)); + } catch (IOException e) { + logger.warn(XACMLErrorConstants.ERROR_SYSTEM_ERROR + + "- JSON Body is invalid in " + + this.scope + ":" + this.type + ":" + this.name + ":" + + e.getMessage() + System.lineSeparator() + this.body, e); + return false; + } + + if (propBody.isEmpty()) { + if (logger.isInfoEnabled()) { + logger.info("Empty set of properties: " + + this.scope + ":" + this.type + ":" + this.name + + System.lineSeparator() + this.body); + } + return true; + } + + + final ObjectMapper mapper = new ObjectMapper(); + + ObjectNode jPolicyRoot = (ObjectNode) jPolicy; + ObjectNode jBody = jPolicyRoot.putObject("Body"); + String jBodyName = this.type + "_" + "Body"; + ObjectNode jBodyContainer = jBody.putObject(jBodyName); + + // ObjectNode jBodyContainer = mapper.createObjectNode(); + + for(String key : propBody.stringPropertyNames()) { + String value = propBody.getProperty(key); + if (logger.isDebugEnabled()) { + logger.debug("Attaching JSON field to " + jBodyName + " for " + + this.type.toString() + ":" + + this.scope + ":" + this.name + ":" + jBodyName + ":" + + " <" + key +"," + value + ">"); + } + jBodyContainer.put(key, propBody.getProperty(key)); + } + + return true; + } + + public boolean attachTextBody(JsonNode jPolicy) { + if (this.body == null) { + if (logger.isWarnEnabled()) { + logger.warn(XACMLErrorConstants.ERROR_SYSTEM_ERROR + + "- JSON Body expected but none provided from " + + this.scope + ":" + this.type + ":" + this.name); + } + return true; + } + + final ObjectMapper mapper = new ObjectMapper(); + StringWriter jsonEscapedTextWriter = new StringWriter(); + try { + mapper.writer().writeValue(jsonEscapedTextWriter, this.body); + } catch (IOException e) { + if (logger.isWarnEnabled()) { + logger.warn(XACMLErrorConstants.ERROR_SYSTEM_ERROR + + "- Text Body cannot be converted from " + + this.scope + ":" + this.type + ":" + this.name + ":" + + e.getMessage() + ":" + jsonEscapedTextWriter , e); + } + return false; + } + String jTextBody = jsonEscapedTextWriter.toString(); + + if (logger.isDebugEnabled()) + logger.debug("XML 2JSON Body conversion: " + this.scope + ":" + + this.type + ":" + this.name + ":" + jTextBody); + + ObjectNode jPolicyRoot = (ObjectNode) jPolicy; + + String jBodyName = this.type + "_" + "Body"; + ObjectNode jBodyContainer = mapper.createObjectNode(); + jBodyContainer.put(jBodyName, jTextBody); + + jPolicyRoot.set("Body", jBodyContainer); + + if (logger.isDebugEnabled()) + logger.debug("Attaching JSON to " + + this.scope + ":" + + this.type + ":" + this.name + ":" + + jBodyName + ":" + + jTextBody); + + return true; + } + + protected boolean attachBody(JsonNode jPolicy) { + if (logger.isTraceEnabled()) logger.trace("ENTER"); + + if (this.bodyType == PolicyBodyType.none) { + if (logger.isInfoEnabled()) + logger.info("No body to attach for policy " + + this.scope + "/" + this.type + "_" + this.name); + + return true; + } + + if (this.body == null || this.body.isEmpty()) { + if (logger.isWarnEnabled()) + logger.warn("No body to attach for policy " + + this.bodyType + this.type + this.scope + this.name); + + return true; + } + + switch (this.bodyType) { + case json: + return attachJsonBody(jPolicy); + case properties: + return attachPropertiesBody(jPolicy); + case xml: + return attachXmlBody(jPolicy); + case txt: + return attachTextBody(jPolicy); + case none: + default: + if (logger.isWarnEnabled()) + logger.warn("Unexpected body type: " + this.bodyType + + this.bodyType + this.type + this.scope + this.name); + return false; + } + } + + public ElkRecord record() throws JAXBException, JsonProcessingException, + IOException, IllegalArgumentException { + if (logger.isTraceEnabled()) logger.trace("ENTER"); + + Marshaller m = jaxbContext.createMarshaller(); + m.setProperty(MarshallerProperties.MEDIA_TYPE, "application/json"); + m.setProperty(MarshallerProperties.JSON_INCLUDE_ROOT, true); + m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); + m.setProperty(MarshallerProperties.JSON_REDUCE_ANY_ARRAYS, true); + m.setProperty(MarshallerProperties.JSON_MARSHAL_EMPTY_COLLECTIONS, false); + + StringWriter policyStringWriter = new StringWriter(); + m.marshal(policy, policyStringWriter); + + // add metadata to elk record + + final ObjectMapper mapper = new ObjectMapper(); + JsonNode jRoot = mapper.readTree(policyStringWriter.toString()); + JsonNode jPolicy = jRoot.path("Policy"); + if (jPolicy.isMissingNode()) { + logger.warn("Aborting: Policy root node is missing."); + throw new IllegalArgumentException("Missing policy root node"); + } + + ((ObjectNode) jPolicy).put("PolicyType", this.type.toString()); + ((ObjectNode) jPolicy).put("PolicyName", this.name); + ((ObjectNode) jPolicy).put("Owner", this.owner); + ((ObjectNode) jPolicy).put("Scope", this.scope); + + JsonNode jPolicyId = jPolicy.path("PolicyId"); + if (jPolicyId.isMissingNode()) { + logger.warn("Aborting: Policy ID node is missing."); + throw new IllegalArgumentException("Missing policy id"); + } + + if (!jPolicyId.isTextual() || !jPolicyId.isValueNode()) { + logger.warn("Aborting: Policy ID invalid."); + throw new IllegalArgumentException("Invalid policy id"); + } + + String xacmlPolicyId = jPolicyId.asText(); + String policyId = xacmlPolicyId.substring(xacmlPolicyId.lastIndexOf(":")+1); + + boolean success = attachBody(jPolicy); + + mapper.configure(SerializationFeature.INDENT_OUTPUT, true); + mapper.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING, true); + mapper.configure(SerializationFeature.WRITE_EMPTY_JSON_ARRAYS, false); + mapper.configure(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED, true); + + String recordText = mapper.writeValueAsString(jRoot); + if (logger.isDebugEnabled()) { + logger.debug("ELK Record: " + System.lineSeparator() + recordText); + } + + ElkRecord elkRecord = new ElkRecord(policyId, recordText, jRoot, success); + return elkRecord; + } + + public void store(String policyId, String record) throws IOException { + if (logger.isTraceEnabled()) logger.trace("ENTER"); + + if (this.elkDirectory != null) { + Files.createDirectories(this.elkDirectory.toPath());; + Path elkPolicyFile = Paths.get(this.elkDirectory.getPath(), policyId + ".json"); + + if (logger.isDebugEnabled()) { + logger.info("Output: " + elkPolicyFile.toAbsolutePath().toString()); + logger.info("---------------------------------------------------"); + } + + Files.write(elkPolicyFile, record.getBytes()); + } + } + + public static void main(String args[]) + throws JAXBException, IOException, CmdLineException, IllegalStateException { + + CLIOptions cliOptions = new CLIOptions(); + CmdLineParser cliParser= new CmdLineParser(cliOptions); + + try { + cliParser.parseArgument(args); + } catch (CmdLineException e) { + System.err.println("Usage: Xacml2elk"); + cliParser.printUsage(System.err); + throw e; + } + + System.out.println("---------------------------------------------------"); + System.out.println("Converting " + cliOptions.xacmlFile.getName() + " to ELK format"); + System.out.println("Metadata=" + "[type:" + cliOptions.type + + "|name:" + cliOptions.name + + "|owner:" + cliOptions.owner + + "|scope:" + cliOptions.scope + "]"); + + // generate json from jaxb input file + + Path xacmlPath = cliOptions.xacmlFile.toPath(); + if (!Files.isReadable(xacmlPath) || !Files.isRegularFile(xacmlPath)) { + System.out.println("Error: " + xacmlPath + " is invalid."); + throw new IllegalArgumentException("Error: " + xacmlPath + " is invalid."); + } + + + Xacml2Elk convertor = new Xacml2Elk(cliOptions); + ElkRecord elkRecord = convertor.record(); + System.out.println(elkRecord.record); + + Path elkOutDir = cliOptions.elkDirectory.toPath(); + if (!Files.isReadable(elkOutDir) || !Files.isDirectory(elkOutDir) || + !Files.isWritable(elkOutDir) || !Files.isExecutable(elkOutDir)) { + System.out.println("Error: " + elkOutDir.getFileName() + " is invalid."); + throw new IllegalArgumentException("Error: " + elkOutDir.getFileName() + " is invalid."); + } + + convertor.store(elkRecord.policyId, elkRecord.record); + } +} -- cgit 1.2.3-korg