/*
* File: DbXmlPolicyDataManager.java
*
* Copyright 2007 Macquarie E-Learning Centre Of Excellence
*
* 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 melcoe.xacml.pdp.data;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import melcoe.xacml.pdp.MelcoePDP;
import melcoe.xacml.util.AttributeBean;
import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import com.sleepycat.db.Environment;
import com.sleepycat.db.EnvironmentConfig;
import com.sleepycat.dbxml.XmlContainer;
import com.sleepycat.dbxml.XmlContainerConfig;
import com.sleepycat.dbxml.XmlDocument;
import com.sleepycat.dbxml.XmlDocumentConfig;
import com.sleepycat.dbxml.XmlException;
import com.sleepycat.dbxml.XmlIndexSpecification;
import com.sleepycat.dbxml.XmlManager;
import com.sleepycat.dbxml.XmlManagerConfig;
import com.sleepycat.dbxml.XmlQueryContext;
import com.sleepycat.dbxml.XmlQueryExpression;
import com.sleepycat.dbxml.XmlResults;
import com.sleepycat.dbxml.XmlTransaction;
import com.sleepycat.dbxml.XmlUpdateContext;
import com.sleepycat.dbxml.XmlValue;
import com.sun.xacml.EvaluationCtx;
import com.sun.xacml.attr.AttributeDesignator;
import com.sun.xacml.attr.AttributeValue;
import com.sun.xacml.attr.BagAttribute;
import com.sun.xacml.cond.EvaluationResult;
/**
* <p>
* DbXmlPolicyDataManager provides policy management capabilities for the
* PolicyFinder. This class implements the PolicyDataManager interface.
* </p>
* <p>
* This class is used by the PolicyFinder to produce a subset of policies for
* matching. For the PolicyFinder to match every policy would be extremely time
* consuming and inefficient, so this class uses an index and the XACML
* EvaluationCtx to generate a much smaller subset of policies that can then be
* matched with a much greater efficiency.
* </p>
* <p>
* The DbXmlPolicyDataManager is designed to handle large numbers of policies
* with extremely fast search times. This is mainly achieved through the use of
* DBXML's indexing engine which is extremely quick.
* </p>
* <p>
* DBXML is an embedded database requiring minimal to no administration. All
* indexes are created and maintained within this application.
* </p>
* <p>
* To be able to use this class there are some software and configuration
* requirements that need to be met:
* <ul>
* <li>All the requirements for installation of the MelcoePDP are met.</li>
* <li>DBXML 2.5.13+ is installed.</li>
* <li>The config-dbxml.xml configuration file located in $FEDORA_HOME/pdp/conf</li>
* </ul>
* </p>
*
* @author nishen@melcoe.mq.edu.au
*/
public class DbXmlPolicyDataManager
implements PolicyDataManager {
private static final Logger log =
Logger.getLogger(DbXmlPolicyDataManager.class.getName());
private static final String XACML20_POLICY_NS =
"urn:oasis:names:tc:xacml:2.0:policy:schema:os";
private static final String METADATA_POLICY_NS = "metadata";
private static final String XACML_RESOURCE_ID =
"urn:oasis:names:tc:xacml:1.0:resource:resource-id";
private String DB_HOME = null;
private String CONTAINER = null;
private Validator validator = null;
private Map<String, Map<String, String>> indexMap = null;
private XmlManager manager = null;
private XmlUpdateContext updateContext = null;
private XmlContainer container = null;
private Environment env = null;
private Map<String, XmlQueryExpression> queries = null;
private XmlQueryExpression[] searchQueries = null;
private long lastUpdate;
/**
* The default constructor for DbXmlPolicyDataManager. This constructor
* reads the configuration file, 'config-dbxml.xml' and initialises/creates
* the database as required based on that configuration. Any required
* indexes are automatically created.
*
* @throws PolicyDataManagerException
*/
public DbXmlPolicyDataManager()
throws PolicyDataManagerException {
initConfig();
File envHome = new File(DB_HOME);
XmlTransaction txn = null;
try {
try {
EnvironmentConfig envCfg = new EnvironmentConfig();
if (log.isDebugEnabled()) {
log.debug("Lockers: " + envCfg.getMaxLockers());
log.debug("LockObjects: " + envCfg.getMaxLockObjects());
log.debug("Locks: " + envCfg.getMaxLocks());
}
// envCfg.setRunRecovery(true);
envCfg.setAllowCreate(true);
envCfg.setInitializeCache(true);
envCfg.setInitializeLocking(true);
envCfg.setInitializeLogging(true);
envCfg.setTransactional(true);
envCfg.setMaxLockers(10000);
envCfg.setMaxLockObjects(10000);
envCfg.setMaxLocks(10000);
XmlManagerConfig managerCfg = new XmlManagerConfig();
managerCfg.setAdoptEnvironment(true);
managerCfg.setAllowExternalAccess(true);
XmlContainerConfig containerCfg = new XmlContainerConfig();
containerCfg.setAllowCreate(true);
containerCfg.setTransactional(true);
env = new Environment(envHome, envCfg);
manager = new XmlManager(env, managerCfg);
// XmlManager.setLogCategory(XmlManager.CATEGORY_NONE, true);
// XmlManager.setLogLevel(XmlManager.LEVEL_WARNING, true);
updateContext = manager.createUpdateContext();
txn = manager.createTransaction();
if (manager.existsContainer(CONTAINER) == 0) {
container =
manager.createContainer(txn,
CONTAINER,
containerCfg);
// if we just created a container we also need to add an
// index
XmlIndexSpecification is =
container.getIndexSpecification(txn);
int idxType = 0;
int syntaxType = 0;
// Add the attribute value index
idxType |= XmlIndexSpecification.PATH_EDGE;
idxType |= XmlIndexSpecification.NODE_ELEMENT;
idxType |= XmlIndexSpecification.KEY_EQUALITY;
syntaxType = XmlValue.STRING;
is.addIndex(XACML20_POLICY_NS,
"AttributeValue",
idxType,
syntaxType);
// Add the metadata default index
idxType = 0;
idxType |= XmlIndexSpecification.PATH_NODE;
idxType |= XmlIndexSpecification.NODE_METADATA;
idxType |= XmlIndexSpecification.KEY_PRESENCE;
syntaxType = XmlValue.NONE;
is.addDefaultIndex(idxType, syntaxType);
container.setIndexSpecification(txn, is, updateContext);
} else {
container =
manager.openContainer(txn, CONTAINER, containerCfg);
}
txn.commit();
log.info("Opened Container: " + CONTAINER);
queries = new HashMap<String, XmlQueryExpression>();
searchQueries = new XmlQueryExpression[10];
setLastUpdate(System.currentTimeMillis());
} catch (Exception e) {
log.fatal("Could not start database subsystem.", e);
txn.abort();
close();
throw new PolicyDataManagerException(e.getMessage(), e);
}
} catch (XmlException xe) {
throw new PolicyDataManagerException("Error aborting transaction: "
+ xe.getMessage(), xe);
}
}
/*
* (non-Javadoc)
* @see melcoe.xacml.pdp.data.PolicyDataManager#addPolicy(java.io.File)
*/
public String addPolicy(File f) throws PolicyDataManagerException {
return addPolicy(f, null);
}
/*
* (non-Javadoc)
* @see melcoe.xacml.pdp.data.PolicyDataManager#addPolicy(java.io.File,
* java.lang.String)
*/
public String addPolicy(File f, String name)
throws PolicyDataManagerException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] bytes = new byte[1024];
try {
FileInputStream fis = new FileInputStream(f);
int count = fis.read(bytes);
while (count > -1) {
out.write(bytes, 0, count);
count = fis.read(bytes);
}
} catch (IOException e) {
throw new PolicyDataManagerException("Error reading file: "
+ f.getName(), e);
}
return addPolicy(out.toString(), name);
}
/*
* (non-Javadoc)
* @see melcoe.xacml.pdp.data.PolicyDataManager#addPolicy(java.lang.String)
*/
public String addPolicy(String document) throws PolicyDataManagerException {
return addPolicy(document, null);
}
/*
* (non-Javadoc)
* @see melcoe.xacml.pdp.data.PolicyDataManager#addPolicy(java.lang.String,
* java.lang.String)
*/
public String addPolicy(String document, String name)
throws PolicyDataManagerException {
try {
if (validator != null) {
if (log.isDebugEnabled()) {
log.debug("validating document: " + name);
}
validator
.validate(new StreamSource(new ByteArrayInputStream(document
.getBytes())));
}
} catch (Exception e) {
throw new PolicyDataManagerException("Could not validate policy: "
+ name, e);
}
String docName = null;
XmlTransaction txn = null;
try {
try {
XmlDocument doc = makeDocument(name, document);
docName = doc.getName();
txn = manager.createTransaction();
container.putDocument(txn, doc, updateContext);
txn.commit();
setLastUpdate(System.currentTimeMillis());
} catch (XmlException xe) {
if (xe.getErrorCode() == XmlException.UNIQUE_ERROR) {
throw new PolicyDataManagerException("Document already exists: "
+ docName);
}
txn.abort();
throw new PolicyDataManagerException("Error adding policy: "
+ xe.getMessage(), xe);
}
} catch (XmlException xe) {
throw new PolicyDataManagerException("Error aborting transaction: "
+ xe.getMessage(), xe);
}
return docName;
}
/*
* (non-Javadoc)
* @see
* melcoe.xacml.pdp.data.PolicyDataManager#deletePolicy(java.lang.String)
*/
public boolean deletePolicy(String name) throws PolicyDataManagerException {
XmlTransaction txn = null;
try {
try {
txn = manager.createTransaction();
container.deleteDocument(txn, name, updateContext);
txn.commit();
setLastUpdate(System.currentTimeMillis());
} catch (XmlException xe) {
txn.abort();
throw new PolicyDataManagerException("Error deleting document: "
+ name,
xe);
}
} catch (XmlException xe) {
throw new PolicyDataManagerException("Error aborting transaction: "
+ xe.getMessage(), xe);
}
return true;
}
/*
* (non-Javadoc)
* @see
* melcoe.xacml.pdp.data.PolicyDataManager#updatePolicy(java.lang.String,
* java.lang.String)
*/
public boolean updatePolicy(String name, String newDocument)
throws PolicyDataManagerException {
try {
if (validator != null) {
if (log.isDebugEnabled()) {
log.debug("validating document: " + name);
}
validator
.validate(new StreamSource(new ByteArrayInputStream(newDocument
.getBytes())));
}
} catch (Exception e) {
throw new PolicyDataManagerException("Could not validate policy: "
+ name, e);
}
XmlTransaction txn = null;
try {
try {
txn = manager.createTransaction();
XmlDocument doc = makeDocument(name, newDocument);
container.updateDocument(txn, doc, updateContext);
txn.commit();
setLastUpdate(System.currentTimeMillis());
} catch (XmlException xe) {
txn.abort();
throw new PolicyDataManagerException("Error updating document: "
+ name,
xe);
}
} catch (XmlException xe) {
throw new PolicyDataManagerException("Error aborting transaction: "
+ xe.getMessage(), xe);
}
return true;
}
/*
* (non-Javadoc)
* @see melcoe.xacml.pdp.data.PolicyDataManager#getPolicy(java.lang.String)
*/
public byte[] getPolicy(String name) throws PolicyDataManagerException {
try {
XmlDocument doc = container.getDocument(name);
return doc.getContent();
} catch (XmlException xe) {
throw new PolicyDataManagerException("Error retrieving document: "
+ name, xe);
}
}
/*
* (non-Javadoc)
* @seemelcoe.xacml.pdp.data.PolicyDataManager#getPolicies(com.sun.xacml.
* EvaluationCtx)
*/
public Map<String, byte[]> getPolicies(EvaluationCtx eval)
throws PolicyDataManagerException {
long a = 0;
long b = 0;
long total = 0;
Map<String, byte[]> documents = new HashMap<String, byte[]>();
try {
// Get the query (query gets prepared if necesary)
a = System.nanoTime();
Map<String, Set<AttributeBean>> attributeMap =
getAttributeMap(eval);
XmlQueryContext context = manager.createQueryContext();
context.setDefaultCollection(CONTAINER);
context.setNamespace("p", XACML20_POLICY_NS);
context.setNamespace("m", METADATA_POLICY_NS);
// Set all the bind variables in the query context
String[] types =
new String[] {"Subject", "Resource", "Action",
"Environment"};
int resourceComponentCount = 0;
for (String t : types) {
int count = 0;
for (AttributeBean bean : attributeMap.get(t.toLowerCase()
+ "Attributes")) {
if (bean.getId().equals(XACML_RESOURCE_ID)) {
context.setVariableValue("XacmlResourceId",
new XmlValue(bean.getId()));
int c = 0;
for (String value : bean.getValues()) {
XmlValue component = new XmlValue(value);
context
.setVariableValue("XacmlResourceIdValue"
+ c, component);
if (log.isDebugEnabled()) {
log
.debug("XacmlResourceIdValue"
+ resourceComponentCount + ": "
+ value);
}
resourceComponentCount++;
c++;
}
} else {
context.setVariableValue(t + "Id" + count,
new XmlValue(bean.getId()));
if (log.isDebugEnabled()) {
log.debug(t + "Id" + count + " = '" + bean.getId()
+ "'");
}
int valueCount = 0;
for (String value : bean.getValues()) {
context.setVariableValue(t + "Id" + count
+ "-Value"
+ valueCount,
new XmlValue(value));
if (log.isDebugEnabled()) {
log.debug(t + "Id" + count + "-Value"
+ valueCount + " = '" + value + "'");
}
valueCount++;
}
count++;
}
}
}
XmlQueryExpression qe =
getQuery(attributeMap, context, resourceComponentCount);
b = System.nanoTime();
total += b - a;
if (log.isDebugEnabled()) {
log.debug("Query prep. time: " + (b - a) + "ns");
}
// execute the query
a = System.nanoTime();
XmlResults results = qe.execute(context);
b = System.nanoTime();
total += b - a;
if (log.isDebugEnabled()) {
log.debug("Query exec. time: " + (b - a) + "ns");
}
// process results
while (results.hasNext()) {
XmlValue value = results.next();
if (log.isDebugEnabled()) {
log.debug("Retrieved Document: "
+ value.asDocument().getName());
}
documents.put(value.asDocument().getName(), value.asDocument()
.getContent());
}
results.delete();
if (log.isDebugEnabled()) {
log.debug("Total exec. time: " + total + "ns");
}
} catch (XmlException xe) {
throw new PolicyDataManagerException("Error getting policies from PolicyDataManager.",
xe);
} catch (URISyntaxException use) {
throw new PolicyDataManagerException("Error building query.", use);
}
return documents;
}
/**
* Check if the policy identified by policyName exists.
*
* @param policyName
* @return true iff the policy store contains a policy identified as
* policyName
* @throws PolicyDataManagerException
*/
public boolean contains(String policyName)
throws PolicyDataManagerException {
try {
container.getDocument(policyName, new XmlDocumentConfig()
.setLazyDocs(true));
} catch (XmlException e) {
if (e.getErrorCode() == XmlException.DOCUMENT_NOT_FOUND) {
return false;
} else {
throw new PolicyDataManagerException(e.getMessage(), e);
}
}
return true;
}
/**
* Check if the policy identified by policyName exists.
*
* @param policy
* @return true iff the policy store contains a policy with the same
* PolicyId
* @throws PolicyDataManagerException
*/
public boolean contains(File policy) throws PolicyDataManagerException {
InputStream is;
String policyName;
try {
is = new FileInputStream(policy);
Map<String, String> metadata = getDocumentMetadata(is);
is.close();
policyName = metadata.get("PolicyId");
} catch (IOException e) {
throw new PolicyDataManagerException(e.getMessage(), e);
}
return contains(policyName);
}
/*
* (non-Javadoc)
* @see melcoe.xacml.pdp.data.PolicyDataManager#listPolicies()
*/
public List<String> listPolicies() throws PolicyDataManagerException {
List<String> documents = new ArrayList<String>();
XmlTransaction txn = null;
try {
try {
txn = manager.createTransaction();
XmlDocumentConfig docConf = new XmlDocumentConfig();
XmlResults results = container.getAllDocuments(txn, docConf);
while (results.hasNext()) {
XmlValue value = results.next();
documents.add(value.asDocument().getName());
}
results.delete();
txn.commit();
} catch (XmlException xe) {
txn.abort();
throw new PolicyDataManagerException(xe);
}
} catch (XmlException xe) {
throw new PolicyDataManagerException("Error aborting transaction: "
+ xe.getMessage(), xe);
}
return documents;
}
/**
* Closes the dbxml container and manager.
*/
private void close() {
try {
if (container != null) {
container.close();
container = null;
log.info("Closed container");
}
if (manager != null) {
manager.close();
manager = null;
log.info("Closed manager");
}
} catch (Exception de) {
log.warn(de.getMessage());
}
}
/**
* Obtains the metadata for the given document.
*
* @param docIS
* the document as an InputStream
* @return the document metadata as a Map
*/
private Map<String, String> getDocumentMetadata(InputStream docIS) {
Map<String, String> metadata = new HashMap<String, String>();
try {
// Create instance of DocumentBuilderFactory
DocumentBuilderFactory factory =
DocumentBuilderFactory.newInstance();
// Get the DocumentBuilder
DocumentBuilder docBuilder = factory.newDocumentBuilder();
// Create blank DOM Document and parse contents of input stream
Document doc = docBuilder.parse(docIS);
NodeList nodes = null;
metadata.put("PolicyId", doc.getDocumentElement()
.getAttribute("PolicyId"));
nodes = doc.getElementsByTagName("Subjects");
if (nodes.getLength() == 0) {
metadata.put("anySubject", "T");
}
nodes = doc.getElementsByTagName("Resources");
if (nodes.getLength() == 0) {
metadata.put("anyResource", "T");
}
nodes = doc.getElementsByTagName("Actions");
if (nodes.getLength() == 0) {
metadata.put("anyAction", "T");
}
nodes = doc.getElementsByTagName("Environments");
if (nodes.getLength() == 0) {
metadata.put("anyEnvironment", "T");
}
} catch (Exception e) {
log.error(e.getMessage());
}
return metadata;
}
/**
* Creates an instance of an XmlDocument for storage in the database.
*
* @param name
* the name of the document (policy)
* @param document
* the document data as a String
* @return the XmlDocument instance
* @throws XmlException
* @throws PolicyDataManagerException
*/
private XmlDocument makeDocument(String name, String document)
throws XmlException, PolicyDataManagerException {
Map<String, String> metadata =
getDocumentMetadata(new ByteArrayInputStream(document
.getBytes()));
XmlDocument doc = manager.createDocument();
String docName = name;
if (docName == null || "".equals(docName)) {
docName = metadata.get("PolicyId");
}
if (docName == null || "".equals(docName)) {
throw new PolicyDataManagerException("Could not extract PolicyID from document.");
}
doc.setMetaData("metadata", "PolicyId", new XmlValue(XmlValue.STRING,
docName));
doc.setContent(document);
doc.setName(docName);
String item = null;
item = metadata.get("anySubject");
if (item != null) {
doc.setMetaData("metadata",
"anySubject",
new XmlValue(XmlValue.STRING, item));
}
item = metadata.get("anyResource");
if (item != null) {
doc.setMetaData("metadata",
"anyResource",
new XmlValue(XmlValue.STRING, item));
}
item = metadata.get("anyAction");
if (item != null) {
doc.setMetaData("metadata",
"anyAction",
new XmlValue(XmlValue.STRING, item));
}
item = metadata.get("anyEnvironment");
if (item != null) {
doc.setMetaData("metadata",
"anyEnvironment",
new XmlValue(XmlValue.STRING, item));
}
return doc;
}
/**
* Either returns a query that has previously been generated, or generates a
* new one if it has not.
*
* @param attributeMap
* the Map of attributes, type and values upon which this query is
* based
* @param context
* the context for the query
* @return an XmlQueryExpression that can be executed
* @throws XmlException
*/
private XmlQueryExpression getQuery(Map<String, Set<AttributeBean>> attributeMap,
XmlQueryContext context,
int r) throws XmlException {
// The dimensions for this query.
StringBuilder sb = new StringBuilder();
for (Set<AttributeBean> attributeBeans : attributeMap.values()) {
sb.append(attributeBeans.size() + ":");
for (AttributeBean bean : attributeBeans) {
sb.append(bean.getValues().size() + "-");
}
}
// If a query of these dimensions already exists, then just return it.
String hash = sb.toString() + r;
XmlQueryExpression result = queries.get(hash);
if (result != null) {
return result;
}
// We do not have a query of those dimensions. We must make one.
String query = createQuery(attributeMap, r);
if (log.isDebugEnabled()) {
log.debug("Query [" + hash + "]:\n" + query);
}
// Once we have created a query, we can parse it and store the
// execution plan. This is an expensive operation that we do
// not want to have to do more than once for each dimension
result = manager.prepare(query, context);
queries.put(hash, result);
return result;
}
/**
* Given a set of attributes this method generates a DBXML XPath query based
* on those attributes to extract a subset of policies from the database.
*
* @param attributeMap
* the Map of Attributes from which to generate the query
* @param r
* the number of components in the resource id
* @return the query as a String
*/
private String createQuery(Map<String, Set<AttributeBean>> attributeMap,
int r) {
// The query contains these 4 sections.
String[] types =
new String[] {"Subject", "Resource", "Action", "Environment"};
int sections = 0;
StringBuilder sb = new StringBuilder();
sb.append("collection('" + CONTAINER + "')/p:Policy/p:Target[");
for (String t : types) {
if (attributeMap.get(t.toLowerCase() + "Attributes").size() == 0) {
continue;
}
if (sections > 0) {
sb.append(" and ");
}
sections++;
sb.append("((exists(dbxml:metadata('m:any" + t + "')))");
// sb.append("((not('p:" + t + "s'))");
int count = 0;
for (AttributeBean bean : attributeMap.get(t.toLowerCase()
+ "Attributes")) {
sb.append(" or ");
sb.append("(");
if (bean.getId().equals(XACML_RESOURCE_ID) && r > 0) {
sb.append("p:" + t + "s/p:" + t + "/p:" + t + "Match/");
sb.append("p:" + t + "AttributeDesignator/@AttributeId = ");
sb.append("$XacmlResourceId");
sb.append(" and ");
/*
* sb.append("p:" + t + "s/p:" + t + "/p:" + t + "Match/");
* sb.append("p:" + t + "AttributeDesignator/@DataType = ");
* sb.append("$XacmlResourceType"); sb.append(" and ");
*/
sb.append("(");
for (int i = 0; i < bean.getValues().size(); i++) {
if (i > 0) {
sb.append(" or ");
}
sb.append("p:" + t + "s/p:" + t + "/p:" + t + "Match/");
sb.append("p:AttributeValue = ");
sb.append("$XacmlResourceIdValue" + i);
}
sb.append(")");
} else {
sb.append("p:" + t + "s/p:" + t + "/p:" + t + "Match/");
sb.append("p:" + t + "AttributeDesignator/@AttributeId = ");
sb.append("$" + t + "Id" + count);
sb.append(" and ");
sb.append("(");
/*
* sb.append("p:" + t + "s/p:" + t + "/p:" + t + "Match/");
* sb.append("p:" + t + "AttributeDesignator/@DataType = ");
* sb.append("$" + t + "Type" + count); sb.append(" and ");
*/
for (int valueCount = 0; valueCount < bean.getValues()
.size(); valueCount++) {
if (valueCount > 0) {
sb.append(" or ");
}
sb.append("p:" + t + "s/p:" + t + "/p:" + t + "Match/");
sb.append("p:AttributeValue = ");
sb.append("$" + t + "Id" + count + "-Value"
+ valueCount);
}
sb.append(")");
count++;
}
sb.append(")");
}
sb.append(")");
}
sb.append("]");
return sb.toString();
}
/**
* This method extracts the attributes listed in the indexMap from the given
* evaluation context.
*
* @param eval
* the Evaluation Context from which to extract Attributes
* @return a Map of Attributes for each category (Subject, Resource, Action,
* Environment)
* @throws URISyntaxException
*/
@SuppressWarnings("unchecked")
private Map<String, Set<AttributeBean>> getAttributeMap(EvaluationCtx eval)
throws URISyntaxException {
URI defaultCategoryURI =
new URI(AttributeDesignator.SUBJECT_CATEGORY_DEFAULT);
Map<String, String> im = null;
Map<String, Set<AttributeBean>> attributeMap =
new HashMap<String, Set<AttributeBean>>();
Map<String, AttributeBean> attributeBeans = null;
im = indexMap.get("subjectAttributes");
attributeBeans = new HashMap<String, AttributeBean>();
for (String attributeId : im.keySet()) {
EvaluationResult result =
eval.getSubjectAttribute(new URI(im.get(attributeId)),
new URI(attributeId),
defaultCategoryURI);
if (result.getStatus() == null && !result.indeterminate()) {
AttributeValue attr = result.getAttributeValue();
if (attr.returnsBag()) {
Iterator<AttributeValue> i =
((BagAttribute) attr).iterator();
if (i.hasNext()) {
while (i.hasNext()) {
AttributeValue value = i.next();
String attributeType = im.get(attributeId);
AttributeBean ab = attributeBeans.get(attributeId);
if (ab == null) {
ab = new AttributeBean();
ab.setId(attributeId);
ab.setType(attributeType);
attributeBeans.put(attributeId, ab);
}
ab.addValue(value.encode());
}
}
}
}
}
attributeMap.put("subjectAttributes", new HashSet(attributeBeans
.values()));
im = indexMap.get("resourceAttributes");
attributeBeans = new HashMap<String, AttributeBean>();
for (String attributeId : im.keySet()) {
EvaluationResult result =
eval.getResourceAttribute(new URI(im.get(attributeId)),
new URI(attributeId),
null);
if (result.getStatus() == null && !result.indeterminate()) {
AttributeValue attr = result.getAttributeValue();
if (attr.returnsBag()) {
Iterator<AttributeValue> i =
((BagAttribute) attr).iterator();
if (i.hasNext()) {
while (i.hasNext()) {
AttributeValue value = i.next();
String attributeType = im.get(attributeId);
AttributeBean ab = attributeBeans.get(attributeId);
if (ab == null) {
ab = new AttributeBean();
ab.setId(attributeId);
ab.setType(attributeType);
attributeBeans.put(attributeId, ab);
}
if (attributeId.equals(XACML_RESOURCE_ID)
&& value.encode().startsWith("/")) {
String[] components =
makeComponents(value.encode());
if (components != null && components.length > 0) {
for (String c : components) {
ab.addValue(c);
}
} else {
ab.addValue(value.encode());
}
} else {
ab.addValue(value.encode());
}
}
}
}
}
}
attributeMap.put("resourceAttributes", new HashSet(attributeBeans
.values()));
im = indexMap.get("actionAttributes");
attributeBeans = new HashMap<String, AttributeBean>();
for (String attributeId : im.keySet()) {
EvaluationResult result =
eval.getActionAttribute(new URI(im.get(attributeId)),
new URI(attributeId),
null);
if (result.getStatus() == null && !result.indeterminate()) {
AttributeValue attr = result.getAttributeValue();
if (attr.returnsBag()) {
Iterator<AttributeValue> i =
((BagAttribute) attr).iterator();
if (i.hasNext()) {
while (i.hasNext()) {
AttributeValue value = i.next();
String attributeType = im.get(attributeId);
AttributeBean ab = attributeBeans.get(attributeId);
if (ab == null) {
ab = new AttributeBean();
ab.setId(attributeId);
ab.setType(attributeType);
attributeBeans.put(attributeId, ab);
}
ab.addValue(value.encode());
}
}
}
}
}
attributeMap.put("actionAttributes", new HashSet(attributeBeans
.values()));
im = indexMap.get("environmentAttributes");
attributeBeans = new HashMap<String, AttributeBean>();
for (String attributeId : im.keySet()) {
URI imAttrId = new URI(im.get(attributeId));
URI attrId = new URI(attributeId);
EvaluationResult result =
eval.getEnvironmentAttribute(imAttrId, attrId, null);
if (result.getStatus() == null && !result.indeterminate()) {
AttributeValue attr = result.getAttributeValue();
if (attr.returnsBag()) {
Iterator<AttributeValue> i =
((BagAttribute) attr).iterator();
if (i.hasNext()) {
while (i.hasNext()) {
AttributeValue value = i.next();
String attributeType = im.get(attributeId);
AttributeBean ab = attributeBeans.get(attributeId);
if (ab == null) {
ab = new AttributeBean();
ab.setId(attributeId);
ab.setType(attributeType);
attributeBeans.put(attributeId, ab);
}
ab.addValue(value.encode());
}
}
}
}
}
attributeMap.put("environmentAttributes", new HashSet(attributeBeans
.values()));
return attributeMap;
}
/**
* Reads a configuration file and initialises the instance based on that
* information.
*
* @throws PolicyDataManagerException
*/
private void initConfig() throws PolicyDataManagerException {
if (log.isDebugEnabled()) {
Runtime runtime = Runtime.getRuntime();
log.debug("Total memory: " + runtime.totalMemory() / 1024);
log.debug("Free memory: " + runtime.freeMemory() / 1024);
log.debug("Max memory: " + runtime.maxMemory() / 1024);
}
try {
String home = MelcoePDP.PDP_HOME.getAbsolutePath();
String filename = home + "/conf/config-dbxml.xml";
File f = new File(filename);
if (!f.exists()) {
throw new PolicyDataManagerException("Could not locate config file: "
+ f.getAbsolutePath());
}
log.info("Loading config file: " + f.getAbsolutePath());
DocumentBuilderFactory factory =
DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = factory.newDocumentBuilder();
Document doc = docBuilder.parse(new FileInputStream(f));
NodeList nodes = null;
// get database information
nodes =
doc.getElementsByTagName("database").item(0)
.getChildNodes();
for (int x = 0; x < nodes.getLength(); x++) {
Node node = nodes.item(x);
if (node.getNodeName().equals("directory")) {
DB_HOME =
MelcoePDP.PDP_HOME.getAbsolutePath()
+ node.getAttributes().getNamedItem("name")
.getNodeValue();
File db_home = new File(DB_HOME);
if (!db_home.exists()) {
try {
db_home.mkdirs();
} catch (Exception e) {
throw new PolicyDataManagerException("Could not create DB directory: "
+ db_home.getAbsolutePath());
}
}
if (log.isDebugEnabled()) {
log.debug("[config] " + node.getNodeName() + ": "
+ db_home.getAbsolutePath());
}
}
if (node.getNodeName().equals("container")) {
CONTAINER =
node.getAttributes().getNamedItem("name")
.getNodeValue();
File conFile = new File(DB_HOME + "/" + CONTAINER);
if (log.isDebugEnabled()) {
log.debug("[config] " + node.getNodeName() + ": "
+ conFile.getAbsolutePath());
}
}
}
// get index map information
String[] indexMapElements =
{"subjectAttributes", "resourceAttributes",
"actionAttributes", "environmentAttributes"};
indexMap = new HashMap<String, Map<String, String>>();
for (String s : indexMapElements) {
indexMap.put(s, new HashMap<String, String>());
}
nodes =
doc.getElementsByTagName("indexMap").item(0)
.getChildNodes();
for (int x = 0; x < nodes.getLength(); x++) {
Node node = nodes.item(x);
if (node.getNodeType() == Node.ELEMENT_NODE) {
if (log.isDebugEnabled()) {
log.debug("Node name: " + node.getNodeName());
}
NodeList attrs = node.getChildNodes();
for (int y = 0; y < attrs.getLength(); y++) {
Node attr = attrs.item(y);
if (attr.getNodeType() == Node.ELEMENT_NODE) {
String name =
attr.getAttributes().getNamedItem("name")
.getNodeValue();
String type =
attr.getAttributes().getNamedItem("type")
.getNodeValue();
indexMap.get(node.getNodeName()).put(name, type);
}
}
}
}
// get validation information
Node schemaConfig =
doc.getElementsByTagName("schemaConfig").item(0);
nodes = schemaConfig.getChildNodes();
if ("true".equals(schemaConfig.getAttributes()
.getNamedItem("validation").getNodeValue())) {
log.info("Initialising validation");
for (int x = 0; x < nodes.getLength(); x++) {
Node schemaNode = nodes.item(x);
if (schemaNode.getNodeType() == Node.ELEMENT_NODE) {
if (XACML20_POLICY_NS.equals(schemaNode.getAttributes()
.getNamedItem("namespace").getNodeValue())) {
if (log.isDebugEnabled()) {
log
.debug("found valid schema. Creating validator");
}
String loc =
schemaNode.getAttributes()
.getNamedItem("location")
.getNodeValue();
SchemaFactory schemaFactory =
SchemaFactory
.newInstance("http://www.w3.org/2001/XMLSchema");
Schema schema =
schemaFactory.newSchema(new URL(loc));
validator = schema.newValidator();
}
}
}
}
} catch (Exception e) {
log.fatal("Could not initialise DBXML: " + e.getMessage(), e);
throw new PolicyDataManagerException("Could not initialise DBXML: "
+ e.getMessage(), e);
}
}
/*
* (non-Javadoc)
* @see melcoe.xacml.pdp.data.PolicyDataManager#getLastUpdate()
*/
public long getLastUpdate() {
return lastUpdate;
}
/**
* @param lastUpdate
* the lastUpdate to set
*/
public void setLastUpdate(long lastUpdate) {
this.lastUpdate = lastUpdate;
}
/*
* (non-Javadoc)
* @see
* melcoe.xacml.pdp.data.PolicyDataManager#findPolicies(melcoe.xacml.util
* .AttributeBean[])
*/
public Map<String, byte[]> findPolicies(AttributeBean[] attributes)
throws PolicyDataManagerException {
if (attributes == null || attributes.length == 0) {
throw new PolicyDataManagerException("attribute array cannot be null or zero length");
}
long a, b, total = 0;
Map<String, byte[]> documents = new TreeMap<String, byte[]>();
try {
a = System.nanoTime();
XmlQueryContext context = manager.createQueryContext();
context.setDefaultCollection(CONTAINER);
context.setNamespace("p", XACML20_POLICY_NS);
context.setNamespace("m", METADATA_POLICY_NS);
for (int x = 0; attributes.length < 0; x++) {
context.setVariableValue("id" + x, new XmlValue(attributes[x]
.getId()));
// context.setVariableValue("type" + x, new
// XmlValue(attributes[x].getType()));
// context.setVariableValue("value" + x, new
// XmlValue(attributes[x].getValue()));
}
if (searchQueries[attributes.length] == null) {
StringBuilder sb = new StringBuilder();
sb.append("for $doc in ");
sb.append("collection('" + CONTAINER + "') ");
sb.append("let $value := $doc//p:AttributeValue ");
sb.append("let $id := $value/..//@AttributeId ");
sb.append("where 1 = 1 ");
for (int x = 0; x < attributes.length; x++) {
sb.append("and $value = $value" + x + " ");
sb.append("and $id = $id" + x + " ");
}
sb.append("return $doc");
searchQueries[attributes.length] =
manager.prepare(sb.toString(), context);
}
b = System.nanoTime();
total += b - a;
if (log.isDebugEnabled()) {
log.debug("Query prep. time: " + (b - a) + "ns");
}
a = System.nanoTime();
XmlResults results =
searchQueries[attributes.length].execute(context);
b = System.nanoTime();
total += b - a;
if (log.isDebugEnabled()) {
log.debug("Search exec. time: " + (b - a) + "ns");
}
a = System.nanoTime();
while (results.hasNext()) {
XmlValue value = results.next();
if (log.isDebugEnabled()) {
log.debug("Found search result: "
+ value.asDocument().getName());
}
documents.put(value.asDocument().getName(), value.asDocument()
.getContent());
}
results.delete();
b = System.nanoTime();
total += b - a;
if (log.isDebugEnabled()) {
log.debug("Result proc. time: " + (b - a) + "ns");
}
log.info("Total time: " + total + "ns");
} catch (XmlException xe) {
log.error("Exception during findPolicies: " + xe.getMessage(), xe);
throw new PolicyDataManagerException("Exception during findPolicies: "
+ xe.getMessage(),
xe);
}
return documents;
}
private String[] makeComponents(String resourceId) {
if (resourceId == null || resourceId.equals("")
|| !resourceId.startsWith("/")) {
return null;
}
List<String> components = new ArrayList<String>();
String[] parts = resourceId.split("\\/");
for (int x = 1; x < parts.length; x++) {
StringBuilder sb = new StringBuilder();
for (int y = 0; y < x; y++) {
sb.append("/");
sb.append(parts[y + 1]);
}
components.add(sb.toString());
if (x != parts.length - 1) {
components.add(sb.toString() + "/.*");
} else {
components.add(sb.toString() + "$");
}
}
return components.toArray(new String[components.size()]);
}
/*
* (non-Javadoc)
* @see java.lang.Object#finalize()
*/
@Override
protected void finalize() throws Throwable {
try {
close(); // close open files
} finally {
super.finalize();
}
}
}