/*
* JBoss, Home of Professional Open Source.
* Copyright 2012, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.as.test.integration.security.xacml;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import javax.inject.Inject;
import javax.xml.bind.JAXBElement;
import javax.xml.namespace.QName;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.text.StrSubstitutor;
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.logging.Logger;
import org.jboss.security.xacml.core.JBossPDP;
import org.jboss.security.xacml.core.model.context.ActionType;
import org.jboss.security.xacml.core.model.context.AttributeType;
import org.jboss.security.xacml.core.model.context.EnvironmentType;
import org.jboss.security.xacml.core.model.context.RequestType;
import org.jboss.security.xacml.core.model.context.ResourceType;
import org.jboss.security.xacml.core.model.context.SubjectType;
import org.jboss.security.xacml.factories.RequestAttributeFactory;
import org.jboss.security.xacml.factories.RequestResponseContextFactory;
import org.jboss.security.xacml.interfaces.PolicyDecisionPoint;
import org.jboss.security.xacml.interfaces.RequestContext;
import org.jboss.security.xacml.interfaces.XACMLConstants;
import org.jboss.security.xacml.jaxb.LocatorType;
import org.jboss.security.xacml.jaxb.LocatorsType;
import org.jboss.security.xacml.jaxb.PDP;
import org.jboss.security.xacml.jaxb.PoliciesType;
import org.jboss.security.xacml.jaxb.PolicySetType;
import org.jboss.security.xacml.locators.JBossPolicySetLocator;
import org.jboss.shrinkwrap.api.ArchivePaths;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.EmptyAsset;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.Test;
import org.junit.runner.RunWith;
/**
* Testcases which test JBossPDP initialization a validates XACML Interoperability Use Cases. .
*
* @author Josef Cacek
*/
@RunWith(Arquillian.class)
public class JBossPDPInteroperabilityTestCase {
private static Logger LOGGER = Logger.getLogger(JBossPDPInteroperabilityTestCase.class);
@Inject
JBossPDPServiceBean pdpServiceBean;
// Public methods --------------------------------------------------------
/**
* Creates {@link JavaArchive} for the deployment.
*
* @return
*/
@Deployment
public static JavaArchive deployment() {
final JavaArchive jar = ShrinkWrap.create(JavaArchive.class, "pdp-service-bean.jar");
jar.addAsManifestResource(EmptyAsset.INSTANCE, ArchivePaths.create("beans.xml"));
XACMLTestUtils.addCommonClassesToArchive(jar);
XACMLTestUtils.addJBossDeploymentStructureToArchive(jar);
XACMLTestUtils.addXACMLPoliciesToArchive(jar);
//we need this because of "in-container" testing
for (int i = 1; i <= 7; i++) {
jar.addAsResources(JBossPDPServletInitializationTestCase.class.getPackage(), XACMLTestUtils.TESTOBJECTS_REQUESTS
+ "/scenario2-testcase" + i + "-request.xml");
}
jar.addAsResource(JBossPDPServletInitializationTestCase.class.getPackage(), XACMLTestUtils.TESTOBJECTS_REQUESTS
+ "/med-example-request.xml");
LOGGER.info(jar.toString(true));
return jar;
}
/**
* Validates the 7 Oasis XACML Interoperability Use Cases.
*
* @throws Exception
*/
@Test
public void testInteropTestWithXMLRequests() throws Exception {
assertNotNull("PDPServiceBean should be injected.", pdpServiceBean);
final PolicyDecisionPoint pdp = pdpServiceBean.getJBossPDP();
assertNotNull("JBossPDP should be not-null", pdp);
assertEquals("Case 1 should be deny", XACMLConstants.DECISION_DENY, getDecision(pdp, "scenario2-testcase1-request.xml"));
assertEquals("Case 2 should be permit", XACMLConstants.DECISION_PERMIT,
getDecision(pdp, "scenario2-testcase2-request.xml"));
assertEquals("Case 3 should be permit", XACMLConstants.DECISION_PERMIT,
getDecision(pdp, "scenario2-testcase3-request.xml"));
assertEquals("Case 4 should be deny", XACMLConstants.DECISION_DENY, getDecision(pdp, "scenario2-testcase4-request.xml"));
assertEquals("Case 5 should be deny", XACMLConstants.DECISION_DENY, getDecision(pdp, "scenario2-testcase5-request.xml"));
assertEquals("Case 6 should be deny", XACMLConstants.DECISION_DENY, getDecision(pdp, "scenario2-testcase6-request.xml"));
assertEquals("Case 7 should be permit", XACMLConstants.DECISION_PERMIT,
getDecision(pdp, "scenario2-testcase7-request.xml"));
}
/**
* Tests PDP evaluation of XACML requests provided as the objects (Oasis XACML Interoperability Use Cases).
*
* @throws Exception
*/
@Test
public void testInteropTestWithObjects() throws Exception {
assertNotNull("PDPServiceBean should be injected.", pdpServiceBean);
final PolicyDecisionPoint pdp = pdpServiceBean.getJBossPDP();
assertNotNull("JBossPDP should be not-null", pdp);
assertEquals("Case 1 should be deny", XACMLConstants.DECISION_DENY,
getDecision(pdp, getRequestContext("false", "false", 10)));
assertEquals("Case 2 should be permit", XACMLConstants.DECISION_PERMIT,
getDecision(pdp, getRequestContext("false", "false", 1)));
assertEquals("Case 3 should be permit", XACMLConstants.DECISION_PERMIT,
getDecision(pdp, getRequestContext("true", "false", 5)));
assertEquals("Case 4 should be deny", XACMLConstants.DECISION_DENY,
getDecision(pdp, getRequestContext("false", "false", 9)));
assertEquals("Case 5 should be deny", XACMLConstants.DECISION_DENY,
getDecision(pdp, getRequestContext("true", "false", 10)));
assertEquals("Case 6 should be deny", XACMLConstants.DECISION_DENY,
getDecision(pdp, getRequestContext("true", "false", 15)));
assertEquals("Case 7 should be permit", XACMLConstants.DECISION_PERMIT,
getDecision(pdp, getRequestContext("true", "true", 10)));
}
/**
* Tests loading XACML policies from a filesystem folder.
*
* @throws Exception
*/
@Test
public void testPoliciesLoadedFromDir() throws Exception {
//create temporary folder for policies
final File policyDir = new File("test-JBossPDP-Med-" + System.currentTimeMillis());
final InputStream requestIS = getClass().getResourceAsStream(
XACMLTestUtils.TESTOBJECTS_REQUESTS + "/med-example-request.xml");
try {
policyDir.mkdirs();
final JBossPDP pdp = createPDPForMed(policyDir);
final String requestTemplate = IOUtils.toString(requestIS, "UTF-8");
LOGGER.info("REQUEST template: " + requestTemplate);
final Map<String, Object> substitutionMap = new HashMap<String, Object>();
substitutionMap.put(XACMLTestUtils.SUBST_SUBJECT_ID, "josef@med.example.com");
assertEquals("Decision for josef@med.example.com should be DECISION_PERMIT", XACMLConstants.DECISION_PERMIT,
getDecisionForStr(pdp, StrSubstitutor.replace(requestTemplate, substitutionMap)));
substitutionMap.put(XACMLTestUtils.SUBST_SUBJECT_ID, "guest@med.example.com");
assertEquals("Decision for guest@med.example.com should be DECISION_DENY", XACMLConstants.DECISION_DENY,
getDecisionForStr(pdp, StrSubstitutor.replace(requestTemplate, substitutionMap)));
substitutionMap.put(XACMLTestUtils.SUBST_SUBJECT_ID, "hs@simpsons.com");
assertEquals("Decision for hs@simpsons.com should be DECISION_DENY", XACMLConstants.DECISION_DENY,
getDecisionForStr(pdp, StrSubstitutor.replace(requestTemplate, substitutionMap)));
substitutionMap.put(XACMLTestUtils.SUBST_SUBJECT_ID, "bs@simpsons.com");
assertEquals("Decision for bs@simpsons.com should be DECISION_NOT_APPLICABLE",
XACMLConstants.DECISION_NOT_APPLICABLE,
getDecisionForStr(pdp, StrSubstitutor.replace(requestTemplate, substitutionMap)));
substitutionMap.put(XACMLTestUtils.SUBST_SUBJECT_ID, "admin@acme.com");
assertEquals("Decision for admin@acme.com should be DECISION_NOT_APPLICABLE",
XACMLConstants.DECISION_NOT_APPLICABLE,
getDecisionForStr(pdp, StrSubstitutor.replace(requestTemplate, substitutionMap)));
} finally {
FileUtils.deleteDirectory(policyDir);
requestIS.close();
}
}
// Private methods -------------------------------------------------------
/**
* Creates a {@link JBossPDP} instance filled with policies from Medical example (loaded from a directory in the
* filesystem).
*
* @param policyDir
* @return
* @throws IOException
*/
private JBossPDP createPDPForMed(final File policyDir) throws IOException {
final File policySetFile = new File(policyDir, XACMLTestUtils.MED_EXAMPLE_POLICY_SET);
final File policySetFile2 = new File(policyDir, XACMLTestUtils.MED_EXAMPLE_POLICY_SET2);
//copy policy files to the temporary folder
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Copying policies to the " + policyDir.getAbsolutePath());
}
FileUtils.copyInputStreamToFile(
getClass().getResourceAsStream(
XACMLTestUtils.TESTOBJECTS_POLICIES + "/" + XACMLTestUtils.MED_EXAMPLE_POLICY_SET), policySetFile);
FileUtils.copyInputStreamToFile(
getClass().getResourceAsStream(
XACMLTestUtils.TESTOBJECTS_POLICIES + "/" + XACMLTestUtils.MED_EXAMPLE_POLICY_SET2), policySetFile2);
//create XML configuration for the PDP
final PDP pdp = new PDP();
final PoliciesType policies = new PoliciesType();
final PolicySetType policySet = new PolicySetType();
policySet.setLocation(policyDir.toURI().getPath());
policies.getPolicySet().add(policySet);
pdp.setPolicies(policies);
final LocatorType locator = new LocatorType();
locator.setName(JBossPolicySetLocator.class.getName());
final LocatorsType locators = new LocatorsType();
locators.getLocator().add(locator);
pdp.setLocators(locators);
return new JBossPDP(new JAXBElement<PDP>(new QName("urn:jboss:xacml:2.0", "jbosspdp"), PDP.class, pdp));
}
/**
* Get the decision from the given PDP for the given request string (XML as a String).
*
* @param pdp
* @param requestStr
* @return
* @throws Exception
*/
private int getDecisionForStr(PolicyDecisionPoint pdp, String requestStr) throws Exception {
final RequestContext request = RequestResponseContextFactory.createRequestCtx();
request.readRequest(IOUtils.toInputStream(requestStr));
return getDecision(pdp, request);
}
/**
* Get the decision from the PDP.
*
* @param pdp
* @param requestFileLoc a file where the xacml request is stored
* @return
* @throws Exception
*/
private int getDecision(PolicyDecisionPoint pdp, String requestFileLoc) throws Exception {
final RequestContext request = RequestResponseContextFactory.createRequestCtx();
LOGGER.info("Creating request from " + requestFileLoc);
final InputStream requestStream = JBossPDPInteroperabilityTestCase.class
.getResourceAsStream(XACMLTestUtils.TESTOBJECTS_REQUESTS + "/" + requestFileLoc);
if (requestStream == null) {
LOGGER.warn("INPUT IS NULL");
}
request.readRequest(requestStream);
return getDecision(pdp, request);
}
/**
* Gets the decision from the PDP.
*
* @param pdp
* @param request
* @return
*/
private static int getDecision(PolicyDecisionPoint pdp, RequestContext request) {
return pdp.evaluate(request).getDecision();
}
/**
* Creates a single XACML request (instance of {@link RequestType}) with given parameters (subject's attribute values).
*
* @param reqTradeAppr
* @param reqCreditAppr
* @param buyPrice
* @return
* @throws Exception
*/
private RequestContext getRequestContext(String reqTradeAppr, String reqCreditAppr, int buyPrice) throws Exception {
final RequestType request = new RequestType();
request.getSubject().add(createSubject(reqTradeAppr, reqCreditAppr, buyPrice));
request.getResource().add(createResource());
request.setAction(createAction());
request.setEnvironment(new EnvironmentType());
final RequestContext requestCtx = RequestResponseContextFactory.createRequestCtx();
requestCtx.setRequest(request);
return requestCtx;
}
/**
* Creates a {@link SubjectType} with given attribute values. Some of the attribute values (as "subject-id" for instance)
* are fixed.
*
* @param reqTradeAppr
* @param reqCreditAppr
* @param buyPrice
* @return
*/
private SubjectType createSubject(String reqTradeAppr, String reqCreditAppr, int buyPrice) {
// Create a subject type
SubjectType subject = new SubjectType();
subject.setSubjectCategory("urn:oasis:names:tc:xacml:1.0:subject-category:access-subject");
// create the subject attributes
AttributeType attSubjectID = RequestAttributeFactory.createStringAttributeType(
"urn:oasis:names:tc:xacml:1.0:subject:subject-id", "xacml20.interop.com", "123456");
subject.getAttribute().add(attSubjectID);
AttributeType attUserName = RequestAttributeFactory.createStringAttributeType(
"urn:xacml:2.0:interop:example:subject:user-name", "xacml20.interop.com", "John Smith");
subject.getAttribute().add(attUserName);
AttributeType attBuyNumShares = RequestAttributeFactory.createIntegerAttributeType(
"urn:xacml:2.0:interop:example:subject:buy-num-shares", "xacml20.interop.com", 1000);
subject.getAttribute().add(attBuyNumShares);
AttributeType attBuyOfferShare = RequestAttributeFactory.createIntegerAttributeType(
"urn:xacml:2.0:interop:example:subject:buy-offer-price", "xacml20.interop.com", buyPrice);
subject.getAttribute().add(attBuyOfferShare);
AttributeType attRequestExtCred = RequestAttributeFactory.createStringAttributeType(
"urn:xacml:2.0:interop:example:subject:req-credit-ext-approval", "xacml20.interop.com", reqCreditAppr);
subject.getAttribute().add(attRequestExtCred);
AttributeType attRequestTradeApproval = RequestAttributeFactory.createStringAttributeType(
"urn:xacml:2.0:interop:example:subject:req-trade-approval", "xacml20.interop.com", reqTradeAppr);
subject.getAttribute().add(attRequestTradeApproval);
return subject;
}
/**
* Creates a {@link ResourceType} with several attributes.
*
* @return
*/
private ResourceType createResource() {
ResourceType resourceType = new ResourceType();
AttributeType attResourceID = RequestAttributeFactory.createStringAttributeType(
"urn:oasis:names:tc:xacml:1.0:resource:resource-id", "xacml20.interop.com", "CustomerAccount");
resourceType.getAttribute().add(attResourceID);
AttributeType attOwnerID = RequestAttributeFactory.createStringAttributeType(
"urn:xacml:2.0:interop:example:resource:owner-id", "xacml20.interop.com", "123456");
resourceType.getAttribute().add(attOwnerID);
AttributeType attOwnerName = RequestAttributeFactory.createStringAttributeType(
"urn:xacml:2.0:interop:example:resource:owner-name", "xacml20.interop.com", "John Smith");
resourceType.getAttribute().add(attOwnerName);
AttributeType attAccountStatus = RequestAttributeFactory.createStringAttributeType(
"urn:xacml:2.0:interop:example:resource:account-status", "xacml20.interop.com", "Active");
resourceType.getAttribute().add(attAccountStatus);
AttributeType attCreditLine = RequestAttributeFactory.createIntegerAttributeType(
"urn:xacml:2.0:interop:example:resource:credit-line", "xacml20.interop.com", 15000);
resourceType.getAttribute().add(attCreditLine);
AttributeType attCurrentCredit = RequestAttributeFactory.createIntegerAttributeType(
"urn:xacml:2.0:interop:example:resource:current-credit", "xacml20.interop.com", 10000);
resourceType.getAttribute().add(attCurrentCredit);
AttributeType attTradeLimit = RequestAttributeFactory.createIntegerAttributeType(
"urn:xacml:2.0:interop:example:resource:trade-limit", "xacml20.interop.com", 10000);
resourceType.getAttribute().add(attTradeLimit);
return resourceType;
}
/**
* Creates a simple {@link ActionType} with a single attribute - action-id=Buy.
*
* @return
*/
private ActionType createAction() {
final ActionType actionType = new ActionType();
final AttributeType attActionID = RequestAttributeFactory.createStringAttributeType(
"urn:oasis:names:tc:xacml:1.0:action:action-id", "xacml20.interop.com", "Buy");
actionType.getAttribute().add(attActionID);
return actionType;
}
}