package org.linkality.xacmlanalysr;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.StringReader;
import java.io.StringWriter;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.Vector;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.mindswap.pellet.jena.PelletReasonerFactory;
import org.mindswap.pellet.owlapi.Reasoner;
import org.semanticweb.owl.apibinding.OWLManager;
import org.semanticweb.owl.io.StringInputSource;
import org.semanticweb.owl.model.OWLAxiom;
import org.semanticweb.owl.model.OWLDataFactory;
import org.semanticweb.owl.model.OWLException;
import org.semanticweb.owl.model.OWLIndividual;
import org.semanticweb.owl.model.OWLOntology;
import org.semanticweb.owl.model.OWLOntologyCreationException;
import org.semanticweb.owl.model.OWLOntologyManager;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import xacmltest.semantics.IsInstanceOfFunction;
import com.clarkparsia.explanation.PelletExplanation;
import com.clarkparsia.explanation.io.manchester.ManchesterSyntaxExplanationRenderer;
import com.clarkparsia.pellet.sparqldl.jena.SparqlDLExecutionFactory;
import com.declarativa.interprolog.SWISubprocessEngine;
import com.declarativa.interprolog.TermModel;
import com.hp.hpl.jena.ontology.OntModel;
import com.hp.hpl.jena.query.Query;
import com.hp.hpl.jena.query.QueryExecution;
import com.hp.hpl.jena.query.QueryFactory;
import com.hp.hpl.jena.query.QuerySolution;
import com.hp.hpl.jena.query.ResultSet;
import com.hp.hpl.jena.query.ResultSetFormatter;
import com.hp.hpl.jena.rdf.model.ModelFactory;
import com.sun.xacml.Obligation;
import com.sun.xacml.Policy;
import com.sun.xacml.PolicySet;
import com.sun.xacml.cond.FunctionFactory;
import com.sun.xacml.cond.FunctionFactoryProxy;
import com.sun.xacml.cond.StandardFunctionFactory;
import com.sun.xacml.finder.AttributeFinder;
import com.sun.xacml.finder.AttributeFinderModule;
import com.sun.xacml.finder.PolicyFinder;
import com.sun.xacml.finder.impl.CurrentEnvModule;
import com.sun.xacml.finder.impl.SelectorModule;
import com.sun.xacml.support.finder.URLPolicyFinderModule;
/**
* A very basic implementation of a XACML Policy Decision Point (PDP).
* <p>
* This PDP can answer policy requests in XACML 2.0 format, based on policies
* which have been added before. Additionally, this PDP supports a non-standard
* function <i>is-instance-of</i> which is checked against an ontology by using
* the Jena reasoner.
*
* @author Julian Schuette
*
*/
public class SemanticPolicyAnalyser {
private final String NS = "http://hydra.eu.com/sit/xacml#";
// The original XACML file
private String xacmlFileName;
// The policy, parsed as an XACML string
private String xacmlPolicy;
// The policy, converted to OWL
private String owlPolicy = null;
// The policy, converted to Prolog
private String prologPolicy = null;
// The policy as an object
private PolicySet policySet = null;
private Policy policy = null;
private SWISubprocessEngine engine = null;
private URI id;
private URI combiningAlgo;
private String description;
private int xacmlVersion;
private static SemanticPolicyAnalyser myInstance;
public static SemanticPolicyAnalyser createInstance(String fileName)
throws Exception {
myInstance = new SemanticPolicyAnalyser();
myInstance.init(fileName);
return myInstance;
}
public static SemanticPolicyAnalyser getInstance() throws Exception {
if (myInstance == null) {
throw new Exception("Must call createInstance() first.");
}
return myInstance;
}
private void init(String fileName) throws Exception {
this.xacmlFileName = fileName;
// Configure PDP to use these policy files:
this.xacmlPolicy = Utils.readFileAsString(new File(fileName));
CurrentEnvModule envAttributeModule = new CurrentEnvModule();
SelectorModule selectorAttributeModule = new SelectorModule();
// Setup the AttributeFinder just like we setup the PolicyFinder.
AttributeFinder attributeFinder = new AttributeFinder();
AttributeFinderModule sampleAttributeFinder = new SampleAttrFinderModule();
List attributeModules = new ArrayList();
attributeModules.add(envAttributeModule);
attributeModules.add(sampleAttributeFinder);
attributeModules.add(selectorAttributeModule);
attributeFinder.setModules(attributeModules);
// Load the customized is-instance-of function.
FunctionFactoryProxy proxy = StandardFunctionFactory.getNewFactoryProxy();
FunctionFactory factory = proxy.getTargetFactory();
factory.addFunction(new IsInstanceOfFunction("urn:oasis:names:tc:xacml:1.0:function:is-instance-of"));
FunctionFactory.setDefaultFactory(proxy);
// Setup the PolicyFinder
URLPolicyFinderModule urlFinder = new URLPolicyFinderModule();
PolicyFinder policyFinder = new PolicyFinder();
Set<URLPolicyFinderModule> policyModules = new HashSet<URLPolicyFinderModule>();
policyModules.add(urlFinder);
policyFinder.setModules(policyModules);
// Load policy to OWL and Prolog
initOWL(new ByteArrayInputStream(xacmlPolicy.getBytes()), policyFinder);
initProlog();
}
private void initProlog() throws FileNotFoundException, IOException {
// Initialise the Prolog engine
Properties props = new Properties();
props.load(new FileInputStream(new File("policy_analyser.properties")));
System.setProperty("JAVA_BIN", props.getProperty("JAVA_BIN"));
System.setProperty("SWI_BIN_DIRECTORY", props.getProperty("SWI_BIN_DIRECTORY"));
engine = new SWISubprocessEngine();
engine.consultAbsolute(new File("prolog/policy_analysis.pl"));
// Load the converted policy and convert
new File("xacml.pl").delete();
System.out.println();
SWISubprocessEngine engine = new SWISubprocessEngine();
boolean success = engine.consultAbsolute(new File("prolog/policy_analysis.pl"));
success = engine.deterministicGoal("go_thea('xacml_gen.owl','xacml.pl', dlp)");
System.out.println(success);
System.out.println("Converted to Prolog!");
// Apply post-processor
PrologPostProcessor post = PrologPostProcessor.getInstance();
post.execute(new File("xacml.pl"));
success = engine.consultAbsolute(new File("xacml.pl"));
System.out.println("Loaded Prolog");
this.prologPolicy = Utils.readFileAsString(new File("xacml.pl"));
}
/**
* Explains how a policy effect can happen.
* <p>
* This method returns a list of Strings representing a Prolog list of
* <code>effect</code>, <code>resource</code>, <code>action</code>,
* <code>subject</code>, <code>environment</code>.
*
* @param subject
* @param action
* @param resource
* @param environment
* @param effect
* @return
*/
public String[] getExplanations(String subject, String action,
String resource, String environment, String effect) {
System.out.println("Getting explanations using Prolog");
String query = "getExplanation(" + effect + "," + resource + ", "
+ action + ", " + subject + ", " + environment + ", Result)";
System.out.println(query);
// TODO Warum muss hier nochmal consult aufgerufen werden? Sollte schon
// in initProlog() vorhanden sein?
engine.consultAbsolute(new File("xacml.pl"));
Object[] prologResults = engine.deterministicGoal(query, null);
System.out.println("done");
System.out.println(prologResults.length);
// The result will look like this:
// getExplanation(hydra#deny,main-door,open,Var2,Var3,[[hydra#TimeRangePolicy,hydra#DenyAllOthers,hydra#deny,main-door,open,hydra#ANY,hydra#ANY],[hydra#TimeRangePolicy,hydra#NoHumans,hydra#deny,main-door,open,hydra#ANY,hydra#ANY]])
// Get the result array
String resultString = ((TermModel) prologResults[0]).toString();
resultString = resultString.substring(resultString.indexOf('[') + 2, resultString.lastIndexOf(']') - 1);
System.out.println(resultString);
String[] result = resultString.split("],\\[");
return result;
}
/**
* Returns all attributes which <i>might</i> be required to evaluate a request.
* <p>
* @param subject
* @param action
* @param resource
* @param environment
* @param effect
* @return
*/
public String[] getRequiredAttributes(String subject, String action,
String resource, String environment, String effect) {
//FIXME Add variables for target, include in query.
String[] result = null;
// First create a Jena ontology model backed by the Pellet reasoner
// (note, the Pellet reasoner is required)
OntModel m = ModelFactory.createOntologyModel( PelletReasonerFactory.THE_SPEC );
// Then read the data from the file into the ontology model
m.read( new StringReader(this.owlPolicy),"hydra" );
System.out.println("Size " + m.size());
Map prefixes = m.getNsPrefixMap();
Set prefixKeys = prefixes.keySet();
Iterator it = prefixKeys.iterator();
while (it.hasNext()) {
String key = (String) it.next();
System.out.print(key + " ");
System.out.println(prefixes.get(key));
}
try {
Query q = QueryFactory.create( "PREFIX hydra: <http://www.hydramiddleware.eu/xacml#> \r\n" +
"PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> \r\n" +
"SELECT ?conditionElement ?value WHERE {" +
"?conditionElement rdf:type hydra:Attribute." +
"?conditionElement hydra:hasAttributeId ?value." +
"}" );
// Create a SPARQL-DL query execution for the given query and
// ontology model
QueryExecution qe = SparqlDLExecutionFactory.create( q, m );
// We want to execute a SELECT query, do it, and return the result set
ResultSet rs = qe.execSelect();
// ResultSetFormatter.out(rs);
// List resultList = ResultSetFormatter.toList(rs);
Vector<String> results = new Vector<String>();
// Iterator ite = resultList.iterator();
while (rs.hasNext()) {
// System.out.println("y: "+ite.next());
QuerySolution solution = rs.nextSolution();
String attributeResult = solution.get("value").toString();
attributeResult = attributeResult.substring(0,attributeResult.indexOf('^'));
results.add(attributeResult);
System.out.println("x: " + attributeResult);
}
result = new String[results.size()];
for (int i=0;i<results.size();i++) {
result[i] = results.get(i);
}
// Print the query for better understanding
System.out.println(q.toString());
} catch (Throwable t) {
t.printStackTrace();
}
System.out.println("Returning " + result.length);
return result;
}
/**
* Returns the policy in OWL format.
*
* @return the OWL representation as a String.
*/
public String getAsOWL() {
return this.owlPolicy;
}
/**
* Returns the policy in Prolog format.
*
* @return the Prolog representation as a String.
*/
public String getAsProlog() {
return this.prologPolicy;
}
/**
* Checks the consistency of the policy. This method confirms that the
* policy, converted to OWL, corresponds to the pre-defined T-box.
*
* @return true if consistency check passed, false otherwise.
*/
public String checkConsistency() throws OWLOntologyCreationException,
OWLException, IOException {
PelletExplanation.setup();
// The renderer is used to pretty print explanation
ManchesterSyntaxExplanationRenderer renderer = new ManchesterSyntaxExplanationRenderer();
// The writer used for the explanation rendered
ByteArrayOutputStream bos = new ByteArrayOutputStream();
PrintWriter out = new PrintWriter(bos);
renderer.startRendering(out);
// Create an OWLAPI manager that allows to load an ontology file and
// create OWLEntities
OWLOntologyManager manager = OWLManager.createOWLOntologyManager();
OWLOntology ontology = manager.loadOntology(new StringInputSource(this.owlPolicy));
OWLDataFactory factory = manager.getOWLDataFactory();
// Create the reasoner and load the ontology
Reasoner reasoner = new Reasoner(manager);
reasoner.loadOntology(ontology);
// Create an explanation generator
PelletExplanation expGen = new PelletExplanation(reasoner);
// Explain unsatisfiable concepts
Set<Set<OWLAxiom>> exp = expGen.getInconsistencyExplanations();
OWLIndividual deny = factory.getOWLIndividual(URI.create(NS + "deny"));
// Explain why mad cow is an unsatisfiable concept
System.out.println("Xaim:" + exp.toString());
System.out.println("Running");
System.out.println("Test: " + new String(bos.toByteArray()));
String allExplanationsString = "";
Iterator<Set<OWLAxiom>> it = exp.iterator();
while (it.hasNext()) {
Set<OWLAxiom> explanation = it.next();
Iterator<OWLAxiom> et = explanation.iterator();
String explanationString = "";
while (et.hasNext()) {
OWLAxiom axiom = et.next();
explanationString = explanationString + ", " + axiom.toString();
}
allExplanationsString += "\n" + explanationString;
}
String result = "";
result = allExplanationsString;
System.out.println("consistency check counter-proof: " + result);
renderer.endRendering();
// return (exp.isEmpty());
if (exp.isEmpty()) {
result = "true";
} else {
renderer.render(exp);
result = new String(bos.toByteArray());
}
renderer.endRendering();
return result;
}
/**
* Main method for testing purposes.
*
* @param args
* No arguments required.
*/
public static void main(String[] args) {
try {
System.out.println("Creating analyser");
SemanticPolicyAnalyser analyser = SemanticPolicyAnalyser.createInstance("policies/policy.xml");
System.out.println("Checking for consistency");
analyser.checkConsistency();
System.out.println(analyser.getAsOWL());
System.out.println("Testing getTargets:");
Vector<String> targets = analyser.getPolicyTargets();
for (int i = 0; i < targets.size(); i++)
System.out.println(targets.get(i));
} catch (Exception e) {
e.printStackTrace();
}
}
public static String encodePrologLiteral(String literal) {
if (!literal.equals("_")
&& literal.substring(0, 1).toLowerCase().equals(literal.substring(0, 1))) {
literal = "'" + literal + "'";
}
return literal;
}
/**
* Returns all targets of the policy.
*
* @return
*/
public Vector<String> getPolicyTargets() {
if (policy == null) {
return null;
}
//TODO code works but is cumbersome. Revise.
Vector<String> targets = new Vector<String>();
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setIgnoringComments(true);
factory.setNamespaceAware(true);
factory.setValidating(false);
// create a builder based on the factory & try to load the policy
DocumentBuilder db = factory.newDocumentBuilder();
String action = "";
String resource = "";
String subject = "";
String environment = "";
ByteArrayOutputStream bos = new ByteArrayOutputStream();
policy.getTarget().getActionsSection().encode(bos);
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
Document doc = db.parse(bis);
NodeList attributeValues = doc.getElementsByTagName("AttributeValue");
for (int i = 0; i < attributeValues.getLength(); i++) {
Node nod = attributeValues.item(i);
if (nod != null)
action = nod.getTextContent();
else
action = "_";
bos = new ByteArrayOutputStream();
policy.getTarget().getResourcesSection().encode(bos);
bis = new ByteArrayInputStream(bos.toByteArray());
doc = db.parse(bis);
nod = doc.getElementsByTagName("AttributeValue").item(0);
if (nod != null)
resource = nod.getTextContent();
else
resource = "_";
bos = new ByteArrayOutputStream();
policy.getTarget().getSubjectsSection().encode(bos);
bis = new ByteArrayInputStream(bos.toByteArray());
doc = db.parse(bis);
nod = doc.getElementsByTagName("AttributeValue").item(0);
if (nod != null)
subject = nod.getTextContent();
else
subject = "_";
bos = new ByteArrayOutputStream();
policy.getTarget().getEnvironmentsSection().encode(bos);
bis = new ByteArrayInputStream(bos.toByteArray());
doc = db.parse(bis);
nod = doc.getElementsByTagName("AttributeValue").item(0);
if (nod != null)
environment = nod.getTextContent();
else
environment = "_";
targets.add(environment + "," + subject + "," + action + ","
+ resource);
}
} catch (Exception e) {
e.printStackTrace();
}
return targets;
}
private void initOWL(InputStream is, PolicyFinder finder) {
// Shortcut: return immediately if already available
if (this.owlPolicy != null) {
return;
}
try {
// create the factory
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setIgnoringComments(true);
factory.setNamespaceAware(true);
factory.setValidating(false);
// create a builder based on the factory & try to load the policy
DocumentBuilder db = factory.newDocumentBuilder();
Document doc = db.parse(is);
// handle the policy, if it's a known type
Element root = doc.getDocumentElement();
String name = root.getLocalName();
Policy pol = null;
if (name.equals("Policy")) {
policy = (Policy) Policy.getInstance(root);
pol = policy;
} else if (name.equals("PolicySet")) {
policySet = PolicySet.getInstance(root, finder);
}
id = policy.getId();
combiningAlgo = policy.getCombiningAlg().getIdentifier();
xacmlVersion = policy.getMetaData().getXACMLVersion();
description = policy.getDescription();
// TODO handle policy recursively and add policy set
Document mainOWLDoc = db.newDocument();
// Element policyElementOWL = mainOWLDoc.createElement("Policy");
Element policyElementOWL = policy.toOWLNode(mainOWLDoc);
policyElementOWL.setAttribute("rdf:ID", pol.getId().toASCIIString());
// Element ruleCombiningAlgo =
// mainOWLDoc.createElement("hasRuleCombiningAlgorithm");
// ruleCombiningAlgo.setAttribute("rdf:resource",
// pol.getCombiningAlg().getIdentifier().toString());
// policyElementOWL.appendChild(ruleCombiningAlgo);
Iterator<Obligation> it = pol.getObligations().iterator();
while (it.hasNext()) {
Element obligation = mainOWLDoc.createElement("hasObligation");
obligation.setAttribute("rdf:resource", it.next().toString());
policyElementOWL.appendChild(obligation);
}
mainOWLDoc.appendChild(policyElementOWL);
String templ = Utils.readFileAsString(new File("XACMLOWL.template"));
String owl = templ.replace("[XACML]", toString(mainOWLDoc).replace("<?xml version=\"1.0\" encoding=\"UTF-8\"?>", ""));
FileWriter fw = new FileWriter("xacml_gen.owl");
fw.write(owl);
fw.close();
this.owlPolicy = owl;
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Convert XML DOM document to a string.
*
* @param document
* XML DOM document
* @return XML string
* @throws TransformerException
*/
public static String toString(Document document)
throws TransformerException {
StringWriter stringWriter = new StringWriter();
StreamResult streamResult = new StreamResult(stringWriter);
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
transformer.setOutputProperty(OutputKeys.METHOD, "xml");
transformer.transform(new DOMSource(document.getDocumentElement()), streamResult);
return stringWriter.toString();
}
public Properties getInfo() {
Properties props = new Properties();
props.put("id", this.id);
props.put("combiningAlgo", this.combiningAlgo);
props.put("filename", this.xacmlFileName);
props.put("version", this.xacmlVersion);
return props;
}
}