/*******************************************************************************
* Open Behavioral Health Information Technology Architecture (OBHITA.org)
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the <organization> nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package gov.samhsa.acs.contexthandler;
import static gov.samhsa.acs.audit.AcsAuditVerb.DEPLOY_POLICY;
import static gov.samhsa.acs.audit.AcsPredicateKey.XACML_POLICY;
import static gov.samhsa.acs.audit.AcsPredicateKey.XACML_POLICY_ID;
import gov.samhsa.acs.audit.AuditService;
import gov.samhsa.acs.audit.PredicateKey;
import gov.samhsa.acs.common.dto.PdpRequestResponse;
import gov.samhsa.acs.common.dto.XacmlRequest;
import gov.samhsa.acs.common.dto.XacmlResponse;
import gov.samhsa.acs.common.tool.DocumentAccessor;
import gov.samhsa.acs.common.tool.DocumentXmlConverter;
import gov.samhsa.acs.common.tool.exception.DocumentAccessorException;
import gov.samhsa.acs.common.tool.exception.DocumentXmlConverterException;
import gov.samhsa.acs.contexthandler.exception.NoPolicyFoundException;
import gov.samhsa.acs.contexthandler.exception.PolicyProviderException;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.herasaf.xacml.core.WritingException;
import org.herasaf.xacml.core.api.PDP;
import org.herasaf.xacml.core.api.PolicyRepository;
import org.herasaf.xacml.core.api.PolicyRetrievalPoint;
import org.herasaf.xacml.core.api.UnorderedPolicyRepository;
import org.herasaf.xacml.core.context.impl.RequestType;
import org.herasaf.xacml.core.context.impl.ResponseType;
import org.herasaf.xacml.core.context.impl.ResultType;
import org.herasaf.xacml.core.policy.Evaluatable;
import org.herasaf.xacml.core.policy.EvaluatableID;
import org.herasaf.xacml.core.policy.PolicyMarshaller;
import org.herasaf.xacml.core.policy.impl.AttributeAssignmentType;
import org.herasaf.xacml.core.policy.impl.ObligationType;
import org.herasaf.xacml.core.simplePDP.SimplePDPFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import ch.qos.logback.audit.AuditException;
/**
* The Class PolicyDecisionPointImpl.
*/
public class PolicyDecisionPointImpl implements PolicyDecisionPoint {
/** The logger. */
private final Logger logger = LoggerFactory
.getLogger(PolicyDecisionPointImpl.class);
/** The policy provider. */
private final PolicyProvider policyProvider;
/** The request generator. */
private final RequestGenerator requestGenerator;
/** The document accessor. */
private final DocumentAccessor documentAccessor;
/** The document xml converter. */
private final DocumentXmlConverter documentXmlConverter;
/** The audit service. */
private final AuditService auditService;
/**
* Instantiates a new policy decision point impl.
*
* @param policyProvider
* the policy provider
* @param requestGenerator
* the request generator
* @param documentAccessor
* the document accessor
* @param documentXmlConverter
* the document xml converter
* @param auditService
* the audit service
*/
public PolicyDecisionPointImpl(PolicyProvider policyProvider,
RequestGenerator requestGenerator,
DocumentAccessor documentAccessor,
DocumentXmlConverter documentXmlConverter, AuditService auditService) {
super();
this.policyProvider = policyProvider;
this.requestGenerator = requestGenerator;
this.documentAccessor = documentAccessor;
this.documentXmlConverter = documentXmlConverter;
this.auditService = auditService;
// to initialize herasaf context
getSimplePDP();
}
/**
* Instantiates a new policy decision point impl.
*
* @param requestGenerator
* the request generator
* @param documentAccessor
* the document accessor
* @param documentXmlConverter
* the document xml converter
*/
public PolicyDecisionPointImpl(RequestGenerator requestGenerator,
DocumentAccessor documentAccessor,
DocumentXmlConverter documentXmlConverter) {
super();
this.policyProvider = null;
this.requestGenerator = requestGenerator;
this.documentAccessor = documentAccessor;
this.documentXmlConverter = documentXmlConverter;
this.auditService = null;
// to initialize herasaf context
getSimplePDP();
}
/*
* (non-Javadoc)
*
* @see
* gov.samhsa.acs.contexthandler.PolicyDecisionPoint#evaluatePolicyForTrying
* (java.lang.String)
*/
@Override
public synchronized PdpRequestResponse evaluatePolicyForTrying(
String xacmlPolicy, String purposeOfUse) {
Assert.hasText(xacmlPolicy, "Xacml policy is not set");
Node resourceNode = null;
NodeList senderNodeList = null;
NodeList recipientNodeList = null;
try {
final Document xmlDoc = documentXmlConverter
.loadDocument(xacmlPolicy);
// XPath ignores namespaces by using local-name() XPath function
resourceNode = documentAccessor
.getNode(
xmlDoc,
"//*[local-name() = 'ResourceAttributeDesignator'][@AttributeId='urn:oasis:names:tc:xacml:1.0:resource:resource-id']/ancestor::*[local-name() = 'ResourceMatch'][1]/*[local-name() = 'AttributeValue']");
senderNodeList = documentAccessor
.getNodeList(
xmlDoc,
"//*[local-name() = 'SubjectAttributeDesignator'][@AttributeId='urn:oasis:names:tc:xacml:1.0:subject-category:intermediary-subject']/../../*[local-name() = 'AttributeValue']");
recipientNodeList = documentAccessor
.getNodeList(
xmlDoc,
"//*[local-name() = 'SubjectAttributeDesignator'][@AttributeId='urn:oasis:names:tc:xacml:1.0:subject-category:recipient-subject']/../../*[local-name() = 'AttributeValue']");
final NodeList currentDateNodeList = documentAccessor
.getNodeList(
xmlDoc,
"//*[local-name() = 'EnvironmentAttributeDesignator'][@AttributeId='urn:oasis:names:tc:xacml:1.0:environment:current-dateTime']/../..");
// Remove environment:current-dateTime nodes
for (int i = 0; i < currentDateNodeList.getLength(); i++) {
final Node node = currentDateNodeList.item(i);
node.getParentNode().removeChild(node);
}
// Get the updated policy after removing
// environment:current-dateTime nodes
xacmlPolicy = documentXmlConverter.convertXmlDocToString(xmlDoc);
} catch (final Exception e) {
logger.error(
"Exception occured when trying to query and manipulate xaml policy string",
e);
}
// Create xacmlRequest
final XacmlRequest xacmlRequest = new XacmlRequest();
final String resourceId = resourceNode.getTextContent();
xacmlRequest.setPatientId(resourceId);
final String intermediarySubjectNPI = senderNodeList.item(0)
.getTextContent();
xacmlRequest.setIntermediarySubjectNPI(intermediarySubjectNPI);
final String recipientSubjectNPI = recipientNodeList.item(0)
.getTextContent();
xacmlRequest.setRecipientSubjectNPI(recipientSubjectNPI);
xacmlRequest.setPurposeOfUse(purposeOfUse);
final RequestType request = requestGenerator.generateRequest(
xacmlRequest.getRecipientSubjectNPI(),
xacmlRequest.getIntermediarySubjectNPI(),
xacmlRequest.getPurposeOfUse(), xacmlRequest.getPatientId());
final InputSource source = new InputSource(
new StringReader(xacmlPolicy));
Evaluatable policy = null;
try {
policy = PolicyMarshaller.unmarshal(source);
} catch (final Exception e) {
logger.error(
"Exception occured when trying to unmarshal xaml policy to be used by PDP engine",
e);
}
final List<Evaluatable> policies = new ArrayList<Evaluatable>();
policies.add(policy);
final XacmlResponse xacmlResponse = managePoliciesAndEvaluateRequest(
policies, xacmlRequest, request);
final PdpRequestResponse pdpRequestResponse = new PdpRequestResponse();
pdpRequestResponse.setXacmlRequest(xacmlRequest);
pdpRequestResponse.setXacmlResponse(xacmlResponse);
return pdpRequestResponse;
}
/*
* (non-Javadoc)
*
* @see
* gov.samhsa.acs.contexthandler.PolicyDecisionPoint#evaluateRequest(org
* .herasaf.xacml.core.api.PDP,
* org.herasaf.xacml.core.context.impl.RequestType, java.util.List)
*/
@Override
@Deprecated
public XacmlResponse evaluateRequest(PDP pdp, RequestType request,
List<Evaluatable> policies, XacmlRequest xacmlRequest)
throws AuditException {
logger.info("evaluateRequest invoked");
return managePoliciesAndEvaluateRequest(pdp, request, policies,
xacmlRequest);
}
/*
* (non-Javadoc)
*
* @see
* gov.samhsa.acs.contexthandler.PolicyDecisionPoint#evaluateRequest(org
* .herasaf.xacml.core.api.PDP,
* org.herasaf.xacml.core.context.impl.RequestType, java.lang.String,
* java.lang.String, java.lang.String)
*/
@Override
@Deprecated
public XacmlResponse evaluateRequest(PDP pdp, RequestType request,
XacmlRequest xacmlRequest) throws AuditException,
NoPolicyFoundException, PolicyProviderException {
logger.info("evaluateRequest invoked");
final List<Evaluatable> deployedPolicies = deployPolicies(pdp,
xacmlRequest);
return managePoliciesAndEvaluateRequest(pdp, request, deployedPolicies,
xacmlRequest);
}
/*
* (non-Javadoc)
*
* @see
* gov.samhsa.acs.contexthandler.PolicyDecisionPoint#evaluateRequest(org
* .herasaf.xacml.core.context.impl.RequestType, java.util.List)
*/
@Override
@Deprecated
public XacmlResponse evaluateRequest(RequestType request,
List<Evaluatable> policies, XacmlRequest xacmlRequest)
throws AuditException {
logger.info("evaluateRequest invoked");
return managePoliciesAndEvaluateRequest(getSimplePDP(), request,
policies, xacmlRequest);
}
/*
* (non-Javadoc)
*
* @see
* gov.samhsa.acs.contexthandler.PolicyDecisionPoint#evaluateRequest(org
* .herasaf.xacml.core.context.impl.RequestType, java.lang.String,
* java.lang.String, java.lang.String)
*/
@Override
public XacmlResponse evaluateRequest(RequestType request,
XacmlRequest xacmlRequest) throws AuditException,
NoPolicyFoundException, PolicyProviderException {
logger.info("evaluateRequest invoked");
return managePoliciesAndEvaluateRequest(request, xacmlRequest);
}
/*
* (non-Javadoc)
*
* @see
* gov.samhsa.acs.contexthandler.PolicyDecisionPoint#evaluateRequest(gov
* .samhsa.acs.common.dto.XacmlRequest)
*/
@Override
public XacmlResponse evaluateRequest(XacmlRequest xacmlRequest)
throws AuditException, NoPolicyFoundException,
PolicyProviderException {
logger.info("evaluateRequest invoked");
final RequestType request = requestGenerator.generateRequest(
xacmlRequest.getRecipientSubjectNPI(),
xacmlRequest.getIntermediarySubjectNPI(),
xacmlRequest.getPurposeOfUse(), xacmlRequest.getPatientId());
return managePoliciesAndEvaluateRequest(request, xacmlRequest);
}
/**
* Deploy policies.
*
* @param pdp
* the pdp
* @param policies
* the policies
* @param xacmlRequest
* the xacml request
* @param isAudited
* the is audited
* @throws AuditException
* the audit exception
*/
void deployPolicies(PDP pdp, List<Evaluatable> policies,
XacmlRequest xacmlRequest, boolean isAudited) throws AuditException {
try {
final PolicyRetrievalPoint repo = pdp.getPolicyRepository();
final UnorderedPolicyRepository repository = (UnorderedPolicyRepository) repo;
repository.deploy(policies);
if (isAudited) {
for (final Evaluatable policy : policies) {
auditPolicy(policy, xacmlRequest);
}
}
} catch (AuditException | WritingException | IOException
| DocumentAccessorException | DocumentXmlConverterException e) {
// TODO (BU): ADD ERROR LOG
undeployAllPolicies(pdp);
throw new AuditException(e.getMessage(), e);
}
}
/**
* Gets all xacml policies of a patient unique id.
*
* @param xacmlRequest
* the xacml request
* @return the policies
* @throws NoPolicyFoundException
* the no policy found exception
* @throws PolicyProviderException
* the policy provider exception
*/
List<Evaluatable> getPolicies(XacmlRequest xacmlRequest)
throws NoPolicyFoundException, PolicyProviderException {
return policyProvider.getPolicies(xacmlRequest);
}
/**
* Gets the simple pdp.
*
* @return the simple pdp
*/
PDP getSimplePDP() {
return SimplePDPFactory.getSimplePDP();
}
/**
* Undeploy multiple policies on the policy repository.
*
* @param pdp
* the pdp
* @param policyIds
* the policy ids
*/
void undeployPoliciesById(PDP pdp, List<EvaluatableID> policyIds) {
final PolicyRepository repo = (PolicyRepository) pdp
.getPolicyRepository();
repo.undeploy(policyIds);
}
/**
* Undeploy multiple policies on the policy repository.
*
* @param pdp
* the pdp
* @param policy
* the policy
*/
void undeployPolicy(PDP pdp, Evaluatable policy) {
final PolicyRepository repo = (PolicyRepository) pdp
.getPolicyRepository();
repo.undeploy(policy.getId());
}
/**
* Audit policy.
*
* @param policy
* the policy
* @param xacmlRequest
* the xacml request
* @throws WritingException
* the writing exception
* @throws IOException
* Signals that an I/O exception has occurred.
* @throws DocumentAccessorException
* the document accessor exception
* @throws AuditException
* the audit exception
*/
private void auditPolicy(Evaluatable policy, XacmlRequest xacmlRequest)
throws WritingException, IOException, DocumentAccessorException,
AuditException {
final StringWriter writer = new StringWriter();
PolicyMarshaller.marshal(policy, writer);
final Map<PredicateKey, String> predicateMap = auditService
.createPredicateMap();
final String policyString = writer.toString();
writer.close();
final NodeList policyIdNodeList = documentAccessor.getNodeList(
documentXmlConverter.loadDocument(policyString), "//@PolicyId");
Set<String> policyIdSet = null;
if (policyIdNodeList.getLength() > 0) {
policyIdSet = new HashSet<String>();
for (int i = 0; i < policyIdNodeList.getLength(); i++) {
policyIdSet.add(policyIdNodeList.item(i).getNodeValue());
}
}
predicateMap.put(XACML_POLICY, policyString);
if (policyIdSet != null) {
predicateMap.put(XACML_POLICY_ID, policyIdSet.toString());
}
auditService.audit(this, xacmlRequest.getMessageId(), DEPLOY_POLICY,
xacmlRequest.getPatientId(), predicateMap);
}
/**
* Deploy all policies of a patient unique id.
*
* @param pdp
* the pdp
* @param xacmlRequest
* the xacml request
* @return the list
* @throws AuditException
* the audit exception
* @throws NoPolicyFoundException
* the no policy found exception
* @throws PolicyProviderException
* the policy provider exception
*/
private List<Evaluatable> deployPolicies(PDP pdp, XacmlRequest xacmlRequest)
throws AuditException, NoPolicyFoundException,
PolicyProviderException {
final List<Evaluatable> deployedPolicies = getPolicies(xacmlRequest);
deployPolicies(pdp, deployedPolicies, xacmlRequest, true);
return deployedPolicies;
}
/**
* Evaluate request.
*
* @param simplePDP
* the simple pdp
* @param request
* the request
* @return the xacml response
*/
private XacmlResponse evaluateRequest(PDP simplePDP, RequestType request) {
final XacmlResponse xacmlResponse = new XacmlResponse();
final ResponseType response = simplePDP.evaluate(request);
for (final ResultType r : response.getResults()) {
logger.debug("PDP Decision: " + r.getDecision().toString());
xacmlResponse.setPdpDecision(r.getDecision().toString());
if (r.getObligations() != null) {
final List<String> obligations = new LinkedList<String>();
for (final ObligationType o : r.getObligations()
.getObligations()) {
for (final AttributeAssignmentType a : o
.getAttributeAssignments()) {
for (final Object c : a.getContent()) {
logger.debug("With Obligation: " + c);
obligations.add(c.toString());
}
}
}
xacmlResponse.setPdpObligation(obligations);
}
}
logger.debug("xacmlResponse.pdpDecision: "
+ xacmlResponse.getPdpDecision());
logger.debug("xacmlResponse is ready!");
return xacmlResponse;
}
/**
* Manage policies and evaluate request.
*
* @param policies
* the policies
* @param xacmlRequest
* the xacml request
* @param request
* the request
* @return the xacml response
*/
private synchronized XacmlResponse managePoliciesAndEvaluateRequest(
List<Evaluatable> policies, XacmlRequest xacmlRequest,
RequestType request) {
final PDP pdp = getSimplePDP();
try {
deployPolicies(pdp, policies, xacmlRequest, false);
} catch (final AuditException e) {
// it shouldn't throw AuditException when isAudited=false
}
final XacmlResponse xacmlResponse = evaluateRequest(pdp, request);
undeployAllPolicies(pdp);
return xacmlResponse;
}
/**
* Manage policies and evaluate request.
*
* @param pdp
* the pdp
* @param request
* the request
* @return the xacml response
*/
private XacmlResponse managePoliciesAndEvaluateRequest(PDP pdp,
RequestType request) {
final XacmlResponse xacmlResponse = evaluateRequest(pdp, request);
undeployAllPolicies(pdp);
return xacmlResponse;
}
/**
* Manage policies and evaluate request (the policies should already be
* deployed on pdp. Evaluates the request and undeploys the policies after
* evaluation).
*
* @param pdp
* the pdp
* @param request
* the request
* @param deployedPolicies
* the deployed policies
* @param xacmlRequest
* the xacml request
* @return the xacml response
* @throws AuditException
* the audit exception
*/
@Deprecated
private XacmlResponse managePoliciesAndEvaluateRequest(PDP pdp,
RequestType request, List<Evaluatable> deployedPolicies,
XacmlRequest xacmlRequest) throws AuditException {
deployPolicies(pdp, deployedPolicies, xacmlRequest, true);
final XacmlResponse xacmlResponse = evaluateRequest(pdp, request);
undeployPolicies(pdp, deployedPolicies);
return xacmlResponse;
}
/**
* Manage policies and evaluate request.
*
* @param request
* the request
* @param xacmlRequest
* the xacml request
* @return the xacml response
* @throws AuditException
* the audit exception
* @throws NoPolicyFoundException
* the no policy found exception
* @throws PolicyProviderException
* the policy provider exception
*/
private synchronized XacmlResponse managePoliciesAndEvaluateRequest(
RequestType request, XacmlRequest xacmlRequest)
throws AuditException, NoPolicyFoundException,
PolicyProviderException {
final PDP pdp = getSimplePDP();
deployPolicies(pdp, xacmlRequest);
return managePoliciesAndEvaluateRequest(pdp, request);
}
/**
* Undeploy all policies.
*
* @param pdp
* the pdp
*/
private void undeployAllPolicies(PDP pdp) {
final PolicyRepository repo = (PolicyRepository) pdp
.getPolicyRepository();
final List<Evaluatable> policies = new LinkedList<Evaluatable>(
repo.getDeployment());
for (final Evaluatable policy : policies) {
repo.undeploy(policy.getId());
}
}
/**
* Undeploy policies.
*
* @param pdp
* the pdp
* @param policies
* the policies
*/
private void undeployPolicies(PDP pdp, List<Evaluatable> policies) {
final PolicyRepository repo = (PolicyRepository) pdp
.getPolicyRepository();
for (final Evaluatable policy : policies) {
repo.undeploy(policy.getId());
}
}
}