Skip to content

Commit

Permalink
Merge branch 'release/20.2.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
cdanger committed Dec 6, 2022
2 parents 419c450 + 5f96daf commit 2bbdbe4
Show file tree
Hide file tree
Showing 39 changed files with 908 additions and 239 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ All notable changes to this project are documented in this file following the [K
- Issues reported on [GitHub](https://github.com/authzforce/core/issues) are referenced in the form of `[GH-N]`, where N is the issue number.
- Issues reported on [OW2's GitLab](https://gitlab.ow2.org/authzforce/core/issues) are referenced in the form of `[GL-N]`, where N is the issue number.

## 20.2.0
### Added
- [GH-69] Support for XACML <StatusDetail> / <MissingAttributeDetail>s, returned when missing named Attribute(s) in AttributeDesignator/AttributeSelector expressions, and may be returned by custom PDP extensions as well. See the example of [custom RequestPreprocessor](pdp-testutils/src/test/java/org/ow2/authzforce/core/pdp/testutil/test/CustomTestRequestPreprocessorFactory.java) (PDP extension) adding AttributeId/Category to [custom AttributeValues](pdp-testutils/src/test/java/org/ow2/authzforce/core/pdp/testutil/test/TestExtensibleSimpleValue.java) (PDP extension) and the [custom function](pdp-testutils/src/test/java/org/ow2/authzforce/core/pdp/testutil/test/TestExtensibleSimpleValueEqualFunction.java) (PDP extension) using this info to throw a standard `missing-attribute` error with `<MissingAttributeDetail>` inside a `<StatusDetail>` element; and also the [example of XACML response](pdp-testutils/src/test/resources/custom/CustomRequestPreproc/response.xml) and [PDP configuration](pdp-testutils/src/test/resources/custom/CustomRequestPreproc/pdp.xml).
- Upgraded dependency authzforce-ce-core-pdp-api: 21.3.0:
- `ImmutableXacmlStatus` and `IndeterminateEvaluationException` classes improved: new constructors supporting XACML `MissingAttributeDetail` element
- `BaseXacmlJaxbRequestPreprocessor` class: new constructor arg: `customNamedAttributeParser` to allow extensions to customize the parsing of named Attributes as they are converted into instance of AuthzForce internal Attribute class with minimal effort.
- `SingleDecisionXacmlJaxbRequestPreprocessor` class improved: new constructor with argument `namedAttributeParser` like above.


## 20.1.1
### Fixed
Expand Down
8 changes: 4 additions & 4 deletions pdp-cli/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<parent>
<groupId>org.ow2.authzforce</groupId>
<artifactId>authzforce-ce-core</artifactId>
<version>20.1.1</version>
<version>20.2.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>authzforce-ce-core-pdp-cli</artifactId>
Expand All @@ -30,12 +30,12 @@
<dependency>
<groupId>org.ow2.authzforce</groupId>
<artifactId>authzforce-ce-core-pdp-engine</artifactId>
<version>20.1.1</version>
<version>20.2.0</version>
</dependency>
<dependency>
<groupId>org.ow2.authzforce</groupId>
<artifactId>authzforce-ce-core-pdp-io-xacml-json</artifactId>
<version>20.1.1</version>
<version>20.2.0</version>
</dependency>
<dependency>
<groupId>org.testng</groupId>
Expand All @@ -49,7 +49,7 @@
<dependency>
<groupId>org.ow2.authzforce</groupId>
<artifactId>authzforce-ce-core-pdp-testutils</artifactId>
<version>20.1.1</version>
<version>20.2.0</version>
<scope>test</scope>
</dependency>
</dependencies>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,89 +39,77 @@
public class CliTest
{

private static final String TEST_DATA_DIR = "src/test/resources/conformance/xacml-3.0-core/mandatory";
private static final String TEST_DATA_DIR = "src/test/resources/conformance/xacml-3.0-core/mandatory";

@Test
public void testXml() throws JAXBException
{
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (PrintStream ps = new PrintStream(baos, true, StandardCharsets.UTF_8))
{
/*
* Redirect system.out to the byte stream
*/
System.setOut(ps);
/*
* Should throw IllegalArgumentException for invalid pdp config, not NPE (because of relative path with no parent path which used to cause NPE when trying to get the parent directory path)
*/
CommandLine.call(new PdpCommandLineCallable(), System.out, "-p", TEST_DATA_DIR + "/pdp.xml", TEST_DATA_DIR + "/IIA001/Request.xml");
System.setOut(System.out);
}
@Test
public void testXml() throws JAXBException
{
final CommandLine cmdLine = new CommandLine(new PdpCommandLineCallable());
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (PrintStream ps = new PrintStream(baos, true, StandardCharsets.UTF_8))
{
/*
* Redirect system.out to the byte stream
*/
System.setOut(ps);
cmdLine.execute("-p", TEST_DATA_DIR + "/pdp.xml", TEST_DATA_DIR + "/IIA001/Request.xml");
}
System.setOut(System.out);
final String output = baos.toString(StandardCharsets.UTF_8);
final Response expectedXacmlJaxbObj = (Response) Xacml3JaxbHelper.createXacml3Unmarshaller().unmarshal(new File(TEST_DATA_DIR + "/IIA001/Response.xml"));
final Response actualXacmlJaxbObj;
try
{
actualXacmlJaxbObj = (Response) Xacml3JaxbHelper.createXacml3Unmarshaller().unmarshal(new StringReader(output));
TestUtils.assertNormalizedEquals(TEST_DATA_DIR + "/IIA001", expectedXacmlJaxbObj, actualXacmlJaxbObj, true);
} catch (final JAXBException e)
{
Assert.fail("Invalid XACML/XML Response returned", e);
}

final String output = baos.toString(StandardCharsets.UTF_8);
System.out.println(output);
final Response expectedXacmlJaxbObj = (Response) Xacml3JaxbHelper.createXacml3Unmarshaller().unmarshal(new File(TEST_DATA_DIR + "/IIA001/Response.xml"));
}

final Response actualXacmlJaxbObj;
try
{
actualXacmlJaxbObj = (Response) Xacml3JaxbHelper.createXacml3Unmarshaller().unmarshal(new StringReader(output));
TestUtils.assertNormalizedEquals(TEST_DATA_DIR + "/IIA001", expectedXacmlJaxbObj, actualXacmlJaxbObj);
}
catch (final JAXBException e)
{
Assert.fail("Invalid XACML/XML Response returned", e);
}
@Test
public void testJson() throws IOException
{
final CommandLine cmd = new CommandLine(new PdpCommandLineCallable());
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (PrintStream ps = new PrintStream(baos, true, StandardCharsets.UTF_8))
{
/*
* Redirect system.out to the byte stream
*/
System.setOut(ps);
cmd.execute("-p", "-tXACML_JSON", TEST_DATA_DIR + "/pdp.xml", TEST_DATA_DIR + "/IIA001/Request.json");
}
System.setOut(System.out);
final String output = baos.toString(StandardCharsets.UTF_8);
final JSONObject normalizedExpectedResponse;
try (final BufferedReader reader = Files.newBufferedReader(Paths.get(TEST_DATA_DIR + "/IIA001/Response.json"), StandardCharsets.UTF_8))
{
normalizedExpectedResponse = XacmlJsonUtils.canonicalizeResponse(new JSONObject(new JSONTokener(reader)), true);
}
final JSONObject normalizedActualResponse = XacmlJsonUtils.canonicalizeResponse(new JSONObject(output), true);
Assert.assertTrue(normalizedActualResponse.similar(normalizedExpectedResponse), "Actual XACML/JSON Response does not match expected");
}

}

@Test
public void testJson() throws IOException
{
final ByteArrayOutputStream baos = new ByteArrayOutputStream();

try (PrintStream ps = new PrintStream(baos, true, StandardCharsets.UTF_8))
{
/*
* Redirect system.out to the byte stream
*/
System.setOut(ps);
/*
* Should throw IllegalArgumentException for invalid pdp config, not NPE (because of relative path with no parent path which used to cause NPE when trying to get the parent directory path)
*/
CommandLine.call(new PdpCommandLineCallable(), System.out, "-p", "-tXACML_JSON", TEST_DATA_DIR + "/pdp.xml", TEST_DATA_DIR + "/IIA001/Request.json");
System.setOut(System.out);
}

final String output = baos.toString(StandardCharsets.UTF_8);
System.out.println(output);

final JSONObject normalizedExpectedResponse;
try (final BufferedReader reader = Files.newBufferedReader(Paths.get(TEST_DATA_DIR + "/IIA001/Response.json"), StandardCharsets.UTF_8))
{
normalizedExpectedResponse = XacmlJsonUtils.canonicalizeResponse(new JSONObject(new JSONTokener(reader)), true);
}
final JSONObject normalizedActualResponse = XacmlJsonUtils.canonicalizeResponse(new JSONObject(output), true);
Assert.assertTrue(normalizedActualResponse.similar(normalizedExpectedResponse), "Actual XACML/JSON Response does not match expected");
}

/**
* Non-regression test for https://github.com/authzforce/core/issues/9
*/
@Test
public void IssueGH9()
{
/*
* Should throw IllegalArgumentException for invalid pdp config, not NPE (because of relative path with no parent path which used to cause NPE when trying to get the parent directory path)
*/
try
{
CommandLine.call(new PdpCommandLineCallable(), System.out, "pom.xml", TEST_DATA_DIR + "/IIA001/Request.json");
}
catch (final CommandLine.ExecutionException e)
{
assertSame(e.getCause().getClass(), IllegalArgumentException.class);
}
}
/**
* Non-regression test for <a href="https://github.com/authzforce/core/issues/9">Issue GH-9: Getting Started Problem</a>
*/
@Test
public void IssueGH9()
{
final CommandLine cmd = new CommandLine(new PdpCommandLineCallable());
/*
* Should throw IllegalArgumentException for invalid pdp config, not NPE (because of relative path with no parent path which used to cause NPE when trying to get the parent directory path)
*/
try
{
cmd.execute("pom.xml", TEST_DATA_DIR + "/IIA001/Request.json");
} catch (final CommandLine.ExecutionException e)
{
assertSame(e.getCause().getClass(), IllegalArgumentException.class);
}
}

}
2 changes: 1 addition & 1 deletion pdp-engine/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<parent>
<groupId>org.ow2.authzforce</groupId>
<artifactId>authzforce-ce-core</artifactId>
<version>20.1.1</version>
<version>20.2.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>authzforce-ce-core-pdp-engine</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import net.sf.saxon.s9api.*;
import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeSelectorType;
import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeValueType;
import oasis.names.tc.xacml._3_0.core.schema.wd_17.MissingAttributeDetail;
import org.ow2.authzforce.core.pdp.api.*;
import org.ow2.authzforce.core.pdp.api.expression.AttributeSelectorExpression;
import org.ow2.authzforce.core.pdp.api.expression.VariableReference;
Expand Down Expand Up @@ -49,7 +50,7 @@ private AttributeSelectorExpressions()
* Reasons for using SAXON's native API (s9api) in XPath evaluation instead of standard Java APIs (e.g. JAXP):
*
* <ol>
* <li>Performance: See http://www.saxonica.com/documentation9.5/javadoc/net /sf/saxon/s9api/package-summary.html:
* <li>Performance: See <a href="http://www.saxonica.com/documentation9.5/javadoc/net/sf/saxon/s9api/package-summary.html">Package net.sf.saxon.s9api Description</a>:
* <p>
* <i>This package provides Saxon's preferred Java API for XSLT, XQuery, XPath, and XML Schema processing. The interface is designed to hide as much as possible of the detail of the
* implementation. However, the API architecture faithfully reflects the internal architecture of the Saxon product, unlike standard APIs such as JAXP and XQJ which in many cases force compromises
Expand Down Expand Up @@ -144,15 +145,18 @@ private static AttributeValueType xdmToJaxbAttributeValue(final String attrDatat
protected final AttributeSelectorId attributeSelectorId;
private final boolean mustBePresent;
private final AttributeValueFactory<?> attrFactory;
private final transient IndeterminateEvaluationException missingAttributeBecauseNullContextException;
private final transient IndeterminateEvaluationException missingAttributesContentException;

protected final MissingAttributeDetail xacmlMissingAttributeDetail;


private final transient Bag.Validator mustBePresentEnforcer;
protected final transient XPathCompilerProxy xPathCompiler;
private final transient Optional<XPathCompilerProxy> optXPathCompiler;
private final transient XPathExecutable xPathEvaluator;
private final transient List<VariableReference<?>> xpathVariables;
private final transient BagDatatype<AV> returnType;
private final transient IndeterminateEvaluationException missingAttributeBecauseNullContextException;
private final transient IndeterminateEvaluationException missingAttributesContentException;

// cached method results
private transient volatile String toString = null;
Expand Down Expand Up @@ -198,8 +202,6 @@ private ExtensibleAttributeSelectorExpression(final String attrSelectorCategory,

final String attributeCategory = attributeSelectorId.getCategory();

final String missingAttributeMessage = this + " not found in context";

this.xPathCompiler = xPathCompiler;
this.optXPathCompiler = Optional.of(this.xPathCompiler);

Expand Down Expand Up @@ -229,8 +231,11 @@ private ExtensibleAttributeSelectorExpression(final String attrSelectorCategory,
this.missingAttributesContentException = new IndeterminateEvaluationException(this + ": No <Content> element found in Attributes of Category='" + attributeCategory + "'",
XacmlStatusCode.SYNTAX_ERROR.value());

// Empty string (undefined) for ContextSelectorId (AttributeId) if undefined is allowed as xs:anyURI
this.xacmlMissingAttributeDetail = new MissingAttributeDetail(null, attrSelectorCategory, contextSelectorId.orElse(""), attrFactory.getDatatype().getId(), null);

this.mustBePresent = mustBePresent;
this.mustBePresentEnforcer = mustBePresent ? new Bags.NonEmptinessValidator(missingAttributeMessage) : Bags.DUMB_VALIDATOR;
this.mustBePresentEnforcer = mustBePresent ? new Bags.NonEmptinessValidator(new ImmutableXacmlStatus(xacmlMissingAttributeDetail, Optional.empty(), Optional.of(this + " not found in context"))) : Bags.DUMB_VALIDATOR;
}

protected abstract ImmutableXacmlStatus getXpathEvalErrorStatus();
Expand Down Expand Up @@ -627,8 +632,8 @@ private AttributeSelectorExpressionWithContextSelector(final AttributeSelectorTy
final String contextSelectorId = attributeSelectorId.getContextSelectorId().get();
this.contextSelectorFQN = Optional.of(AttributeFqns.newInstance(attributeCategory, Optional.empty(), contextSelectorId));
this.attrProvider = attrProvider;
this.missingAttributeForUnknownReasonException = new IndeterminateEvaluationException(this + " not found in context for unknown reason", XacmlStatusCode.MISSING_ATTRIBUTE.value());
this.missingContextSelectorAttributeErrorStatus = new ImmutableXacmlStatus(XacmlStatusCode.MISSING_ATTRIBUTE.value(), Optional.of(this + ": No value found for attribute designated by Category=" + attributeCategory + " and ContextSelectorId=" + contextSelectorId));
this.missingAttributeForUnknownReasonException = new IndeterminateEvaluationException(this + " not found in context for unknown reason", xacmlMissingAttributeDetail, Optional.empty());
this.missingContextSelectorAttributeErrorStatus = new ImmutableXacmlStatus(xacmlMissingAttributeDetail, Optional.empty(), Optional.of(this + ": No value found for attribute designated by Category=" + attributeCategory + " and ContextSelectorId=" + contextSelectorId));
this.xpathEvalErrMsgSuffix = "' from ContextSelectorId='" + contextSelectorId + "' against Content of Attributes of Category=" + attributeCategory;
this.xpathEvalErrStatus = new ImmutableXacmlStatus(XacmlStatusCode.SYNTAX_ERROR.value(), Optional.of(this + ": Error evaluating XPath against XML node from Content of Attributes Category='" + attributeCategory + xpathEvalErrMsgSuffix));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,12 @@

import com.google.common.collect.ImmutableList;
import net.sf.saxon.s9api.XdmNode;
import oasis.names.tc.xacml._3_0.core.schema.wd_17.Attribute;
import oasis.names.tc.xacml._3_0.core.schema.wd_17.Attributes;
import oasis.names.tc.xacml._3_0.core.schema.wd_17.Request;
import org.ow2.authzforce.core.pdp.api.*;
import org.ow2.authzforce.core.pdp.api.expression.XPathCompilerProxy;
import org.ow2.authzforce.core.pdp.api.io.BaseXacmlJaxbRequestPreprocessor;
import org.ow2.authzforce.core.pdp.api.io.IndividualXacmlJaxbRequest;
import org.ow2.authzforce.core.pdp.api.io.SingleCategoryAttributes;
import org.ow2.authzforce.core.pdp.api.io.SingleCategoryXacmlAttributesParser;
import org.ow2.authzforce.core.pdp.api.io.*;
import org.ow2.authzforce.core.pdp.api.value.AttributeBag;
import org.ow2.authzforce.core.pdp.api.value.AttributeValueFactoryRegistry;
import org.ow2.authzforce.xacml.identifiers.XacmlStatusCode;
Expand Down Expand Up @@ -111,6 +109,31 @@ public DecisionRequestPreprocessor<Request, IndividualXacmlJaxbRequest> getInsta

private final DecisionRequestFactory<ImmutableDecisionRequest> reqFactory;

/**
* Creates instance of default request preprocessor
*
* @param datatypeFactoryRegistry
* attribute datatype registry
* @param requestFactory
* decision request factory
* @param strictAttributeIssuerMatch
* true iff strict attribute Issuer match must be enforced (in particular request attributes with empty Issuer only match corresponding AttributeDesignators with empty Issuer)
* @param allowAttributeDuplicates
* true iff duplicate Attribute (with same metadata) elements in Request (for multi-valued attributes) must be allowed
* @param requireContentForXPath
* true iff Content elements must be parsed, else ignored
* @param extraPdpFeatures
* extra - not mandatory per XACML 3.0 core specification - features supported by the PDP engine. This preprocessor checks whether it is supported by the PDP before processing the request further.
* @param namedAttributeParser custom parser of named Attributes, to customize how XACML Attributes are converted into instance of AuthzForce internal Attribute class
*/
public SingleDecisionXacmlJaxbRequestPreprocessor(final AttributeValueFactoryRegistry datatypeFactoryRegistry, final DecisionRequestFactory<ImmutableDecisionRequest> requestFactory,
final boolean strictAttributeIssuerMatch, final boolean allowAttributeDuplicates, final boolean requireContentForXPath, final Set<String> extraPdpFeatures, Optional<NamedXacmlAttributeParser<Attribute>> namedAttributeParser)
{
super(datatypeFactoryRegistry, strictAttributeIssuerMatch, allowAttributeDuplicates, requireContentForXPath, extraPdpFeatures, namedAttributeParser);
assert requestFactory != null;
reqFactory = requestFactory;
}

/**
* Creates instance of default request preprocessor
*
Expand Down
2 changes: 1 addition & 1 deletion pdp-io-xacml-json/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<parent>
<groupId>org.ow2.authzforce</groupId>
<artifactId>authzforce-ce-core</artifactId>
<version>20.1.1</version>
<version>20.2.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>authzforce-ce-core-pdp-io-xacml-json</artifactId>
Expand Down
Loading

0 comments on commit 2bbdbe4

Please sign in to comment.