From aaf13b9901a62a32cf3a96ece85f51353b6b0538 Mon Sep 17 00:00:00 2001 From: cdanger Date: Sat, 30 Apr 2016 02:53:20 +0200 Subject: [PATCH] Fixed javadoc comments --- src/main/java/com/sun/xacml/NotFunction.java | 158 +- src/main/java/com/sun/xacml/PortRange.java | 478 ++--- .../sun/xacml/StringNormalizeFunction.java | 310 +-- .../com/sun/xacml/TimeInRangeFunction.java | 404 ++-- .../sun/xacml/UnknownIdentifierException.java | 119 +- .../core/pdp/impl/AllOfEvaluator.java | 21 +- .../core/pdp/impl/AnyOfEvaluator.java | 22 +- ...ttributeAssignmentExpressionEvaluator.java | 320 +-- .../core/pdp/impl/BaseDecisionResult.java | 614 +++--- .../pdp/impl/BasePdpExtensionRegistry.java | 293 +-- .../core/pdp/impl/BasePepActions.java | 392 ++-- .../pdp/impl/CloseableAttributeProvider.java | 440 ++-- .../impl/DefaultEnvironmentProperties.java | 161 +- .../core/pdp/impl/DefaultRequestFilter.java | 281 +-- .../IndividualDecisionRequestContext.java | 23 +- .../IndividualDecisionRequestEvaluator.java | 222 +- .../core/pdp/impl/MatchEvaluator.java | 12 +- .../pdp/impl/ModularAttributeProvider.java | 360 ++-- .../pdp/impl/MultiDecisionRequestFilter.java | 403 ++-- .../MutableIndividualDecisionRequest.java | 318 +-- .../ow2/authzforce/core/pdp/impl/PDPImpl.java | 786 ++++---- .../ow2/authzforce/core/pdp/impl/PdpBean.java | 325 +-- .../core/pdp/impl/PdpConfigurationParser.java | 818 ++++---- .../core/pdp/impl/PdpExtensionLoader.java | 676 +++---- .../core/pdp/impl/PdpModelHandler.java | 399 ++-- .../core/pdp/impl/PepActionExpression.java | 330 ++- .../core/pdp/impl/PepActionExpressions.java | 580 +++--- .../core/pdp/impl/PepActionFactory.java | 100 +- .../core/pdp/impl/SchemaHandler.java | 1119 +++++------ .../core/pdp/impl/TargetEvaluator.java | 16 +- .../combining/BaseCombiningAlgRegistry.java | 141 +- .../pdp/impl/combining/CombiningAlgSet.java | 147 +- .../pdp/impl/combining/DenyOverridesAlg.java | 4 + .../impl/combining/DenyUnlessPermitAlg.java | 201 +- .../impl/combining/FirstApplicableAlg.java | 189 +- .../combining/LegacyDenyOverridesAlg.java | 183 +- .../combining/LegacyPermitOverridesAlg.java | 199 +- .../impl/combining/OnlyOneApplicableAlg.java | 231 +-- .../impl/combining/PermitOverridesAlg.java | 4 + .../impl/combining/PermitUnlessDenyAlg.java | 225 ++- .../StandardCombiningAlgRegistry.java | 175 +- .../core/pdp/impl/expression/Apply.java | 25 +- .../impl/expression/AttributeDesignator.java | 26 +- .../AttributeSelectorExpression.java | 34 +- .../expression/BaseVariableReference.java | 304 +-- .../expression/ExpressionFactoryImpl.java | 742 ++++--- .../expression/PrimitiveValueExpression.java | 119 +- .../core/pdp/impl/func/BaseFunctionSet.java | 230 +-- .../pdp/impl/func/ComparisonFunction.java | 477 ++--- .../impl/func/DatatypeConversionFunction.java | 859 ++++---- .../pdp/impl/func/EqualTypeMatchFunction.java | 5 +- .../impl/func/FirstOrderBagFunctionSet.java | 4 +- .../core/pdp/impl/func/FunctionRegistry.java | 236 +-- .../GenericHigherOrderFunctionFactory.java | 92 +- .../pdp/impl/func/HigherOrderBagFunction.java | 298 +-- .../impl/func/HigherOrderBagFunctionSet.java | 1788 +++++++++-------- .../pdp/impl/func/LogicalAndFunction.java | 361 ++-- .../pdp/impl/func/LogicalNOfFunction.java | 550 ++--- .../core/pdp/impl/func/LogicalOrFunction.java | 323 +-- .../pdp/impl/func/MapFunctionFactory.java | 268 +-- .../impl/func/NonEqualTypeMatchFunction.java | 734 +++---- .../impl/func/NumericArithmeticFunction.java | 632 +++--- .../impl/func/RegexpMatchFunctionHelper.java | 412 ++-- .../impl/func/StandardFunctionRegistry.java | 13 +- .../impl/func/StringConcatenateFunction.java | 5 +- .../core/pdp/impl/func/SubstringFunction.java | 6 +- .../impl/func/TemporalArithmeticFunction.java | 408 ++-- .../pdp/impl/func/XPathNodeCountFunction.java | 295 +-- .../policy/CoreRefPolicyProviderModule.java | 15 +- .../pdp/impl/policy/PolicyEvaluators.java | 22 +- .../PolicyPepActionExpressionsEvaluator.java | 374 ++-- .../core/pdp/impl/policy/PolicyVersions.java | 303 +-- .../pdp/impl/policy/RootPolicyEvaluator.java | 608 +++--- .../policy/StaticApplicablePolicyView.java | 23 +- .../pdp/impl/rule/ConditionEvaluator.java | 14 +- .../core/pdp/impl/rule/RuleEvaluator.java | 17 +- .../RulePepActionExpressionsEvaluator.java | 384 ++-- .../core/pdp/impl/value/AnyURIValue.java | 164 +- .../pdp/impl/value/Base64BinaryValue.java | 179 +- .../value/BaseDatatypeFactoryRegistry.java | 274 +-- .../core/pdp/impl/value/BaseTimeValue.java | 211 +- .../core/pdp/impl/value/BooleanValue.java | 347 ++-- .../core/pdp/impl/value/DNSNameValue.java | 427 ++-- .../pdp/impl/value/DatatypeConstants.java | 767 +++---- .../core/pdp/impl/value/DateTimeValue.java | 175 +- .../core/pdp/impl/value/DateValue.java | 183 +- .../pdp/impl/value/DayTimeDurationValue.java | 81 +- .../core/pdp/impl/value/DoubleValue.java | 370 ++-- .../core/pdp/impl/value/DurationValue.java | 150 +- .../core/pdp/impl/value/HexBinaryValue.java | 201 +- .../core/pdp/impl/value/IPAddressValue.java | 565 +++--- .../core/pdp/impl/value/IntegerValue.java | 396 ++-- .../core/pdp/impl/value/NumericValue.java | 169 +- .../core/pdp/impl/value/RFC822NameValue.java | 486 ++--- .../core/pdp/impl/value/SimpleValue.java | 595 +++--- .../StandardDatatypeFactoryRegistry.java | 185 +- .../core/pdp/impl/value/StringValue.java | 271 +-- .../core/pdp/impl/value/TimeValue.java | 186 +- .../core/pdp/impl/value/X500NameValue.java | 245 +-- .../core/pdp/impl/value/XPathValue.java | 417 ++-- .../impl/value/YearMonthDurationValue.java | 104 +- 101 files changed, 15353 insertions(+), 15000 deletions(-) diff --git a/src/main/java/com/sun/xacml/NotFunction.java b/src/main/java/com/sun/xacml/NotFunction.java index 2c8b18f5..f5414a42 100644 --- a/src/main/java/com/sun/xacml/NotFunction.java +++ b/src/main/java/com/sun/xacml/NotFunction.java @@ -1,78 +1,80 @@ -/** - * - * Copyright 2003-2004 Sun Microsystems, Inc. All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * - * 1. Redistribution of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - * - * 2. Redistribution 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 Sun Microsystems, Inc. or the names of contributors may be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * This software is provided "AS IS," without a warranty of any kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY IMPLIED - * WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS - * SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL - * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER - * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGES. - * - * You acknowledge that this software is not designed or intended for use in the design, construction, operation or maintenance of any nuclear facility. - */ -package com.sun.xacml; - -import java.util.Collections; -import java.util.Deque; -import java.util.List; - -import org.ow2.authzforce.core.pdp.api.Datatype; -import org.ow2.authzforce.core.pdp.api.Expression; -import org.ow2.authzforce.core.pdp.api.FirstOrderFunction; -import org.ow2.authzforce.core.pdp.api.FirstOrderFunctionCall; -import org.ow2.authzforce.core.pdp.api.IndeterminateEvaluationException; -import org.ow2.authzforce.core.pdp.impl.value.BooleanValue; -import org.ow2.authzforce.core.pdp.impl.value.DatatypeConstants; - -/** - * A class that implements the not function. This function takes one boolean argument and returns the logical negation of that value. If the argument evaluates - * to indeterminate, an indeterminate result is returned. - * - * @since 1.0 - * @author Steve Hanna - * @author Seth Proctor - */ -public final class NotFunction extends FirstOrderFunction.SingleParameterTyped -{ - - /** - * Standard identifier for the not function. - */ - public static final String NAME_NOT = XACML_NS_1_0 + "not"; - - /** - * Singleton instance of "not" logical function - */ - public static final NotFunction INSTANCE = new NotFunction(); - - private NotFunction() - { - super(NAME_NOT, DatatypeConstants.BOOLEAN.TYPE, false, Collections.singletonList(DatatypeConstants.BOOLEAN.TYPE)); - } - - @Override - public FirstOrderFunctionCall newCall(List> argExpressions, Datatype... remainingArgTypes) throws IllegalArgumentException - { - return new FirstOrderFunctionCall.EagerSinglePrimitiveTypeEval(functionSignature, argExpressions, remainingArgTypes) - { - @Override - protected BooleanValue evaluate(Deque args) throws IndeterminateEvaluationException - { - return args.getFirst().not(); - } - - }; - } - -} +/** + * + * Copyright 2003-2004 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * + * 1. Redistribution of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * + * 2. Redistribution 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 Sun Microsystems, Inc. or the names of contributors may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY IMPLIED + * WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS + * SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER + * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use in the design, construction, operation or maintenance of any nuclear facility. + */ +package com.sun.xacml; + +import java.util.Collections; +import java.util.Deque; +import java.util.List; + +import org.ow2.authzforce.core.pdp.api.Datatype; +import org.ow2.authzforce.core.pdp.api.Expression; +import org.ow2.authzforce.core.pdp.api.FirstOrderFunction; +import org.ow2.authzforce.core.pdp.api.FirstOrderFunctionCall; +import org.ow2.authzforce.core.pdp.api.IndeterminateEvaluationException; +import org.ow2.authzforce.core.pdp.impl.value.BooleanValue; +import org.ow2.authzforce.core.pdp.impl.value.DatatypeConstants; + +/** + * A class that implements the not function. This function takes one boolean argument and returns the logical negation of that value. If the argument evaluates + * to indeterminate, an indeterminate result is returned. + * + * @since 1.0 + * @author Steve Hanna + * @author Seth Proctor + * @version $Id: $ + */ +public final class NotFunction extends FirstOrderFunction.SingleParameterTyped +{ + + /** + * Standard identifier for the not function. + */ + public static final String NAME_NOT = XACML_NS_1_0 + "not"; + + /** + * Singleton instance of "not" logical function + */ + public static final NotFunction INSTANCE = new NotFunction(); + + private NotFunction() + { + super(NAME_NOT, DatatypeConstants.BOOLEAN.TYPE, false, Collections.singletonList(DatatypeConstants.BOOLEAN.TYPE)); + } + + /** {@inheritDoc} */ + @Override + public FirstOrderFunctionCall newCall(List> argExpressions, Datatype... remainingArgTypes) throws IllegalArgumentException + { + return new FirstOrderFunctionCall.EagerSinglePrimitiveTypeEval(functionSignature, argExpressions, remainingArgTypes) + { + @Override + protected BooleanValue evaluate(Deque args) throws IndeterminateEvaluationException + { + return args.getFirst().not(); + } + + }; + } + +} diff --git a/src/main/java/com/sun/xacml/PortRange.java b/src/main/java/com/sun/xacml/PortRange.java index 00239aed..fef2c733 100644 --- a/src/main/java/com/sun/xacml/PortRange.java +++ b/src/main/java/com/sun/xacml/PortRange.java @@ -1,239 +1,239 @@ -/** - * - * Copyright 2003-2004 Sun Microsystems, Inc. All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * - * 1. Redistribution of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - * - * 2. Redistribution 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 Sun Microsystems, Inc. or the names of contributors may be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * This software is provided "AS IS," without a warranty of any kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY IMPLIED - * WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS - * SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL - * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER - * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGES. - * - * You acknowledge that this software is not designed or intended for use in the design, construction, operation or maintenance of any nuclear facility. - */ -package com.sun.xacml; - -import java.util.Objects; - -/** - * This class represents a port range as specified in the dnsName and ipAddress datatypes. The range may have upper and lower bounds, - * be specified by a single port number, or may be unbound. - */ -public final class PortRange -{ - - /** - * Constant used to specify that the range is unbound on one side. - */ - private static final int UNBOUND = -1; - - // the port bound values - private final int lowerBound; - private final int upperBound; - - /** - * Default constructor used to represent an unbound range. This is typically used when an address has no port information. - */ - public PortRange() - { - this(UNBOUND, UNBOUND); - } - - /** - * Creates a PortRange with upper and lower bounds. Either of the parameters may have the value UNBOUND meaning that there is no - * bound at the respective end. - * - * @param lowerBound - * the lower-bound port number or UNBOUND - * @param upperBound - * the upper-bound port number or UNBOUND - */ - private PortRange(int lowerBound, int upperBound) - { - this.lowerBound = lowerBound; - this.upperBound = upperBound; - } - - /** - * Creates an instance of PortRange based on the given value. - * - * @param value - * a String representing the range - * - * @return a new PortRange - * - * @throws NumberFormatException - * if a port value isn't an integer - */ - public static PortRange getInstance(String value) - { - int lowerBound = UNBOUND; - int upperBound = UNBOUND; - - // first off, make sure there's actually content here - if (value.length() == 0 || value.equals("-")) - { - return new PortRange(); - } - - // there's content, so figure where the '-' is, if at all - int dashPos = value.indexOf('-'); - - if (dashPos == -1) - { - // there's no dash, so it's just a single number - lowerBound = upperBound = Integer.parseInt(value); - } else if (dashPos == 0) - { - // it starts with a dash, so it's just upper-range bound - upperBound = Integer.parseInt(value.substring(1)); - } else - { - // it's a number followed by a dash, so get the lower-bound... - lowerBound = Integer.parseInt(value.substring(0, dashPos)); - int len = value.length(); - - // ... and see if there is a second port number - if (dashPos != len - 1) - { - // the dash wasn't at the end, so there's an upper-bound - upperBound = Integer.parseInt(value.substring(dashPos + 1, len)); - } - } - - return new PortRange(lowerBound, upperBound); - } - - /** - * Returns the lower-bound port value. If the range is not lower-bound, then this returns UNBOUND. If the range is actually a single port - * number, then this returns the same value as getUpperBound. - * - * @return the upper-bound - */ - public int getLowerBound() - { - return lowerBound; - } - - /** - * Returns the upper-bound port value. If the range is not upper-bound, then this returns UNBOUND. If the range is actually a single port - * number, then this returns the same value as getLowerBound. - * - * @return the upper-bound - */ - public int getUpperBound() - { - return upperBound; - } - - /** - * Returns whether the range is bounded by a lower port number. - * - * @return true if lower-bounded, false otherwise - */ - public boolean isLowerBounded() - { - return lowerBound != UNBOUND; - } - - /** - * Returns whether the range is bounded by an upper port number. - * - * @return true if upper-bounded, false otherwise - */ - public boolean isUpperBounded() - { - return upperBound != UNBOUND; - } - - /** - * Returns whether the range is actually a single port number. - * - * @return true if the range is a single port number, false otherwise - */ - public boolean isSinglePort() - { - return lowerBound == upperBound && lowerBound != UNBOUND; - } - - /** - * Returns whether the range is unbound, which means that it specifies no port number or range. This is typically used with addresses that include no port - * information. - * - * @return true if the range is unbound, false otherwise - */ - public boolean isUnbound() - { - return lowerBound == UNBOUND && upperBound == UNBOUND; - } - - private transient volatile int hashCode = 0; // Effective Java - Item 9 - - @Override - public int hashCode() - { - if (hashCode == 0) - { - hashCode = Objects.hash(lowerBound, upperBound); - } - - return hashCode; - } - - /** - * Returns true if the input is an instance of this class and if its value equals the value contained in this class. - * - * @param o - * the object to compare - * - * @return true if this object and the input represent the same value - */ - @Override - public boolean equals(Object o) - { - if (this == o) - { - return true; - } - - if (!(o instanceof PortRange)) - { - return false; - } - - final PortRange other = (PortRange) o; - return lowerBound == other.lowerBound && upperBound == other.upperBound; - } - - /** - * @return encoded port range - * - */ - public String encode() - { - if (isUnbound()) - return ""; - - if (isSinglePort()) - return Integer.toString(lowerBound, 10); - - if (!isLowerBounded()) - return "-" + Integer.toString(upperBound, 10); - - if (!isUpperBounded()) - return Integer.toString(lowerBound, 10) + "-"; - - return Integer.toString(lowerBound, 10) + "-" + Integer.toString(upperBound, 10); - } - -} +/** + * + * Copyright 2003-2004 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * + * 1. Redistribution of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * + * 2. Redistribution 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 Sun Microsystems, Inc. or the names of contributors may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY IMPLIED + * WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS + * SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER + * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use in the design, construction, operation or maintenance of any nuclear facility. + */ +package com.sun.xacml; + +import java.util.Objects; + +/** + * This class represents a port range as specified in the dnsName and ipAddress datatypes. The range may have upper and lower bounds, + * be specified by a single port number, or may be unbound. + * + * @author cdangerv + * @version $Id: $ + */ +public final class PortRange +{ + + /** + * Constant used to specify that the range is unbound on one side. + */ + private static final int UNBOUND = -1; + + // the port bound values + private final int lowerBound; + private final int upperBound; + + /** + * Default constructor used to represent an unbound range. This is typically used when an address has no port information. + */ + public PortRange() + { + this(UNBOUND, UNBOUND); + } + + /** + * Creates a PortRange with upper and lower bounds. Either of the parameters may have the value UNBOUND meaning that there is no + * bound at the respective end. + * + * @param lowerBound + * the lower-bound port number or UNBOUND + * @param upperBound + * the upper-bound port number or UNBOUND + */ + private PortRange(int lowerBound, int upperBound) + { + this.lowerBound = lowerBound; + this.upperBound = upperBound; + } + + /** + * Creates an instance of PortRange based on the given value. + * + * @param value + * a String representing the range + * @return a new PortRange + * @throws java.lang.NumberFormatException + * if a port value isn't an integer + */ + public static PortRange getInstance(String value) + { + int lowerBound = UNBOUND; + int upperBound = UNBOUND; + + // first off, make sure there's actually content here + if (value.length() == 0 || value.equals("-")) + { + return new PortRange(); + } + + // there's content, so figure where the '-' is, if at all + int dashPos = value.indexOf('-'); + + if (dashPos == -1) + { + // there's no dash, so it's just a single number + lowerBound = upperBound = Integer.parseInt(value); + } else if (dashPos == 0) + { + // it starts with a dash, so it's just upper-range bound + upperBound = Integer.parseInt(value.substring(1)); + } else + { + // it's a number followed by a dash, so get the lower-bound... + lowerBound = Integer.parseInt(value.substring(0, dashPos)); + int len = value.length(); + + // ... and see if there is a second port number + if (dashPos != len - 1) + { + // the dash wasn't at the end, so there's an upper-bound + upperBound = Integer.parseInt(value.substring(dashPos + 1, len)); + } + } + + return new PortRange(lowerBound, upperBound); + } + + /** + * Returns the lower-bound port value. If the range is not lower-bound, then this returns UNBOUND. If the range is actually a single port + * number, then this returns the same value as getUpperBound. + * + * @return the upper-bound + */ + public int getLowerBound() + { + return lowerBound; + } + + /** + * Returns the upper-bound port value. If the range is not upper-bound, then this returns UNBOUND. If the range is actually a single port + * number, then this returns the same value as getLowerBound. + * + * @return the upper-bound + */ + public int getUpperBound() + { + return upperBound; + } + + /** + * Returns whether the range is bounded by a lower port number. + * + * @return true if lower-bounded, false otherwise + */ + public boolean isLowerBounded() + { + return lowerBound != UNBOUND; + } + + /** + * Returns whether the range is bounded by an upper port number. + * + * @return true if upper-bounded, false otherwise + */ + public boolean isUpperBounded() + { + return upperBound != UNBOUND; + } + + /** + * Returns whether the range is actually a single port number. + * + * @return true if the range is a single port number, false otherwise + */ + public boolean isSinglePort() + { + return lowerBound == upperBound && lowerBound != UNBOUND; + } + + /** + * Returns whether the range is unbound, which means that it specifies no port number or range. This is typically used with addresses that include no port + * information. + * + * @return true if the range is unbound, false otherwise + */ + public boolean isUnbound() + { + return lowerBound == UNBOUND && upperBound == UNBOUND; + } + + private transient volatile int hashCode = 0; // Effective Java - Item 9 + + /** {@inheritDoc} */ + @Override + public int hashCode() + { + if (hashCode == 0) + { + hashCode = Objects.hash(lowerBound, upperBound); + } + + return hashCode; + } + + /** + * {@inheritDoc} + * + * Returns true if the input is an instance of this class and if its value equals the value contained in this class. + */ + @Override + public boolean equals(Object o) + { + if (this == o) + { + return true; + } + + if (!(o instanceof PortRange)) + { + return false; + } + + final PortRange other = (PortRange) o; + return lowerBound == other.lowerBound && upperBound == other.upperBound; + } + + /** + *

encode

+ * + * @return encoded port range + */ + public String encode() + { + if (isUnbound()) + return ""; + + if (isSinglePort()) + return Integer.toString(lowerBound, 10); + + if (!isLowerBounded()) + return "-" + Integer.toString(upperBound, 10); + + if (!isUpperBounded()) + return Integer.toString(lowerBound, 10) + "-"; + + return Integer.toString(lowerBound, 10) + "-" + Integer.toString(upperBound, 10); + } + +} diff --git a/src/main/java/com/sun/xacml/StringNormalizeFunction.java b/src/main/java/com/sun/xacml/StringNormalizeFunction.java index 38f714ff..e1915612 100644 --- a/src/main/java/com/sun/xacml/StringNormalizeFunction.java +++ b/src/main/java/com/sun/xacml/StringNormalizeFunction.java @@ -1,154 +1,156 @@ -/** - * - * Copyright 2003-2004 Sun Microsystems, Inc. All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * - * 1. Redistribution of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - * - * 2. Redistribution 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 Sun Microsystems, Inc. or the names of contributors may be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * This software is provided "AS IS," without a warranty of any kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY IMPLIED - * WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS - * SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL - * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER - * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGES. - * - * You acknowledge that this software is not designed or intended for use in the design, construction, operation or maintenance of any nuclear facility. - */ -package com.sun.xacml; - -import java.util.Collections; -import java.util.Deque; -import java.util.List; -import java.util.Locale; - -import org.ow2.authzforce.core.pdp.api.Datatype; -import org.ow2.authzforce.core.pdp.api.Expression; -import org.ow2.authzforce.core.pdp.api.FirstOrderFunction; -import org.ow2.authzforce.core.pdp.api.FirstOrderFunctionCall; -import org.ow2.authzforce.core.pdp.api.FirstOrderFunctionCall.EagerSinglePrimitiveTypeEval; -import org.ow2.authzforce.core.pdp.api.FunctionSet; -import org.ow2.authzforce.core.pdp.api.FunctionSignature; -import org.ow2.authzforce.core.pdp.api.IndeterminateEvaluationException; -import org.ow2.authzforce.core.pdp.impl.func.BaseFunctionSet; -import org.ow2.authzforce.core.pdp.impl.value.DatatypeConstants; -import org.ow2.authzforce.core.pdp.impl.value.StringValue; - -/** - * string-normalize-* function - * - * @since 1.0 - * @author Steve Hanna - * @author Seth Proctor - */ -public final class StringNormalizeFunction extends FirstOrderFunction.SingleParameterTyped -{ - - /** - * Standard identifier for the string-normalize-space function. - */ - public static final String NAME_STRING_NORMALIZE_SPACE = XACML_NS_1_0 + "string-normalize-space"; - - /** - * Standard identifier for the string-normalize-to-lower-case function. - */ - public static final String NAME_STRING_NORMALIZE_TO_LOWER_CASE = XACML_NS_1_0 + "string-normalize-to-lower-case"; - - private interface StringNormalizer - { - StringValue normalize(StringValue value); - } - - private static final class CallFactory - { - - private final StringNormalizer strNormalizer; - private final FunctionSignature.SingleParameterTyped funcSig; - - public CallFactory(FunctionSignature.SingleParameterTyped functionSignature, StringNormalizer stringNormalizer) - { - this.funcSig = functionSignature; - this.strNormalizer = stringNormalizer; - } - - private FirstOrderFunctionCall getInstance(List> argExpressions, Datatype... remainingArgTypes) - throws IllegalArgumentException - { - return new EagerSinglePrimitiveTypeEval(funcSig, argExpressions, remainingArgTypes) - { - - @Override - protected StringValue evaluate(Deque argStack) throws IndeterminateEvaluationException - { - return strNormalizer.normalize(argStack.getFirst()); - } - - }; - } - } - - private static final StringNormalizer STRING_NORMALIZE_SPACE_FUNCTION_CALL_FACTORY = new StringNormalizer() - { - @Override - public StringValue normalize(StringValue value) - { - return value.trim(); - } - - }; - - private static final StringNormalizer STRING_NORMALIZE_TO_LOWER_CASE_FUNCTION_CALL_FACTORY = new StringNormalizer() - { - @Override - public StringValue normalize(StringValue value) - { - /* - * Specified by fn:lower-case function in [XF]. Looking at Saxon HE as our reference for Java open source implementation of XPath functions, we can - * check in Saxon implementation of fn:lower-case (LowerCase class), that this is equivalent to String#toLowerCase(); English locale to be used for - * Locale-insensitive strings, see String.toLowerCase() - */ - return value.toLowerCase(Locale.ENGLISH); - } - - }; - - private final CallFactory funcCallFactory; - - /** - * Creates a new StringNormalizeFunction object. - * - * @param functionName - * the standard XACML function URI - * - */ - private StringNormalizeFunction(String functionName, StringNormalizer stringNormalizer) - { - super(functionName, DatatypeConstants.STRING.TYPE, false, Collections.singletonList(DatatypeConstants.STRING.TYPE)); - this.funcCallFactory = new CallFactory(functionSignature, stringNormalizer); - } - - /** - * *-string-normalize-* function cluster - */ - public static final FunctionSet CLUSTER = new BaseFunctionSet(FunctionSet.DEFAULT_ID_NAMESPACE + "string-normalize", // - new StringNormalizeFunction(NAME_STRING_NORMALIZE_SPACE, STRING_NORMALIZE_SPACE_FUNCTION_CALL_FACTORY), // - new StringNormalizeFunction(NAME_STRING_NORMALIZE_TO_LOWER_CASE, STRING_NORMALIZE_TO_LOWER_CASE_FUNCTION_CALL_FACTORY)); - - /* - * (non-Javadoc) - * - * @see com.thalesgroup.authzforce.core.func.FirstOrderFunction#getFunctionCall(java.util.List, com.thalesgroup.authzforce.core.eval.DatatypeDef[]) - */ - @Override - public FirstOrderFunctionCall newCall(List> argExpressions, Datatype... remainingArgTypes) throws IllegalArgumentException - { - return funcCallFactory.getInstance(argExpressions, remainingArgTypes); - } - -} +/** + * + * Copyright 2003-2004 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * + * 1. Redistribution of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * + * 2. Redistribution 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 Sun Microsystems, Inc. or the names of contributors may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY IMPLIED + * WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS + * SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER + * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use in the design, construction, operation or maintenance of any nuclear facility. + */ +package com.sun.xacml; + +import java.util.Collections; +import java.util.Deque; +import java.util.List; +import java.util.Locale; + +import org.ow2.authzforce.core.pdp.api.Datatype; +import org.ow2.authzforce.core.pdp.api.Expression; +import org.ow2.authzforce.core.pdp.api.FirstOrderFunction; +import org.ow2.authzforce.core.pdp.api.FirstOrderFunctionCall; +import org.ow2.authzforce.core.pdp.api.FirstOrderFunctionCall.EagerSinglePrimitiveTypeEval; +import org.ow2.authzforce.core.pdp.api.FunctionSet; +import org.ow2.authzforce.core.pdp.api.FunctionSignature; +import org.ow2.authzforce.core.pdp.api.IndeterminateEvaluationException; +import org.ow2.authzforce.core.pdp.impl.func.BaseFunctionSet; +import org.ow2.authzforce.core.pdp.impl.value.DatatypeConstants; +import org.ow2.authzforce.core.pdp.impl.value.StringValue; + +/** + * string-normalize-* function + * + * @since 1.0 + * @author Steve Hanna + * @author Seth Proctor + * @version $Id: $ + */ +public final class StringNormalizeFunction extends FirstOrderFunction.SingleParameterTyped +{ + + /** + * Standard identifier for the string-normalize-space function. + */ + public static final String NAME_STRING_NORMALIZE_SPACE = XACML_NS_1_0 + "string-normalize-space"; + + /** + * Standard identifier for the string-normalize-to-lower-case function. + */ + public static final String NAME_STRING_NORMALIZE_TO_LOWER_CASE = XACML_NS_1_0 + "string-normalize-to-lower-case"; + + private interface StringNormalizer + { + StringValue normalize(StringValue value); + } + + private static final class CallFactory + { + + private final StringNormalizer strNormalizer; + private final FunctionSignature.SingleParameterTyped funcSig; + + public CallFactory(FunctionSignature.SingleParameterTyped functionSignature, StringNormalizer stringNormalizer) + { + this.funcSig = functionSignature; + this.strNormalizer = stringNormalizer; + } + + private FirstOrderFunctionCall getInstance(List> argExpressions, Datatype... remainingArgTypes) + throws IllegalArgumentException + { + return new EagerSinglePrimitiveTypeEval(funcSig, argExpressions, remainingArgTypes) + { + + @Override + protected StringValue evaluate(Deque argStack) throws IndeterminateEvaluationException + { + return strNormalizer.normalize(argStack.getFirst()); + } + + }; + } + } + + private static final StringNormalizer STRING_NORMALIZE_SPACE_FUNCTION_CALL_FACTORY = new StringNormalizer() + { + @Override + public StringValue normalize(StringValue value) + { + return value.trim(); + } + + }; + + private static final StringNormalizer STRING_NORMALIZE_TO_LOWER_CASE_FUNCTION_CALL_FACTORY = new StringNormalizer() + { + @Override + public StringValue normalize(StringValue value) + { + /* + * Specified by fn:lower-case function in [XF]. Looking at Saxon HE as our reference for Java open source implementation of XPath functions, we can + * check in Saxon implementation of fn:lower-case (LowerCase class), that this is equivalent to String#toLowerCase(); English locale to be used for + * Locale-insensitive strings, see String.toLowerCase() + */ + return value.toLowerCase(Locale.ENGLISH); + } + + }; + + private final CallFactory funcCallFactory; + + /** + * Creates a new StringNormalizeFunction object. + * + * @param functionName + * the standard XACML function URI + * + */ + private StringNormalizeFunction(String functionName, StringNormalizer stringNormalizer) + { + super(functionName, DatatypeConstants.STRING.TYPE, false, Collections.singletonList(DatatypeConstants.STRING.TYPE)); + this.funcCallFactory = new CallFactory(functionSignature, stringNormalizer); + } + + /** + * *-string-normalize-* function cluster + */ + public static final FunctionSet CLUSTER = new BaseFunctionSet(FunctionSet.DEFAULT_ID_NAMESPACE + "string-normalize", // + new StringNormalizeFunction(NAME_STRING_NORMALIZE_SPACE, STRING_NORMALIZE_SPACE_FUNCTION_CALL_FACTORY), // + new StringNormalizeFunction(NAME_STRING_NORMALIZE_TO_LOWER_CASE, STRING_NORMALIZE_TO_LOWER_CASE_FUNCTION_CALL_FACTORY)); + + /* + * (non-Javadoc) + * + * @see com.thalesgroup.authzforce.core.func.FirstOrderFunction#getFunctionCall(java.util.List, com.thalesgroup.authzforce.core.eval.DatatypeDef[]) + */ + /** {@inheritDoc} */ + @Override + public FirstOrderFunctionCall newCall(List> argExpressions, Datatype... remainingArgTypes) throws IllegalArgumentException + { + return funcCallFactory.getInstance(argExpressions, remainingArgTypes); + } + +} diff --git a/src/main/java/com/sun/xacml/TimeInRangeFunction.java b/src/main/java/com/sun/xacml/TimeInRangeFunction.java index 5b42be8a..8d26d22e 100644 --- a/src/main/java/com/sun/xacml/TimeInRangeFunction.java +++ b/src/main/java/com/sun/xacml/TimeInRangeFunction.java @@ -1,201 +1,203 @@ -/** - * - * Copyright 2003-2004 Sun Microsystems, Inc. All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * - * 1. Redistribution of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - * - * 2. Redistribution 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 Sun Microsystems, Inc. or the names of contributors may be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * This software is provided "AS IS," without a warranty of any kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY IMPLIED - * WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS - * SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL - * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER - * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGES. - * - * You acknowledge that this software is not designed or intended for use in the design, construction, operation or maintenance of any nuclear facility. - */ -package com.sun.xacml; - -import java.util.Arrays; -import java.util.Calendar; -import java.util.Deque; -import java.util.List; -import java.util.TimeZone; - -import org.ow2.authzforce.core.pdp.api.Datatype; -import org.ow2.authzforce.core.pdp.api.Expression; -import org.ow2.authzforce.core.pdp.api.FirstOrderFunction; -import org.ow2.authzforce.core.pdp.api.FirstOrderFunctionCall; -import org.ow2.authzforce.core.pdp.api.FunctionSignature; -import org.ow2.authzforce.core.pdp.api.IndeterminateEvaluationException; -import org.ow2.authzforce.core.pdp.impl.value.BooleanValue; -import org.ow2.authzforce.core.pdp.impl.value.DatatypeConstants; -import org.ow2.authzforce.core.pdp.impl.value.TimeValue; - -/** - * This class implements the time-in-range function, which takes three time values and returns true if the first value falls between the second and the third value. This function was introduced in - * XACML 2.0. - *

- * Note that this function allows any time ranges less than 24 hours. In other words, it is not bound by normal day boundries (midnight GMT), but by the minimum time in the range. This means that - * ranges like 9am-5pm are supported, as are ranges like 5pm-9am. - * - * @since 2.0 - * @author seth proctor - */ -public final class TimeInRangeFunction extends FirstOrderFunction.SingleParameterTyped -{ - - /** - * The identifier for this function - */ - public static final String NAME = XACML_NS_2_0 + "time-in-range"; - - /** - * Singleton instance: Effective Java, item 3 - */ - public static final TimeInRangeFunction INSTANCE = new TimeInRangeFunction(); - - /** - * Default constructor. - */ - private TimeInRangeFunction() - { - /** - * boolean timeInRange(time,time,time) - */ - super(NAME, DatatypeConstants.BOOLEAN.TYPE, false, Arrays.asList(DatatypeConstants.TIME.TYPE, DatatypeConstants.TIME.TYPE, DatatypeConstants.TIME.TYPE)); - } - - private static final class Call extends FirstOrderFunctionCall.EagerSinglePrimitiveTypeEval - { - private static final TimeZone DEFAULT_TZ = TimeZone.getDefault(); - - /** - * Set {@code cal}'s date to the same as {@code ref}'s date - * - * @param cal - * @param ref - */ - private static void setSameDate(Calendar cal, Calendar ref) - { - cal.set(Calendar.YEAR, ref.get(Calendar.YEAR)); - cal.set(Calendar.DAY_OF_YEAR, ref.get(Calendar.DAY_OF_YEAR)); - } - - /** - * Evaluates the time-in-range function, which takes three TimeAttributeValue values. This function return true if the first value falls between the second and third values (ie., - * on or after the second time and on or before the third time). If no time zone is specified for the second and/or third time value, then the timezone from the first time value is used. This - * lets you say time-in-range(current-time, 9am, 5pm) and always have the evaluation happen in your current-time timezone. - * - * @param arg - * time to be checked against the lower and upper bounds - * @param lowerBound - * lower time bound - * @param upperBound - * upper time bound - * @return true iff arg is in range [lowerBound, upperBound] - * - * - */ - public static boolean eval(TimeValue arg, TimeValue lowerBound, TimeValue upperBound) - { - // get the three time values - final Calendar calCheckedWhetherInRange = arg.getUnderlyingValue().toGregorianCalendar(); - if (calCheckedWhetherInRange.getTimeZone() == null) - { - calCheckedWhetherInRange.setTimeZone(DEFAULT_TZ); - } - - final Calendar startCal = lowerBound.getUnderlyingValue().toGregorianCalendar(); - if (startCal.getTimeZone() == null) - { - startCal.setTimeZone(calCheckedWhetherInRange.getTimeZone()); - } - final Calendar endCal = upperBound.getUnderlyingValue().toGregorianCalendar(); - if (endCal.getTimeZone() == null) - { - endCal.setTimeZone(calCheckedWhetherInRange.getTimeZone()); - } - - /* - * Use start time as reference for the day in time comparisons, so set the timeChecked day to the one of the start time - */ - setSameDate(calCheckedWhetherInRange, startCal); - /* - * Now we date does not matter in calendar comparison, we only compare times of the day so ignoring the date, the checked time of the day might be before the lower time bound but still be - * in range if considered this is the time on the next day. In this case, startCal is on day N, and calCheckedWhetherInRange on day N+1. - */ - /* - * Boolean below says whether the checked time is strictly after the start time if considered on the *same day*, i.e. in terms of time of day. - */ - final boolean isCheckedDayTimeStrictlyBeforeStartDayTime = calCheckedWhetherInRange.before(startCal); - if (startCal.after(endCal)) - { - /** - * start time of the day > end time of the day, for instance 02:00:00 > 01:00:00 so we consider the end time (01:00:00) on the next day (later than the second argument - end time - by - * less than 24h, the spec says). So we interpret the time interval as the date interval [startTime on day N, endTime on day N+1]. If checked time of day < start time of day (compared - * on the same day), then checked time can only be on day after to be in range - */ - if (isCheckedDayTimeStrictlyBeforeStartDayTime) - { - /* - * time checked is strictly before start time if considered on the same day, so not in range unless considered on day N+1 So let's compared with end time after considering them on - * the same day - */ - // calCheckedWhetherInRange.add(Calendar.DAY_OF_YEAR, 1); - // set checked time to same day as end time for comparison - setSameDate(calCheckedWhetherInRange, endCal); - // time checked is in range if and only if before or equals end time (on day N+1), - // i.e. not strictly after - return !calCheckedWhetherInRange.after(endCal); - } - - /* - * Time checked is after or equal to start time, so it is in range (on day N), as we already consider end time to be on day N+1 - */ - return true; - } - - // start time <= end time -> all considered on the same day - if (isCheckedDayTimeStrictlyBeforeStartDayTime) - { - // checked time < start time -> out of range - return false; - } - - // checked time >= start time - - // set checked time to same day as end time for comparison - setSameDate(calCheckedWhetherInRange, endCal); - // time checked is in range if and only if before or equals end time, so not strictly after - return !calCheckedWhetherInRange.after(endCal); - } - - private Call(FunctionSignature.SingleParameterTyped functionSignature, List> argExpressions, Datatype... remainingArgTypes) - { - super(functionSignature, argExpressions, remainingArgTypes); - } - - @Override - protected BooleanValue evaluate(Deque argStack) throws IndeterminateEvaluationException - { - /* - * args.poll() returns the first element and remove it from the stack, so that next poll() returns the next element (and removes it from the stack), etc. - */ - return BooleanValue.valueOf(eval(argStack.poll(), argStack.poll(), argStack.poll())); - } - } - - @Override - public FirstOrderFunctionCall newCall(List> argExpressions, Datatype... remainingArgTypes) throws IllegalArgumentException - { - return new Call(functionSignature, argExpressions, remainingArgTypes); - } -} +/** + * + * Copyright 2003-2004 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * + * 1. Redistribution of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * + * 2. Redistribution 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 Sun Microsystems, Inc. or the names of contributors may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY IMPLIED + * WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS + * SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER + * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use in the design, construction, operation or maintenance of any nuclear facility. + */ +package com.sun.xacml; + +import java.util.Arrays; +import java.util.Calendar; +import java.util.Deque; +import java.util.List; +import java.util.TimeZone; + +import org.ow2.authzforce.core.pdp.api.Datatype; +import org.ow2.authzforce.core.pdp.api.Expression; +import org.ow2.authzforce.core.pdp.api.FirstOrderFunction; +import org.ow2.authzforce.core.pdp.api.FirstOrderFunctionCall; +import org.ow2.authzforce.core.pdp.api.FunctionSignature; +import org.ow2.authzforce.core.pdp.api.IndeterminateEvaluationException; +import org.ow2.authzforce.core.pdp.impl.value.BooleanValue; +import org.ow2.authzforce.core.pdp.impl.value.DatatypeConstants; +import org.ow2.authzforce.core.pdp.impl.value.TimeValue; + +/** + * This class implements the time-in-range function, which takes three time values and returns true if the first value falls between the second and the third value. This function was introduced in + * XACML 2.0. + *

+ * Note that this function allows any time ranges less than 24 hours. In other words, it is not bound by normal day boundries (midnight GMT), but by the minimum time in the range. This means that + * ranges like 9am-5pm are supported, as are ranges like 5pm-9am. + * + * @since 2.0 + * @author seth proctor + * @version $Id: $ + */ +public final class TimeInRangeFunction extends FirstOrderFunction.SingleParameterTyped +{ + + /** + * The identifier for this function + */ + public static final String NAME = XACML_NS_2_0 + "time-in-range"; + + /** + * Singleton instance: Effective Java, item 3 + */ + public static final TimeInRangeFunction INSTANCE = new TimeInRangeFunction(); + + /** + * Default constructor. + */ + private TimeInRangeFunction() + { + /** + * boolean timeInRange(time,time,time) + */ + super(NAME, DatatypeConstants.BOOLEAN.TYPE, false, Arrays.asList(DatatypeConstants.TIME.TYPE, DatatypeConstants.TIME.TYPE, DatatypeConstants.TIME.TYPE)); + } + + private static final class Call extends FirstOrderFunctionCall.EagerSinglePrimitiveTypeEval + { + private static final TimeZone DEFAULT_TZ = TimeZone.getDefault(); + + /** + * Set {@code cal}'s date to the same as {@code ref}'s date + * + * @param cal + * @param ref + */ + private static void setSameDate(Calendar cal, Calendar ref) + { + cal.set(Calendar.YEAR, ref.get(Calendar.YEAR)); + cal.set(Calendar.DAY_OF_YEAR, ref.get(Calendar.DAY_OF_YEAR)); + } + + /** + * Evaluates the time-in-range function, which takes three TimeAttributeValue values. This function return true if the first value falls between the second and third values (ie., + * on or after the second time and on or before the third time). If no time zone is specified for the second and/or third time value, then the timezone from the first time value is used. This + * lets you say time-in-range(current-time, 9am, 5pm) and always have the evaluation happen in your current-time timezone. + * + * @param arg + * time to be checked against the lower and upper bounds + * @param lowerBound + * lower time bound + * @param upperBound + * upper time bound + * @return true iff arg is in range [lowerBound, upperBound] + * + * + */ + public static boolean eval(TimeValue arg, TimeValue lowerBound, TimeValue upperBound) + { + // get the three time values + final Calendar calCheckedWhetherInRange = arg.getUnderlyingValue().toGregorianCalendar(); + if (calCheckedWhetherInRange.getTimeZone() == null) + { + calCheckedWhetherInRange.setTimeZone(DEFAULT_TZ); + } + + final Calendar startCal = lowerBound.getUnderlyingValue().toGregorianCalendar(); + if (startCal.getTimeZone() == null) + { + startCal.setTimeZone(calCheckedWhetherInRange.getTimeZone()); + } + final Calendar endCal = upperBound.getUnderlyingValue().toGregorianCalendar(); + if (endCal.getTimeZone() == null) + { + endCal.setTimeZone(calCheckedWhetherInRange.getTimeZone()); + } + + /* + * Use start time as reference for the day in time comparisons, so set the timeChecked day to the one of the start time + */ + setSameDate(calCheckedWhetherInRange, startCal); + /* + * Now we date does not matter in calendar comparison, we only compare times of the day so ignoring the date, the checked time of the day might be before the lower time bound but still be + * in range if considered this is the time on the next day. In this case, startCal is on day N, and calCheckedWhetherInRange on day N+1. + */ + /* + * Boolean below says whether the checked time is strictly after the start time if considered on the *same day*, i.e. in terms of time of day. + */ + final boolean isCheckedDayTimeStrictlyBeforeStartDayTime = calCheckedWhetherInRange.before(startCal); + if (startCal.after(endCal)) + { + /** + * start time of the day > end time of the day, for instance 02:00:00 > 01:00:00 so we consider the end time (01:00:00) on the next day (later than the second argument - end time - by + * less than 24h, the spec says). So we interpret the time interval as the date interval [startTime on day N, endTime on day N+1]. If checked time of day < start time of day (compared + * on the same day), then checked time can only be on day after to be in range + */ + if (isCheckedDayTimeStrictlyBeforeStartDayTime) + { + /* + * time checked is strictly before start time if considered on the same day, so not in range unless considered on day N+1 So let's compared with end time after considering them on + * the same day + */ + // calCheckedWhetherInRange.add(Calendar.DAY_OF_YEAR, 1); + // set checked time to same day as end time for comparison + setSameDate(calCheckedWhetherInRange, endCal); + // time checked is in range if and only if before or equals end time (on day N+1), + // i.e. not strictly after + return !calCheckedWhetherInRange.after(endCal); + } + + /* + * Time checked is after or equal to start time, so it is in range (on day N), as we already consider end time to be on day N+1 + */ + return true; + } + + // start time <= end time -> all considered on the same day + if (isCheckedDayTimeStrictlyBeforeStartDayTime) + { + // checked time < start time -> out of range + return false; + } + + // checked time >= start time + + // set checked time to same day as end time for comparison + setSameDate(calCheckedWhetherInRange, endCal); + // time checked is in range if and only if before or equals end time, so not strictly after + return !calCheckedWhetherInRange.after(endCal); + } + + private Call(FunctionSignature.SingleParameterTyped functionSignature, List> argExpressions, Datatype... remainingArgTypes) + { + super(functionSignature, argExpressions, remainingArgTypes); + } + + @Override + protected BooleanValue evaluate(Deque argStack) throws IndeterminateEvaluationException + { + /* + * args.poll() returns the first element and remove it from the stack, so that next poll() returns the next element (and removes it from the stack), etc. + */ + return BooleanValue.valueOf(eval(argStack.poll(), argStack.poll(), argStack.poll())); + } + } + + /** {@inheritDoc} */ + @Override + public FirstOrderFunctionCall newCall(List> argExpressions, Datatype... remainingArgTypes) throws IllegalArgumentException + { + return new Call(functionSignature, argExpressions, remainingArgTypes); + } +} diff --git a/src/main/java/com/sun/xacml/UnknownIdentifierException.java b/src/main/java/com/sun/xacml/UnknownIdentifierException.java index 17b4d042..800653ff 100644 --- a/src/main/java/com/sun/xacml/UnknownIdentifierException.java +++ b/src/main/java/com/sun/xacml/UnknownIdentifierException.java @@ -1,59 +1,60 @@ -/** - * - * Copyright 2003-2004 Sun Microsystems, Inc. All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * - * 1. Redistribution of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - * - * 2. Redistribution 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 Sun Microsystems, Inc. or the names of contributors may be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * This software is provided "AS IS," without a warranty of any kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY IMPLIED - * WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS - * SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL - * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER - * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGES. - * - * You acknowledge that this software is not designed or intended for use in the design, construction, operation or maintenance of any nuclear facility. - */ -package com.sun.xacml; - -/** - * Exception that gets thrown if an unknown identifier was used, such as the identifier used in any of the standard factories. - * - * @since 1.0 - * @author Seth Proctor - */ -public class UnknownIdentifierException extends Exception -{ - - /** - * - */ - private static final long serialVersionUID = 1L; - - /** - * Creates an UnknownIdentifierException with no data - */ - public UnknownIdentifierException() - { - - } - - /** - * Creates an UnknownIdentifierException with a message - * - * @param message - * the message - */ - public UnknownIdentifierException(String message) - { - super(message); - } - -} +/** + * + * Copyright 2003-2004 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * + * 1. Redistribution of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * + * 2. Redistribution 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 Sun Microsystems, Inc. or the names of contributors may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY IMPLIED + * WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS + * SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER + * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use in the design, construction, operation or maintenance of any nuclear facility. + */ +package com.sun.xacml; + +/** + * Exception that gets thrown if an unknown identifier was used, such as the identifier used in any of the standard factories. + * + * @since 1.0 + * @author Seth Proctor + * @version $Id: $ + */ +public class UnknownIdentifierException extends Exception +{ + + /** + * + */ + private static final long serialVersionUID = 1L; + + /** + * Creates an UnknownIdentifierException with no data + */ + public UnknownIdentifierException() + { + + } + + /** + * Creates an UnknownIdentifierException with a message + * + * @param message + * the message + */ + public UnknownIdentifierException(String message) + { + super(message); + } + +} diff --git a/src/main/java/org/ow2/authzforce/core/pdp/impl/AllOfEvaluator.java b/src/main/java/org/ow2/authzforce/core/pdp/impl/AllOfEvaluator.java index 32eefcb7..6a1bddb8 100644 --- a/src/main/java/org/ow2/authzforce/core/pdp/impl/AllOfEvaluator.java +++ b/src/main/java/org/ow2/authzforce/core/pdp/impl/AllOfEvaluator.java @@ -28,7 +28,9 @@ import org.slf4j.LoggerFactory; /** * XACML AllOf evaluator - * + * + * @author cdangerv + * @version $Id: $ */ public class AllOfEvaluator { @@ -42,14 +44,14 @@ public class AllOfEvaluator /** * Instantiates AllOf (evaluator) from XACML-Schema-derived AllOf. - * + * * @param jaxbAllOf * XACML-schema-derived JAXB AllOf * @param xPathCompiler * XPath compiler corresponding to enclosing policy(set) default XPath version * @param expFactory * Expression factory - * @throws IllegalArgumentException + * @throws java.lang.IllegalArgumentException * one of the child Match elements is invalid */ public AllOfEvaluator(AllOf jaxbAllOf, XPathCompiler xPathCompiler, ExpressionFactory expFactory) throws IllegalArgumentException @@ -80,18 +82,17 @@ public class AllOfEvaluator /** * Determines whether this AllOf matches the input request (whether it is applicable).Here is the table shown in the specification: - * values value - * All True “Match�? - * No False and at least + * values value + * All True “Match�? + * No False and at least * one "Indeterminate" “Indeterminate�? * At least one False "No Match" * - * + * * @param context * the representation of the request - * * @return true iff Match, else No match - * @throws IndeterminateEvaluationException + * @throws org.ow2.authzforce.core.pdp.api.IndeterminateEvaluationException * Indeterminate */ public boolean match(EvaluationContext context) throws IndeterminateEvaluationException @@ -147,4 +148,4 @@ public class AllOfEvaluator throw new IndeterminateEvaluationException("Error evaluating 's #" + lastIndeterminateChildIndex, lastIndeterminate.getStatusCode(), lastIndeterminate); } -} \ No newline at end of file +} diff --git a/src/main/java/org/ow2/authzforce/core/pdp/impl/AnyOfEvaluator.java b/src/main/java/org/ow2/authzforce/core/pdp/impl/AnyOfEvaluator.java index 0a01e842..cae549e8 100644 --- a/src/main/java/org/ow2/authzforce/core/pdp/impl/AnyOfEvaluator.java +++ b/src/main/java/org/ow2/authzforce/core/pdp/impl/AnyOfEvaluator.java @@ -31,7 +31,9 @@ import org.slf4j.LoggerFactory; /** * AnyOf evaluator - * + * + * @author cdangerv + * @version $Id: $ */ public class AnyOfEvaluator { @@ -45,15 +47,14 @@ public class AnyOfEvaluator /** * Constructor that creates a new AnyOf evaluator based on the given XACML-schema-derived JAXB AnyOf. - * + * * @param jaxbAnyOf * JAXB AnyOf * @param xPathCompiler * XPath compiler corresponding to enclosing policy(set) default XPath version * @param expFactory * Expression factory - * - * @throws IllegalArgumentException + * @throws java.lang.IllegalArgumentException * if one of the child AllOf elements is invalid */ public AnyOfEvaluator(AnyOf jaxbAnyOf, XPathCompiler xPathCompiler, ExpressionFactory expFactory) throws IllegalArgumentException @@ -85,20 +86,19 @@ public class AnyOfEvaluator /** * Determines whether this AnyOf matches the input request (whether it is applicable). If all the AllOf values is No_Match so it's a No_Match. * If all matches it's a Match. If None matches and at least one “Indeterminate�? it's Indeterminate - * + * *

-	 * 		AllOf values 						AnyOf value 
-	 * 		At Least one "Match"	 			“Match�? 
-	 * 		None matches and 
+	 * 		AllOf values 						AnyOf value
+	 * 		At Least one "Match"	 			“Match�?
+	 * 		None matches and
 	 * 		at least one Indeterminate 			“Indeterminate�?
 	 * 		All "No Match"						"No Match"
 	 * 
- * + * * @param context * the representation of the request - * * @return true if and only if Match (else No-match) - * @throws IndeterminateEvaluationException + * @throws org.ow2.authzforce.core.pdp.api.IndeterminateEvaluationException * if Indeterminate */ public boolean match(EvaluationContext context) throws IndeterminateEvaluationException diff --git a/src/main/java/org/ow2/authzforce/core/pdp/impl/AttributeAssignmentExpressionEvaluator.java b/src/main/java/org/ow2/authzforce/core/pdp/impl/AttributeAssignmentExpressionEvaluator.java index 0c285cdc..a56e1a86 100644 --- a/src/main/java/org/ow2/authzforce/core/pdp/impl/AttributeAssignmentExpressionEvaluator.java +++ b/src/main/java/org/ow2/authzforce/core/pdp/impl/AttributeAssignmentExpressionEvaluator.java @@ -1,158 +1,162 @@ -/** - * Copyright (C) 2011-2015 Thales Services SAS. - * - * This file is part of AuthZForce. - * - * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software - * Foundation, either version 3 of the License, or (at your option) any later version. - * - * AuthZForce 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . - */ -/** - * - */ -package org.ow2.authzforce.core.pdp.impl; - -import java.util.ArrayList; -import java.util.List; - -import javax.xml.bind.JAXBElement; - -import net.sf.saxon.s9api.XPathCompiler; -import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeAssignment; -import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeAssignmentExpression; -import oasis.names.tc.xacml._3_0.core.schema.wd_17.ExpressionType; - -import org.ow2.authzforce.core.pdp.api.AttributeValue; -import org.ow2.authzforce.core.pdp.api.Bag; -import org.ow2.authzforce.core.pdp.api.EvaluationContext; -import org.ow2.authzforce.core.pdp.api.Expression; -import org.ow2.authzforce.core.pdp.api.ExpressionFactory; -import org.ow2.authzforce.core.pdp.api.IndeterminateEvaluationException; -import org.ow2.authzforce.core.pdp.api.Value; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * XACML AttributeAssignmentExpression evaluator - * - */ -public class AttributeAssignmentExpressionEvaluator extends AttributeAssignmentExpression -{ - private static final Logger LOGGER = LoggerFactory.getLogger(AttributeAssignmentExpressionEvaluator.class); - - private final transient Expression evaluatableExpression; - - private static final UnsupportedOperationException UNSUPPORTED_SET_EXPRESSION_OPERATION_EXCEPTION = new UnsupportedOperationException( - "Unsupported operation: 'Expression' attribute is read-only"); - - /* - * (non-Javadoc) - * - * @see oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeAssignmentExpression#getExpression() - */ - @Override - public final JAXBElement getExpression() - { - return evaluatableExpression.getJAXBElement(); - } - - /* - * (non-Javadoc) - * - * @see oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeAssignmentExpression#setExpression(javax .xml.bind.JAXBElement) - */ - @Override - public final void setExpression(JAXBElement value) - { - throw UNSUPPORTED_SET_EXPRESSION_OPERATION_EXCEPTION; - } - - /** - * Instantiates evaluatable AttributeAssignment expression from XACML-Schema-derived JAXB - * {@link oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeAssignmentExpression} - * - * @param jaxbAttrAssignExp - * XACML-schema-derived JAXB AttributeAssignmentExpression - * @param xPathCompiler - * XPath compiler corresponding to enclosing policy(set) default XPath version - * @param expFactory - * expression factory for parsing the AttributeAssignmentExpression's expression - * @throws IllegalArgumentException - * invalid AttributeAssignmentExpression's Expression - */ - public AttributeAssignmentExpressionEvaluator(AttributeAssignmentExpression jaxbAttrAssignExp, XPathCompiler xPathCompiler, ExpressionFactory expFactory) - throws IllegalArgumentException - { - // JAXB fields - this.attributeId = jaxbAttrAssignExp.getAttributeId(); - this.category = jaxbAttrAssignExp.getCategory(); - this.issuer = jaxbAttrAssignExp.getIssuer(); - /* - * Set JAXB field to null, getExpression() overridden and setExpression() not allowed instead - */ - this.expression = null; - // END OF JAXB fields - - this.evaluatableExpression = expFactory.getInstance(jaxbAttrAssignExp.getExpression().getValue(), xPathCompiler, null); - } - - /** - * Evaluates to AttributeAssignments Section 5.39 and 5.40 of XACML 3.0 core spec: If an AttributeAssignmentExpression evaluates to an atomic attribute - * value, then there MUST be one resulting AttributeAssignment which MUST contain this single attribute value. If the AttributeAssignmentExpression - * evaluates to a bag, then there MUST be a resulting AttributeAssignment for each of the values in the bag. If the bag is empty, there shall be no - * AttributeAssignment from this AttributeAssignmentExpression - * - * @param context - * evaluation context - * @return non-null AttributeAssignments; empty if no AttributeValue resulting from evaluation of the Expression - * @throws IndeterminateEvaluationException - * if evaluation of the Expression in this context fails (Indeterminate) - */ - public List evaluate(EvaluationContext context) throws IndeterminateEvaluationException - { - final Value result = this.evaluatableExpression.evaluate(context); - LOGGER.debug("AttributeAssignmentExpression[Category={},Issuer={},Id={}]/Expression -> {}", this.category, this.issuer, this.attributeId, result); - - final List attrAssignList = new ArrayList<>(); - if (result instanceof Bag) - { - // result is a bag - final Bag bag = (Bag) result; - /* - * Bag may be empty, in particular if AttributeDesignator/AttributeSelector with MustBePresent=False evaluates to empty bag. Sections 5.30/5.40 of - * XACML core spec says: "If the bag is empty, there shall be no from this ." - */ - for (final AttributeValue attrVal : bag) - { - final AttributeAssignment attrAssignment = new AttributeAssignment(attrVal.getContent(), attrVal.getDataType(), attrVal.getOtherAttributes(), - this.attributeId, this.category, this.issuer); - attrAssignList.add(attrAssignment); - } - } else - { - // atomic (see spec §5.30, 5.40) / primitive attribute value - final AttributeValue attrVal = (AttributeValue) result; - final AttributeAssignment attrAssignment = new AttributeAssignment(attrVal.getContent(), attrVal.getDataType(), attrVal.getOtherAttributes(), - this.attributeId, this.category, this.issuer); - attrAssignList.add(attrAssignment); - } - - return attrAssignList; - } - - // public static void main(String[] args) throws JAXBException - // { - // THIS WILL FAIL: com.sun.istack.internal.SAXException2: unable to marshal type "java.lang.Double" as an element because it is missing an - // @XmlRootElement annotation; but it succeeds with java.lang.String - // final AttributeAssignment attrAssignment = new AttributeAssignment(Collections. singletonList("1.0"), "mytype", null, "myattribute1", - // "mycategory", null); - // - // Marshaller marshaller = XACMLBindingUtils.createXacml3Marshaller(); - // marshaller.marshal(attrAssignment, System.out); - // } - -} +/** + * Copyright (C) 2011-2015 Thales Services SAS. + * + * This file is part of AuthZForce. + * + * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * AuthZForce 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . + */ +/** + * + */ +package org.ow2.authzforce.core.pdp.impl; + +import java.util.ArrayList; +import java.util.List; + +import javax.xml.bind.JAXBElement; + +import net.sf.saxon.s9api.XPathCompiler; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeAssignment; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeAssignmentExpression; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.ExpressionType; + +import org.ow2.authzforce.core.pdp.api.AttributeValue; +import org.ow2.authzforce.core.pdp.api.Bag; +import org.ow2.authzforce.core.pdp.api.EvaluationContext; +import org.ow2.authzforce.core.pdp.api.Expression; +import org.ow2.authzforce.core.pdp.api.ExpressionFactory; +import org.ow2.authzforce.core.pdp.api.IndeterminateEvaluationException; +import org.ow2.authzforce.core.pdp.api.Value; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * XACML AttributeAssignmentExpression evaluator + * + * @author cdangerv + * @version $Id: $ + */ +public class AttributeAssignmentExpressionEvaluator extends AttributeAssignmentExpression +{ + private static final Logger LOGGER = LoggerFactory.getLogger(AttributeAssignmentExpressionEvaluator.class); + + private final transient Expression evaluatableExpression; + + private static final UnsupportedOperationException UNSUPPORTED_SET_EXPRESSION_OPERATION_EXCEPTION = new UnsupportedOperationException( + "Unsupported operation: 'Expression' attribute is read-only"); + + /* + * (non-Javadoc) + * + * @see oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeAssignmentExpression#getExpression() + */ + /** {@inheritDoc} */ + @Override + public final JAXBElement getExpression() + { + return evaluatableExpression.getJAXBElement(); + } + + /* + * (non-Javadoc) + * + * @see oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeAssignmentExpression#setExpression(javax .xml.bind.JAXBElement) + */ + /** {@inheritDoc} */ + @Override + public final void setExpression(JAXBElement value) + { + throw UNSUPPORTED_SET_EXPRESSION_OPERATION_EXCEPTION; + } + + /** + * Instantiates evaluatable AttributeAssignment expression from XACML-Schema-derived JAXB + * {@link oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeAssignmentExpression} + * + * @param jaxbAttrAssignExp + * XACML-schema-derived JAXB AttributeAssignmentExpression + * @param xPathCompiler + * XPath compiler corresponding to enclosing policy(set) default XPath version + * @param expFactory + * expression factory for parsing the AttributeAssignmentExpression's expression + * @throws java.lang.IllegalArgumentException + * invalid AttributeAssignmentExpression's Expression + */ + public AttributeAssignmentExpressionEvaluator(AttributeAssignmentExpression jaxbAttrAssignExp, XPathCompiler xPathCompiler, ExpressionFactory expFactory) + throws IllegalArgumentException + { + // JAXB fields + this.attributeId = jaxbAttrAssignExp.getAttributeId(); + this.category = jaxbAttrAssignExp.getCategory(); + this.issuer = jaxbAttrAssignExp.getIssuer(); + /* + * Set JAXB field to null, getExpression() overridden and setExpression() not allowed instead + */ + this.expression = null; + // END OF JAXB fields + + this.evaluatableExpression = expFactory.getInstance(jaxbAttrAssignExp.getExpression().getValue(), xPathCompiler, null); + } + + /** + * Evaluates to AttributeAssignments Section 5.39 and 5.40 of XACML 3.0 core spec: If an AttributeAssignmentExpression evaluates to an atomic attribute + * value, then there MUST be one resulting AttributeAssignment which MUST contain this single attribute value. If the AttributeAssignmentExpression + * evaluates to a bag, then there MUST be a resulting AttributeAssignment for each of the values in the bag. If the bag is empty, there shall be no + * AttributeAssignment from this AttributeAssignmentExpression + * + * @param context + * evaluation context + * @return non-null AttributeAssignments; empty if no AttributeValue resulting from evaluation of the Expression + * @throws org.ow2.authzforce.core.pdp.api.IndeterminateEvaluationException + * if evaluation of the Expression in this context fails (Indeterminate) + */ + public List evaluate(EvaluationContext context) throws IndeterminateEvaluationException + { + final Value result = this.evaluatableExpression.evaluate(context); + LOGGER.debug("AttributeAssignmentExpression[Category={},Issuer={},Id={}]/Expression -> {}", this.category, this.issuer, this.attributeId, result); + + final List attrAssignList = new ArrayList<>(); + if (result instanceof Bag) + { + // result is a bag + final Bag bag = (Bag) result; + /* + * Bag may be empty, in particular if AttributeDesignator/AttributeSelector with MustBePresent=False evaluates to empty bag. Sections 5.30/5.40 of + * XACML core spec says: "If the bag is empty, there shall be no from this ." + */ + for (final AttributeValue attrVal : bag) + { + final AttributeAssignment attrAssignment = new AttributeAssignment(attrVal.getContent(), attrVal.getDataType(), attrVal.getOtherAttributes(), + this.attributeId, this.category, this.issuer); + attrAssignList.add(attrAssignment); + } + } else + { + // atomic (see spec §5.30, 5.40) / primitive attribute value + final AttributeValue attrVal = (AttributeValue) result; + final AttributeAssignment attrAssignment = new AttributeAssignment(attrVal.getContent(), attrVal.getDataType(), attrVal.getOtherAttributes(), + this.attributeId, this.category, this.issuer); + attrAssignList.add(attrAssignment); + } + + return attrAssignList; + } + + // public static void main(String[] args) throws JAXBException + // { + // THIS WILL FAIL: com.sun.istack.internal.SAXException2: unable to marshal type "java.lang.Double" as an element because it is missing an + // @XmlRootElement annotation; but it succeeds with java.lang.String + // final AttributeAssignment attrAssignment = new AttributeAssignment(Collections. singletonList("1.0"), "mytype", null, "myattribute1", + // "mycategory", null); + // + // Marshaller marshaller = XACMLBindingUtils.createXacml3Marshaller(); + // marshaller.marshal(attrAssignment, System.out); + // } + +} diff --git a/src/main/java/org/ow2/authzforce/core/pdp/impl/BaseDecisionResult.java b/src/main/java/org/ow2/authzforce/core/pdp/impl/BaseDecisionResult.java index d6a2ffef..d2e38e30 100644 --- a/src/main/java/org/ow2/authzforce/core/pdp/impl/BaseDecisionResult.java +++ b/src/main/java/org/ow2/authzforce/core/pdp/impl/BaseDecisionResult.java @@ -1,306 +1,308 @@ -/** - * Copyright (C) 2011-2015 Thales Services SAS. - * - * This file is part of AuthZForce. - * - * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software - * Foundation, either version 3 of the License, or (at your option) any later version. - * - * AuthZForce 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . - */ -/** - * - */ -package org.ow2.authzforce.core.pdp.impl; - -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; - -import javax.xml.bind.JAXBElement; - -import oasis.names.tc.xacml._3_0.core.schema.wd_17.DecisionType; -import oasis.names.tc.xacml._3_0.core.schema.wd_17.IdReferenceType; -import oasis.names.tc.xacml._3_0.core.schema.wd_17.Status; - -import org.ow2.authzforce.core.pdp.api.DecisionResult; -import org.ow2.authzforce.core.pdp.api.PepActions; - -/** - * Base implementation of DecisionResult - * - */ -public final class BaseDecisionResult implements DecisionResult -{ - private static final IllegalArgumentException ILLEGAL_DECISION_ARGUMENT_EXCEPTION = new IllegalArgumentException( - "Undefined Decision"); - - /** - * NotApplicable decision result - */ - public static final DecisionResult NOT_APPLICABLE = new BaseDecisionResult(DecisionType.NOT_APPLICABLE, null); - - /** - * Deny result with no obligation/advice/Included attribute/policy identifiers. Deny decision and nothing else. - */ - public static final DecisionResult DENY = new BaseDecisionResult(DecisionType.DENY, null); - - /** - * Permit result with no obligation/advice/Included attribute/policy identifiers. Permit decision and nothing else. - */ - public static final DecisionResult PERMIT = new BaseDecisionResult(DecisionType.PERMIT, null); - - private final DecisionType decision; - - /** - * Extended Indeterminate value, as defined in section 7.10 of XACML 3.0 core: potential effect value which could - * have occurred if there would not have been an error causing the “Indeterminate”. We use the following - * convention: - *
    - *
  • {@link DecisionType#DENY} means "Indeterminate{D}"
  • - *
  • {@link DecisionType#PERMIT} means "Indeterminate{P}"
  • - *
  • Null means "Indeterminate{DP}"
  • - *
  • {@link DecisionType#NOT_APPLICABLE} is the default value and means the decision is not Indeterminate, and - * therefore any extended Indeterminate value should be ignored
  • - *
- * - */ - private final DecisionType extIndeterminate; - - private final Status status; - - // initialized non-null - private final PepActions pepActions; - - // initialized non-null - private final List> applicablePolicyIdList; - - /** - * Instantiates a generic Decision result - * - * @param decision - * decision - * @param extendedIndeterminate - * Extended Indeterminate value, null if {@code decision != DecisionType.INDETERMINATE} - * @param status - * status - * @param pepActions - * PEP actions (obligations/advices) - * @param policyIdentifierList - * list of matched policy identifiers - */ - public BaseDecisionResult(DecisionType decision, DecisionType extendedIndeterminate, Status status, - PepActions pepActions, List> policyIdentifierList) - { - if (decision == null) - { - throw ILLEGAL_DECISION_ARGUMENT_EXCEPTION; - } - - this.decision = decision; - this.extIndeterminate = extendedIndeterminate; - this.status = status; - this.pepActions = pepActions == null ? new BasePepActions(null, null) : pepActions; - this.applicablePolicyIdList = policyIdentifierList == null ? new ArrayList>() - : policyIdentifierList; - - } - - /** - * Instantiates a Indeterminate Decision result with a given error status - * - * @param extendedIndeterminate - * Extended Indeterminate value (XACML 3.0 Core, section 7.10). We use the following convention: - *
    - *
  • {@link DecisionType#DENY} means "Indeterminate{D}"
  • - *
  • {@link DecisionType#PERMIT} means "Indeterminate{P}"
  • - *
  • {@link DecisionType#INDETERMINATE} means "Indeterminate{DP}"
  • - *
  • {@link DecisionType#NOT_APPLICABLE} is the default value and means the decision is not - * Indeterminate, and therefore any extended Indeterminate value should be ignored
  • - *
- * - * @param status - * reason/code for Indeterminate - */ - public BaseDecisionResult(Status status, DecisionType extendedIndeterminate) - { - this(DecisionType.INDETERMINATE, extendedIndeterminate, status, null, null); - } - - /** - * Instantiates a Indeterminate Decision result with a given error status and extended Indeterminate set to - * Indeterminate{DP} - * - * @param status - * reason/code for Indeterminate - */ - public BaseDecisionResult(Status status) - { - this(DecisionType.INDETERMINATE, DecisionType.INDETERMINATE, status, null, null); - } - - /** - * Instantiates a Permit/Deny decision with optional obligations and advice. See - * {@link #BaseDecisionResult(Status, DecisionType)} for Indeterminate, and {@link #NOT_APPLICABLE} for - * NotApplicable. - * - * @param decision - * decision - * @param pepActions - * PEP actions (obligations/advices) - */ - public BaseDecisionResult(DecisionType decision, PepActions pepActions) - { - this(decision, DecisionType.NOT_APPLICABLE, null, pepActions, null); - } - - private transient volatile int hashCode = 0; - - @Override - public int hashCode() - { - if (hashCode == 0) - { - hashCode = Objects.hash(this.decision, this.extIndeterminate, this.status, this.pepActions, - this.applicablePolicyIdList); - } - - return hashCode; - } - - @Override - public boolean equals(Object obj) - { - if (this == obj) - { - return true; - } - - if (!(obj instanceof DecisionResult)) - { - return false; - } - - final DecisionResult other = (DecisionResult) obj; - if (this.decision != other.getDecision()) - { - return false; - } - - if (this.extIndeterminate != other.getExtendedIndeterminate()) - { - return false; - } - - // Status is optional in XACML - if (this.status == null) - { - if (other.getStatus() != null) - { - return false; - } - } else if (!this.status.equals(other.getStatus())) - { - return false; - } - - // this.getObligations() derived from this.pepActions - // pepActions never null - if (!this.pepActions.equals(other.getPepActions())) - { - return false; - } - - // applicablePolicyIdList never null - if (!this.applicablePolicyIdList.equals(other.getApplicablePolicyIdList())) - { - return false; - } - - return true; - } - - /** - * Get identifiers of policies found applicable for the decision request - * - * @return identifiers of policies found applicable for the decision request - */ - @Override - public List> getApplicablePolicyIdList() - { - return this.applicablePolicyIdList; - } - - /** - * Get XACML Decision - * - * @return decision - */ - @Override - public DecisionType getDecision() - { - return this.decision; - } - - /** - * Get PEP actions (Obligations/Advices) - * - * @return PEP actions - */ - @Override - public PepActions getPepActions() - { - return this.pepActions; - } - - /** - * Status code/message/detail - * - * @return status - */ - @Override - public Status getStatus() - { - return this.status; - } - - /** - * Merge extra PEP actions and/or matched policy identifiers. Used when combining results from child Rules of Policy - * or child Policies of PolicySet - * - * @param newPepActions - * new PEP actions - * @param newMatchedPolicyIdList - * new matched policy identifiers - */ - @Override - public void merge(PepActions newPepActions, List> newMatchedPolicyIdList) - { - if (newPepActions != null) - { - this.pepActions.merge(newPepActions); - } - - if (newMatchedPolicyIdList != null) - { - this.applicablePolicyIdList.addAll(newMatchedPolicyIdList); - } - } - - @Override - public String toString() - { - return "Result [decision=" + decision + ", status=" + status + ", pepActions=" + pepActions - + ", applicablePolicyIdList=" + applicablePolicyIdList + "]"; - } - - @Override - public DecisionType getExtendedIndeterminate() - { - return this.extIndeterminate; - } - -} +/** + * Copyright (C) 2011-2015 Thales Services SAS. + * + * This file is part of AuthZForce. + * + * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * AuthZForce 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . + */ +/** + * + */ +package org.ow2.authzforce.core.pdp.impl; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import javax.xml.bind.JAXBElement; + +import oasis.names.tc.xacml._3_0.core.schema.wd_17.DecisionType; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.IdReferenceType; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.Status; + +import org.ow2.authzforce.core.pdp.api.DecisionResult; +import org.ow2.authzforce.core.pdp.api.PepActions; + +/** + * Base implementation of DecisionResult + * + * @author cdangerv + * @version $Id: $ + */ +public final class BaseDecisionResult implements DecisionResult +{ + private static final IllegalArgumentException ILLEGAL_DECISION_ARGUMENT_EXCEPTION = new IllegalArgumentException( + "Undefined Decision"); + + /** + * NotApplicable decision result + */ + public static final DecisionResult NOT_APPLICABLE = new BaseDecisionResult(DecisionType.NOT_APPLICABLE, null); + + /** + * Deny result with no obligation/advice/Included attribute/policy identifiers. Deny decision and nothing else. + */ + public static final DecisionResult DENY = new BaseDecisionResult(DecisionType.DENY, null); + + /** + * Permit result with no obligation/advice/Included attribute/policy identifiers. Permit decision and nothing else. + */ + public static final DecisionResult PERMIT = new BaseDecisionResult(DecisionType.PERMIT, null); + + private final DecisionType decision; + + /** + * Extended Indeterminate value, as defined in section 7.10 of XACML 3.0 core: potential effect value which could + * have occurred if there would not have been an error causing the “Indeterminate”. We use the following + * convention: + *
    + *
  • {@link DecisionType#DENY} means "Indeterminate{D}"
  • + *
  • {@link DecisionType#PERMIT} means "Indeterminate{P}"
  • + *
  • Null means "Indeterminate{DP}"
  • + *
  • {@link DecisionType#NOT_APPLICABLE} is the default value and means the decision is not Indeterminate, and + * therefore any extended Indeterminate value should be ignored
  • + *
+ * + */ + private final DecisionType extIndeterminate; + + private final Status status; + + // initialized non-null + private final PepActions pepActions; + + // initialized non-null + private final List> applicablePolicyIdList; + + /** + * Instantiates a generic Decision result + * + * @param decision + * decision + * @param extendedIndeterminate + * Extended Indeterminate value, null if {@code decision != DecisionType.INDETERMINATE} + * @param status + * status + * @param pepActions + * PEP actions (obligations/advices) + * @param policyIdentifierList + * list of matched policy identifiers + */ + public BaseDecisionResult(DecisionType decision, DecisionType extendedIndeterminate, Status status, + PepActions pepActions, List> policyIdentifierList) + { + if (decision == null) + { + throw ILLEGAL_DECISION_ARGUMENT_EXCEPTION; + } + + this.decision = decision; + this.extIndeterminate = extendedIndeterminate; + this.status = status; + this.pepActions = pepActions == null ? new BasePepActions(null, null) : pepActions; + this.applicablePolicyIdList = policyIdentifierList == null ? new ArrayList>() + : policyIdentifierList; + + } + + /** + * Instantiates a Indeterminate Decision result with a given error status + * + * @param extendedIndeterminate + * Extended Indeterminate value (XACML 3.0 Core, section 7.10). We use the following convention: + *
    + *
  • {@link DecisionType#DENY} means "Indeterminate{D}"
  • + *
  • {@link DecisionType#PERMIT} means "Indeterminate{P}"
  • + *
  • {@link DecisionType#INDETERMINATE} means "Indeterminate{DP}"
  • + *
  • {@link DecisionType#NOT_APPLICABLE} is the default value and means the decision is not + * Indeterminate, and therefore any extended Indeterminate value should be ignored
  • + *
+ * @param status + * reason/code for Indeterminate + */ + public BaseDecisionResult(Status status, DecisionType extendedIndeterminate) + { + this(DecisionType.INDETERMINATE, extendedIndeterminate, status, null, null); + } + + /** + * Instantiates a Indeterminate Decision result with a given error status and extended Indeterminate set to + * Indeterminate{DP} + * + * @param status + * reason/code for Indeterminate + */ + public BaseDecisionResult(Status status) + { + this(DecisionType.INDETERMINATE, DecisionType.INDETERMINATE, status, null, null); + } + + /** + * Instantiates a Permit/Deny decision with optional obligations and advice. See + * {@link #BaseDecisionResult(Status, DecisionType)} for Indeterminate, and {@link #NOT_APPLICABLE} for + * NotApplicable. + * + * @param decision + * decision + * @param pepActions + * PEP actions (obligations/advices) + */ + public BaseDecisionResult(DecisionType decision, PepActions pepActions) + { + this(decision, DecisionType.NOT_APPLICABLE, null, pepActions, null); + } + + private transient volatile int hashCode = 0; + + /** {@inheritDoc} */ + @Override + public int hashCode() + { + if (hashCode == 0) + { + hashCode = Objects.hash(this.decision, this.extIndeterminate, this.status, this.pepActions, + this.applicablePolicyIdList); + } + + return hashCode; + } + + /** {@inheritDoc} */ + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + + if (!(obj instanceof DecisionResult)) + { + return false; + } + + final DecisionResult other = (DecisionResult) obj; + if (this.decision != other.getDecision()) + { + return false; + } + + if (this.extIndeterminate != other.getExtendedIndeterminate()) + { + return false; + } + + // Status is optional in XACML + if (this.status == null) + { + if (other.getStatus() != null) + { + return false; + } + } else if (!this.status.equals(other.getStatus())) + { + return false; + } + + // this.getObligations() derived from this.pepActions + // pepActions never null + if (!this.pepActions.equals(other.getPepActions())) + { + return false; + } + + // applicablePolicyIdList never null + if (!this.applicablePolicyIdList.equals(other.getApplicablePolicyIdList())) + { + return false; + } + + return true; + } + + /** + * {@inheritDoc} + * + * Get identifiers of policies found applicable for the decision request + */ + @Override + public List> getApplicablePolicyIdList() + { + return this.applicablePolicyIdList; + } + + /** + * {@inheritDoc} + * + * Get XACML Decision + */ + @Override + public DecisionType getDecision() + { + return this.decision; + } + + /** + * {@inheritDoc} + * + * Get PEP actions (Obligations/Advices) + */ + @Override + public PepActions getPepActions() + { + return this.pepActions; + } + + /** + * {@inheritDoc} + * + * Status code/message/detail + */ + @Override + public Status getStatus() + { + return this.status; + } + + /** + * {@inheritDoc} + * + * Merge extra PEP actions and/or matched policy identifiers. Used when combining results from child Rules of Policy + * or child Policies of PolicySet + */ + @Override + public void merge(PepActions newPepActions, List> newMatchedPolicyIdList) + { + if (newPepActions != null) + { + this.pepActions.merge(newPepActions); + } + + if (newMatchedPolicyIdList != null) + { + this.applicablePolicyIdList.addAll(newMatchedPolicyIdList); + } + } + + /** {@inheritDoc} */ + @Override + public String toString() + { + return "Result [decision=" + decision + ", status=" + status + ", pepActions=" + pepActions + + ", applicablePolicyIdList=" + applicablePolicyIdList + "]"; + } + + /** {@inheritDoc} */ + @Override + public DecisionType getExtendedIndeterminate() + { + return this.extIndeterminate; + } + +} diff --git a/src/main/java/org/ow2/authzforce/core/pdp/impl/BasePdpExtensionRegistry.java b/src/main/java/org/ow2/authzforce/core/pdp/impl/BasePdpExtensionRegistry.java index 24b16b5c..bbe1bb4e 100644 --- a/src/main/java/org/ow2/authzforce/core/pdp/impl/BasePdpExtensionRegistry.java +++ b/src/main/java/org/ow2/authzforce/core/pdp/impl/BasePdpExtensionRegistry.java @@ -1,145 +1,148 @@ -/** - * Copyright (C) 2011-2015 Thales Services SAS. - * - * This file is part of AuthZForce. - * - * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software - * Foundation, either version 3 of the License, or (at your option) any later version. - * - * AuthZForce 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . - */ -package org.ow2.authzforce.core.pdp.impl; - -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -import org.ow2.authzforce.core.pdp.api.PdpExtension; -import org.ow2.authzforce.core.pdp.api.PdpExtensionRegistry; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * This is a com.thalesgroup.authzforce.core.test.basic implementation of PdpExtensionRegistry. - * - * @param - * type of extension in this registry - */ -public class BasePdpExtensionRegistry implements PdpExtensionRegistry -{ - private static final Logger LOGGER = LoggerFactory.getLogger(BasePdpExtensionRegistry.class); - - private static final IllegalArgumentException NULL_EXTENSION_CLASS_EXCEPTION = new IllegalArgumentException("Extension class arg undefined"); - private static final IllegalArgumentException NULL_EXTENSIONS_EXCEPTION = new IllegalArgumentException("ExtensionsById arg undefined"); - - private final Class extClass; - - // the backing maps for the Function objects - private final Map extensionsById; - - /** - * Instantiates registry from a map (id -> extension) - * - * @param extensionClass - * extension class - * - * @param extensionsById - * extensions indexed by ID - */ - private BasePdpExtensionRegistry(Class extensionClass, Map extensionsById) - { - if (extensionClass == null) - { - throw NULL_EXTENSION_CLASS_EXCEPTION; - } - - if (extensionsById == null) - { - throw NULL_EXTENSIONS_EXCEPTION; - } - - this.extClass = extensionClass; - this.extensionsById = extensionsById; - } - - /** - * Instantiates immutable registry from a set of extensions - * - * @param extensionClass - * extension class - * - * @param extensions - * extensions - */ - public BasePdpExtensionRegistry(Class extensionClass, Set extensions) - { - if (extensionClass == null) - { - throw NULL_EXTENSION_CLASS_EXCEPTION; - } - - if (extensions == null) - { - throw NULL_EXTENSIONS_EXCEPTION; - } - - this.extClass = extensionClass; - - this.extensionsById = new HashMap<>(); - for (final T extension : extensions) - { - final String id = extension.getId(); - - this.extensionsById.put(id, extension); - } - } - - /** - * Default constructor. No superset factory is used. - * - * @param extensionClass - * extension class - */ - public BasePdpExtensionRegistry(Class extensionClass) - { - this(extensionClass, new HashMap()); - } - - /** - * Constructor that sets a "base registry" from which this inherits all the extensions. Used for instance to build a new registry based on a standard one - * like the StandardFunctionRegistry for standard functions). - * - * @param baseRegistry - * the base/parent registry on which this one is based or null - * @param extensionClass - * extension class - */ - public BasePdpExtensionRegistry(Class extensionClass, BasePdpExtensionRegistry baseRegistry) - { - this(extensionClass, baseRegistry == null ? new HashMap() : new HashMap<>(baseRegistry.extensionsById)); - } - - @Override - public void addExtension(T extension) throws IllegalArgumentException - { - final String id = extension.getId(); - // make sure nothing already registered with same ID - if (extensionsById.containsKey(id)) - { - throw new IllegalArgumentException("Conflict: extension (id=" + id + ") already registered"); - } - - extensionsById.put(id, extension); - LOGGER.debug("Added PDP extension of {} to registry: {}", extClass, extension); - } - - @Override - public T getExtension(String identity) - { - return extensionsById.get(identity); - } - -} +/** + * Copyright (C) 2011-2015 Thales Services SAS. + * + * This file is part of AuthZForce. + * + * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * AuthZForce 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . + */ +package org.ow2.authzforce.core.pdp.impl; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import org.ow2.authzforce.core.pdp.api.PdpExtension; +import org.ow2.authzforce.core.pdp.api.PdpExtensionRegistry; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This is a com.thalesgroup.authzforce.core.test.basic implementation of PdpExtensionRegistry. + * + * @param + * type of extension in this registry + * @author cdangerv + * @version $Id: $ + */ +public class BasePdpExtensionRegistry implements PdpExtensionRegistry +{ + private static final Logger LOGGER = LoggerFactory.getLogger(BasePdpExtensionRegistry.class); + + private static final IllegalArgumentException NULL_EXTENSION_CLASS_EXCEPTION = new IllegalArgumentException("Extension class arg undefined"); + private static final IllegalArgumentException NULL_EXTENSIONS_EXCEPTION = new IllegalArgumentException("ExtensionsById arg undefined"); + + private final Class extClass; + + // the backing maps for the Function objects + private final Map extensionsById; + + /** + * Instantiates registry from a map (id -> extension) + * + * @param extensionClass + * extension class + * + * @param extensionsById + * extensions indexed by ID + */ + private BasePdpExtensionRegistry(Class extensionClass, Map extensionsById) + { + if (extensionClass == null) + { + throw NULL_EXTENSION_CLASS_EXCEPTION; + } + + if (extensionsById == null) + { + throw NULL_EXTENSIONS_EXCEPTION; + } + + this.extClass = extensionClass; + this.extensionsById = extensionsById; + } + + /** + * Instantiates immutable registry from a set of extensions + * + * @param extensionClass + * extension class + * @param extensions + * extensions + */ + public BasePdpExtensionRegistry(Class extensionClass, Set extensions) + { + if (extensionClass == null) + { + throw NULL_EXTENSION_CLASS_EXCEPTION; + } + + if (extensions == null) + { + throw NULL_EXTENSIONS_EXCEPTION; + } + + this.extClass = extensionClass; + + this.extensionsById = new HashMap<>(); + for (final T extension : extensions) + { + final String id = extension.getId(); + + this.extensionsById.put(id, extension); + } + } + + /** + * Default constructor. No superset factory is used. + * + * @param extensionClass + * extension class + */ + public BasePdpExtensionRegistry(Class extensionClass) + { + this(extensionClass, new HashMap()); + } + + /** + * Constructor that sets a "base registry" from which this inherits all the extensions. Used for instance to build a new registry based on a standard one + * like the StandardFunctionRegistry for standard functions). + * + * @param baseRegistry + * the base/parent registry on which this one is based or null + * @param extensionClass + * extension class + */ + public BasePdpExtensionRegistry(Class extensionClass, BasePdpExtensionRegistry baseRegistry) + { + this(extensionClass, baseRegistry == null ? new HashMap() : new HashMap<>(baseRegistry.extensionsById)); + } + + /** {@inheritDoc} */ + @Override + public void addExtension(T extension) throws IllegalArgumentException + { + final String id = extension.getId(); + // make sure nothing already registered with same ID + if (extensionsById.containsKey(id)) + { + throw new IllegalArgumentException("Conflict: extension (id=" + id + ") already registered"); + } + + extensionsById.put(id, extension); + LOGGER.debug("Added PDP extension of {} to registry: {}", extClass, extension); + } + + /** {@inheritDoc} */ + @Override + public T getExtension(String identity) + { + return extensionsById.get(identity); + } + +} diff --git a/src/main/java/org/ow2/authzforce/core/pdp/impl/BasePepActions.java b/src/main/java/org/ow2/authzforce/core/pdp/impl/BasePepActions.java index e18ac8d5..588ffe66 100644 --- a/src/main/java/org/ow2/authzforce/core/pdp/impl/BasePepActions.java +++ b/src/main/java/org/ow2/authzforce/core/pdp/impl/BasePepActions.java @@ -1,196 +1,196 @@ -/** - * Copyright (C) 2011-2015 Thales Services SAS. - * - * This file is part of AuthZForce. - * - * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software - * Foundation, either version 3 of the License, or (at your option) any later version. - * - * AuthZForce 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . - */ -/** - * - */ -package org.ow2.authzforce.core.pdp.impl; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Objects; - -import oasis.names.tc.xacml._3_0.core.schema.wd_17.Advice; -import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeAssignment; -import oasis.names.tc.xacml._3_0.core.schema.wd_17.Obligation; - -import org.ow2.authzforce.core.pdp.api.PepActions; - -/** - * Base PEP actions (obligations/advice) - * - */ -public final class BasePepActions implements PepActions -{ - private static final IllegalArgumentException UNDEF_MERGED_PEP_ACTIONS_ARGUMENT_EXCEPTION = new IllegalArgumentException("Undefined PEP actions"); - - /** - * Obligation factory - * - */ - public static final PepActionFactory OBLIGATION_FACTORY = new PepActionFactory() - { - - @Override - public Obligation getInstance(List attributeAssignments, String actionId) - { - return new Obligation(attributeAssignments, actionId); - } - - @Override - public String getActionXmlElementName() - { - return "Obligation"; - } - - }; - - /** - * Advice factory - * - */ - public static final PepActionFactory ADVICE_FACTORY = new PepActionFactory() - { - - @Override - public Advice getInstance(List attributeAssignments, String actionId) - { - return new Advice(attributeAssignments, actionId); - } - - @Override - public String getActionXmlElementName() - { - return "Advice"; - } - - }; - - // always non-null fields - private final List obligationList; - private final List adviceList; - - /** - * Instantiates PEP action set from obligations/advice - * - * @param obligations - * obligation list; null if no obligation - * @param advices - * advice list; null if no advice - */ - public BasePepActions(List obligations, List advices) - { - this.obligationList = obligations == null ? new ArrayList() : obligations; - this.adviceList = advices == null ? new ArrayList() : advices; - } - - /** - * Get the internal obligation list - * - * @return obligations; empty if no obligation (always non-null) - */ - @Override - public List getObligations() - { - return Collections.unmodifiableList(obligationList); - } - - /** - * Get the internal advice list - * - * @return advice; empty if no obligation (always non-null) - */ - @Override - public List getAdvices() - { - return Collections.unmodifiableList(adviceList); - } - - private transient volatile int hashCode = 0; - - @Override - public int hashCode() - { - if (hashCode == 0) - { - hashCode = Objects.hash(this.obligationList, this.adviceList); - } - - return hashCode; - } - - @Override - public boolean equals(Object obj) - { - if (this == obj) - { - return true; - } - - if (!(obj instanceof BasePepActions)) - { - return false; - } - - final BasePepActions other = (BasePepActions) obj; - return this.obligationList.equals(other.obligationList) && this.adviceList.equals(other.adviceList); - } - - /** - * Merge extra PEP actions. Used when combining results from child Rules of Policy or child Policies of PolicySet - * - * @param newObligations - * new obligation list - * @param newAdvices - * new advice list - * - */ - @Override - public void merge(List newObligations, List newAdvices) - { - if (newObligations != null) - { - this.obligationList.addAll(newObligations); - } - - if (newAdvices != null) - { - this.adviceList.addAll(newAdvices); - } - } - - /** - * Merge extra PEP actions. Used when combining results from child Rules of Policy or child Policies of PolicySet - * - * @param pepActions - * PEP actions - */ - @Override - public void merge(PepActions pepActions) - { - if (pepActions == null) - { - throw UNDEF_MERGED_PEP_ACTIONS_ARGUMENT_EXCEPTION; - } - - merge(pepActions.getObligations(), pepActions.getAdvices()); - } - - @Override - public String toString() - { - return "[obligations=" + obligationList + ", advices=" + adviceList + "]"; - } - -} +/** + * Copyright (C) 2011-2015 Thales Services SAS. + * + * This file is part of AuthZForce. + * + * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * AuthZForce 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . + */ +/** + * + */ +package org.ow2.authzforce.core.pdp.impl; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +import oasis.names.tc.xacml._3_0.core.schema.wd_17.Advice; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeAssignment; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.Obligation; + +import org.ow2.authzforce.core.pdp.api.PepActions; + +/** + * Base PEP actions (obligations/advice) + * + * @author cdangerv + * @version $Id: $ + */ +public final class BasePepActions implements PepActions +{ + private static final IllegalArgumentException UNDEF_MERGED_PEP_ACTIONS_ARGUMENT_EXCEPTION = new IllegalArgumentException("Undefined PEP actions"); + + /** + * Obligation factory + * + */ + public static final PepActionFactory OBLIGATION_FACTORY = new PepActionFactory() + { + + @Override + public Obligation getInstance(List attributeAssignments, String actionId) + { + return new Obligation(attributeAssignments, actionId); + } + + @Override + public String getActionXmlElementName() + { + return "Obligation"; + } + + }; + + /** + * Advice factory + * + */ + public static final PepActionFactory ADVICE_FACTORY = new PepActionFactory() + { + + @Override + public Advice getInstance(List attributeAssignments, String actionId) + { + return new Advice(attributeAssignments, actionId); + } + + @Override + public String getActionXmlElementName() + { + return "Advice"; + } + + }; + + // always non-null fields + private final List obligationList; + private final List adviceList; + + /** + * Instantiates PEP action set from obligations/advice + * + * @param obligations + * obligation list; null if no obligation + * @param advices + * advice list; null if no advice + */ + public BasePepActions(List obligations, List advices) + { + this.obligationList = obligations == null ? new ArrayList() : obligations; + this.adviceList = advices == null ? new ArrayList() : advices; + } + + /** + * {@inheritDoc} + * + * Get the internal obligation list + */ + @Override + public List getObligations() + { + return Collections.unmodifiableList(obligationList); + } + + /** + * {@inheritDoc} + * + * Get the internal advice list + */ + @Override + public List getAdvices() + { + return Collections.unmodifiableList(adviceList); + } + + private transient volatile int hashCode = 0; + + /** {@inheritDoc} */ + @Override + public int hashCode() + { + if (hashCode == 0) + { + hashCode = Objects.hash(this.obligationList, this.adviceList); + } + + return hashCode; + } + + /** {@inheritDoc} */ + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + + if (!(obj instanceof BasePepActions)) + { + return false; + } + + final BasePepActions other = (BasePepActions) obj; + return this.obligationList.equals(other.obligationList) && this.adviceList.equals(other.adviceList); + } + + /** + * {@inheritDoc} + * + * Merge extra PEP actions. Used when combining results from child Rules of Policy or child Policies of PolicySet + */ + @Override + public void merge(List newObligations, List newAdvices) + { + if (newObligations != null) + { + this.obligationList.addAll(newObligations); + } + + if (newAdvices != null) + { + this.adviceList.addAll(newAdvices); + } + } + + /** + * {@inheritDoc} + * + * Merge extra PEP actions. Used when combining results from child Rules of Policy or child Policies of PolicySet + */ + @Override + public void merge(PepActions pepActions) + { + if (pepActions == null) + { + throw UNDEF_MERGED_PEP_ACTIONS_ARGUMENT_EXCEPTION; + } + + merge(pepActions.getObligations(), pepActions.getAdvices()); + } + + /** {@inheritDoc} */ + @Override + public String toString() + { + return "[obligations=" + obligationList + ", advices=" + adviceList + "]"; + } + +} diff --git a/src/main/java/org/ow2/authzforce/core/pdp/impl/CloseableAttributeProvider.java b/src/main/java/org/ow2/authzforce/core/pdp/impl/CloseableAttributeProvider.java index 1c94aca7..377fe571 100644 --- a/src/main/java/org/ow2/authzforce/core/pdp/impl/CloseableAttributeProvider.java +++ b/src/main/java/org/ow2/authzforce/core/pdp/impl/CloseableAttributeProvider.java @@ -1,219 +1,221 @@ -/** - * Copyright (C) 2011-2015 Thales Services SAS. - * - * This file is part of AuthZForce. - * - * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software - * Foundation, either version 3 of the License, or (at your option) any later version. - * - * AuthZForce 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . - */ -package org.ow2.authzforce.core.pdp.impl; - -import java.io.Closeable; -import java.io.IOException; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeDesignatorType; - -import org.ow2.authzforce.core.pdp.api.AttributeGUID; -import org.ow2.authzforce.core.pdp.api.AttributeProvider; -import org.ow2.authzforce.core.pdp.api.AttributeProviderModule; -import org.ow2.authzforce.core.pdp.api.CloseableAttributeProviderModule; -import org.ow2.authzforce.core.pdp.api.DatatypeFactoryRegistry; -import org.ow2.authzforce.xmlns.pdp.ext.AbstractAttributeProvider; - -/** - * Closeable AttributeProvider - *

- * The sub-modules may very likely hold resources such as network resources to get attributes remotely, or attribute caches to speed up finding, etc. Therefore, - * you are required to call {@link #close()} when you no longer need an instance - especially before replacing with a new instance (with different modules) - in - * order to make sure these resources are released properly by each underlying module (e.g. close the attribute caches). - * - */ -public final class CloseableAttributeProvider extends ModularAttributeProvider implements Closeable -{ - - private static class ModuleAdapter - { - private CloseableAttributeProviderModule module; - - private ModuleAdapter(CloseableAttributeProviderModule module) throws IOException - { - final Set providedAttributes = module.getProvidedAttributes(); - if (providedAttributes == null || providedAttributes.isEmpty()) - { - module.close(); - throw new IllegalArgumentException("Invalid " + module + " : list of supported AttributeDesignators is null or empty"); - } - - this.module = module; - } - - private void close() throws IOException - { - this.module.close(); - } - - private Set getProvidedAttributes() - { - return this.module.getProvidedAttributes(); - } - - @Override - public String toString() - { - return module.toString(); - } - - private AttributeProviderModule getAdaptedModule() - { - return this.module; - } - } - - private static void close(Set moduleClosers) throws IOException - { - // An error occuring on closing one module should not stop from closing the others - // But we keep the exception in memory if any, to throw it at the end as we do not want to hide that an error occurred - IOException latestEx = null; - for (final ModuleAdapter mod : moduleClosers) - { - try - { - mod.close(); - } catch (IOException e) - { - latestEx = e; - } - } - - if (latestEx != null) - { - throw latestEx; - } - } - - private Set moduleClosers; - - /** - * Instantiates attribute Provider that tries to find attribute values in evaluation context, then, if not there, query the {@code module} providing the - * requested attribute ID, if any. - * - * @param attributeFactory - * (mandatory) attribute value factory - * - * @param jaxbAttributeProviderConfs - * (optional) XML/JAXB configurations of Attribute Providers for AttributeDesignator/AttributeSelector evaluation; may be null for static - * expression evaluation (out of context), in which case AttributeSelectors/AttributeDesignators are not supported - * @throws IllegalArgumentException - * If any of attribute Provider modules created from {@code jaxbAttributeProviderConfs} does not provide any attribute; or it is in conflict - * with another one already registered to provide the same or part of the same attributes. - * @throws IOException - * error closing the attribute Provider modules created from {@code jaxbAttributeProviderConfs}, when and before an - * {@link IllegalArgumentException} is raised - */ - private CloseableAttributeProvider(Map modulesByAttributeId, Set moduleClosers) throws IOException - { - super(modulesByAttributeId); - this.moduleClosers = moduleClosers; - } - - /** - * Instantiates attribute Provider that tries to find attribute values in evaluation context, then, if not there, query the {@code module} providing the - * requested attribute ID, if any. - * - * @param attributeFactory - * (mandatory) attribute value factory - * - * @param jaxbAttributeProviderConfs - * (optional) XML/JAXB configurations of Attribute Providers for AttributeDesignator/AttributeSelector evaluation; may be null for static - * expression evaluation (out of context), in which case AttributeSelectors/AttributeDesignators are not supported - * @return instance of this class - * @throws IllegalArgumentException - * If any of attribute Provider modules created from {@code jaxbAttributeProviderConfs} does not provide any attribute; or it is in conflict - * with another one already registered to provide the same or part of the same attributes. - * @throws IOException - * error closing the attribute Provider modules created from {@code jaxbAttributeProviderConfs}, when and before an - * {@link IllegalArgumentException} is raised - */ - public static CloseableAttributeProvider getInstance(List jaxbAttributeProviderConfs, DatatypeFactoryRegistry attributeFactory) - throws IOException - { - final Map modulesByAttributeId; - final Set moduleCloserSet; - if (jaxbAttributeProviderConfs == null) - { - modulesByAttributeId = null; - moduleCloserSet = null; - } else - { - final int moduleCount = jaxbAttributeProviderConfs.size(); - modulesByAttributeId = new HashMap<>(moduleCount); - moduleCloserSet = new HashSet<>(moduleCount); - for (final AbstractAttributeProvider jaxbAttributeProviderConf : jaxbAttributeProviderConfs) - { - try - { - final CloseableAttributeProviderModule.FactoryBuilder attrProviderModBuilder = PdpExtensionLoader - .getJaxbBoundExtension(CloseableAttributeProviderModule.FactoryBuilder.class, jaxbAttributeProviderConf.getClass()); - final CloseableAttributeProviderModule.DependencyAwareFactory depAwareAttrProviderModBuilder = attrProviderModBuilder - .getInstance(jaxbAttributeProviderConf); - final Set requiredAttrs = depAwareAttrProviderModBuilder.getDependencies(); - /* - * Each AttributeProviderModule is given a read-only AttributeProvider - aka "dependency attribute Provider" - to find any attribute they - * require (dependency), based on the attribute Provider modules that provide these required attributes (set above); read-only so that - * modules use this attribute Provider only to get required attributes, nothing else. Create this dependency attribute Provider. - */ - final AttributeProvider depAttrProvider; - if (requiredAttrs == null) - { - depAttrProvider = new ModularAttributeProvider(null); - } else - { - final Map immutableCopyOfAttrProviderModsByAttrId = Collections - . unmodifiableMap(modulesByAttributeId); - depAttrProvider = new ModularAttributeProvider(immutableCopyOfAttrProviderModsByAttrId, requiredAttrs); - } - - // attrProviderMod closing isn't done in this method but handled in close() method when closing all modules - final ModuleAdapter moduleAdapter = new ModuleAdapter(depAwareAttrProviderModBuilder.getInstance(attributeFactory, depAttrProvider)); - moduleCloserSet.add(moduleAdapter); - - for (final AttributeDesignatorType attrDesignator : moduleAdapter.getProvidedAttributes()) - { - final AttributeGUID attrGUID = new AttributeGUID(attrDesignator); - if (modulesByAttributeId.containsKey(attrGUID)) - { - moduleAdapter.close(); - throw new IllegalArgumentException("Conflict: " + moduleAdapter + " providing the same AttributeDesignator (" + attrGUID - + ") as another already registered."); - } - - modulesByAttributeId.put(attrGUID, moduleAdapter.getAdaptedModule()); - } - } catch (IllegalArgumentException e) - { - close(moduleCloserSet); - throw e; - } - } - } - - return new CloseableAttributeProvider(modulesByAttributeId, moduleCloserSet); - } - - @Override - public void close() throws IOException - { - close(this.moduleClosers); - } -} \ No newline at end of file +/** + * Copyright (C) 2011-2015 Thales Services SAS. + * + * This file is part of AuthZForce. + * + * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * AuthZForce 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . + */ +package org.ow2.authzforce.core.pdp.impl; + +import java.io.Closeable; +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeDesignatorType; + +import org.ow2.authzforce.core.pdp.api.AttributeGUID; +import org.ow2.authzforce.core.pdp.api.AttributeProvider; +import org.ow2.authzforce.core.pdp.api.AttributeProviderModule; +import org.ow2.authzforce.core.pdp.api.CloseableAttributeProviderModule; +import org.ow2.authzforce.core.pdp.api.DatatypeFactoryRegistry; +import org.ow2.authzforce.xmlns.pdp.ext.AbstractAttributeProvider; + +/** + * Closeable AttributeProvider + *

+ * The sub-modules may very likely hold resources such as network resources to get attributes remotely, or attribute caches to speed up finding, etc. Therefore, + * you are required to call {@link #close()} when you no longer need an instance - especially before replacing with a new instance (with different modules) - in + * order to make sure these resources are released properly by each underlying module (e.g. close the attribute caches). + * + * @author cdangerv + * @version $Id: $ + */ +public final class CloseableAttributeProvider extends ModularAttributeProvider implements Closeable +{ + + private static class ModuleAdapter + { + private CloseableAttributeProviderModule module; + + private ModuleAdapter(CloseableAttributeProviderModule module) throws IOException + { + final Set providedAttributes = module.getProvidedAttributes(); + if (providedAttributes == null || providedAttributes.isEmpty()) + { + module.close(); + throw new IllegalArgumentException("Invalid " + module + " : list of supported AttributeDesignators is null or empty"); + } + + this.module = module; + } + + private void close() throws IOException + { + this.module.close(); + } + + private Set getProvidedAttributes() + { + return this.module.getProvidedAttributes(); + } + + @Override + public String toString() + { + return module.toString(); + } + + private AttributeProviderModule getAdaptedModule() + { + return this.module; + } + } + + private static void close(Set moduleClosers) throws IOException + { + // An error occuring on closing one module should not stop from closing the others + // But we keep the exception in memory if any, to throw it at the end as we do not want to hide that an error occurred + IOException latestEx = null; + for (final ModuleAdapter mod : moduleClosers) + { + try + { + mod.close(); + } catch (IOException e) + { + latestEx = e; + } + } + + if (latestEx != null) + { + throw latestEx; + } + } + + private Set moduleClosers; + + /** + * Instantiates attribute Provider that tries to find attribute values in evaluation context, then, if not there, query the {@code module} providing the + * requested attribute ID, if any. + * + * @param attributeFactory + * (mandatory) attribute value factory + * + * @param jaxbAttributeProviderConfs + * (optional) XML/JAXB configurations of Attribute Providers for AttributeDesignator/AttributeSelector evaluation; may be null for static + * expression evaluation (out of context), in which case AttributeSelectors/AttributeDesignators are not supported + * @throws IllegalArgumentException + * If any of attribute Provider modules created from {@code jaxbAttributeProviderConfs} does not provide any attribute; or it is in conflict + * with another one already registered to provide the same or part of the same attributes. + * @throws IOException + * error closing the attribute Provider modules created from {@code jaxbAttributeProviderConfs}, when and before an + * {@link IllegalArgumentException} is raised + */ + private CloseableAttributeProvider(Map modulesByAttributeId, Set moduleClosers) throws IOException + { + super(modulesByAttributeId); + this.moduleClosers = moduleClosers; + } + + /** + * Instantiates attribute Provider that tries to find attribute values in evaluation context, then, if not there, query the {@code module} providing the + * requested attribute ID, if any. + * + * @param attributeFactory + * (mandatory) attribute value factory + * @param jaxbAttributeProviderConfs + * (optional) XML/JAXB configurations of Attribute Providers for AttributeDesignator/AttributeSelector evaluation; may be null for static + * expression evaluation (out of context), in which case AttributeSelectors/AttributeDesignators are not supported + * @return instance of this class + * @throws java.lang.IllegalArgumentException + * If any of attribute Provider modules created from {@code jaxbAttributeProviderConfs} does not provide any attribute; or it is in conflict + * with another one already registered to provide the same or part of the same attributes. + * @throws java.io.IOException + * error closing the attribute Provider modules created from {@code jaxbAttributeProviderConfs}, when and before an + * {@link IllegalArgumentException} is raised + */ + public static CloseableAttributeProvider getInstance(List jaxbAttributeProviderConfs, DatatypeFactoryRegistry attributeFactory) + throws IOException + { + final Map modulesByAttributeId; + final Set moduleCloserSet; + if (jaxbAttributeProviderConfs == null) + { + modulesByAttributeId = null; + moduleCloserSet = null; + } else + { + final int moduleCount = jaxbAttributeProviderConfs.size(); + modulesByAttributeId = new HashMap<>(moduleCount); + moduleCloserSet = new HashSet<>(moduleCount); + for (final AbstractAttributeProvider jaxbAttributeProviderConf : jaxbAttributeProviderConfs) + { + try + { + final CloseableAttributeProviderModule.FactoryBuilder attrProviderModBuilder = PdpExtensionLoader + .getJaxbBoundExtension(CloseableAttributeProviderModule.FactoryBuilder.class, jaxbAttributeProviderConf.getClass()); + final CloseableAttributeProviderModule.DependencyAwareFactory depAwareAttrProviderModBuilder = attrProviderModBuilder + .getInstance(jaxbAttributeProviderConf); + final Set requiredAttrs = depAwareAttrProviderModBuilder.getDependencies(); + /* + * Each AttributeProviderModule is given a read-only AttributeProvider - aka "dependency attribute Provider" - to find any attribute they + * require (dependency), based on the attribute Provider modules that provide these required attributes (set above); read-only so that + * modules use this attribute Provider only to get required attributes, nothing else. Create this dependency attribute Provider. + */ + final AttributeProvider depAttrProvider; + if (requiredAttrs == null) + { + depAttrProvider = new ModularAttributeProvider(null); + } else + { + final Map immutableCopyOfAttrProviderModsByAttrId = Collections + . unmodifiableMap(modulesByAttributeId); + depAttrProvider = new ModularAttributeProvider(immutableCopyOfAttrProviderModsByAttrId, requiredAttrs); + } + + // attrProviderMod closing isn't done in this method but handled in close() method when closing all modules + final ModuleAdapter moduleAdapter = new ModuleAdapter(depAwareAttrProviderModBuilder.getInstance(attributeFactory, depAttrProvider)); + moduleCloserSet.add(moduleAdapter); + + for (final AttributeDesignatorType attrDesignator : moduleAdapter.getProvidedAttributes()) + { + final AttributeGUID attrGUID = new AttributeGUID(attrDesignator); + if (modulesByAttributeId.containsKey(attrGUID)) + { + moduleAdapter.close(); + throw new IllegalArgumentException("Conflict: " + moduleAdapter + " providing the same AttributeDesignator (" + attrGUID + + ") as another already registered."); + } + + modulesByAttributeId.put(attrGUID, moduleAdapter.getAdaptedModule()); + } + } catch (IllegalArgumentException e) + { + close(moduleCloserSet); + throw e; + } + } + } + + return new CloseableAttributeProvider(modulesByAttributeId, moduleCloserSet); + } + + /** {@inheritDoc} */ + @Override + public void close() throws IOException + { + close(this.moduleClosers); + } +} diff --git a/src/main/java/org/ow2/authzforce/core/pdp/impl/DefaultEnvironmentProperties.java b/src/main/java/org/ow2/authzforce/core/pdp/impl/DefaultEnvironmentProperties.java index 8a62ab02..fa2c13f4 100644 --- a/src/main/java/org/ow2/authzforce/core/pdp/impl/DefaultEnvironmentProperties.java +++ b/src/main/java/org/ow2/authzforce/core/pdp/impl/DefaultEnvironmentProperties.java @@ -1,79 +1,82 @@ -/** - * Copyright (C) 2012-2016 Thales Services SAS. - * - * This file is part of AuthZForce CE. - * - * AuthZForce CE is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software - * Foundation, either version 3 of the License, or (at your option) any later version. - * - * AuthZForce CE 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with AuthZForce CE. If not, see . - */ -package org.ow2.authzforce.core.pdp.impl; - -import java.util.Map; -import java.util.Map.Entry; -import java.util.Properties; - -import org.ow2.authzforce.core.pdp.api.EnvironmentProperties; -import org.ow2.authzforce.core.pdp.api.EnvironmentPropertyName; -import org.springframework.util.PropertyPlaceholderHelper; - -/** - * Default implementation of PDP configuration parser's environment properties. - * - */ -public final class DefaultEnvironmentProperties implements EnvironmentProperties -{ - private static final String PROPERTY_PLACEHOLDER_PREFIX = "${"; - private static final String PROPERTY_PLACEHOLDER_SUFFIX = "}"; - private static final String PROPERTY_PLACEHOLDER_DEFAULT_VALUE_SEPARATOR = ":"; - - private static final PropertyPlaceholderHelper PROPERTY_PLACEHOLDER_HELPER = new PropertyPlaceholderHelper(PROPERTY_PLACEHOLDER_PREFIX, - PROPERTY_PLACEHOLDER_SUFFIX, PROPERTY_PLACEHOLDER_DEFAULT_VALUE_SEPARATOR, false); - - private final Properties props = new Properties(); - - /** - * Empty properties - */ - public DefaultEnvironmentProperties() - { - // empty properties - } - - /** - * Constructs instance from existing properties in a map - * - * @param envProps - * environment properties - */ - public DefaultEnvironmentProperties(Map envProps) - { - if (envProps == null) - { - return; - } - - for (final Entry envProp : envProps.entrySet()) - { - /* - * Property value must be a String! Using props.put(Object,Object) is misleading here as it makes falsely believe other datatypes would work - */ - props.setProperty(envProp.getKey().name(), envProp.getValue()); - } - } - - @Override - public String replacePlaceholders(String input) - { - if (input == null) - { - return null; - } - - return PROPERTY_PLACEHOLDER_HELPER.replacePlaceholders(input, props); - } -} +/** + * Copyright (C) 2012-2016 Thales Services SAS. + * + * This file is part of AuthZForce CE. + * + * AuthZForce CE is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * AuthZForce CE 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with AuthZForce CE. If not, see . + */ +package org.ow2.authzforce.core.pdp.impl; + +import java.util.Map; +import java.util.Map.Entry; +import java.util.Properties; + +import org.ow2.authzforce.core.pdp.api.EnvironmentProperties; +import org.ow2.authzforce.core.pdp.api.EnvironmentPropertyName; +import org.springframework.util.PropertyPlaceholderHelper; + +/** + * Default implementation of PDP configuration parser's environment properties. + * + * @author cdangerv + * @version $Id: $ + */ +public final class DefaultEnvironmentProperties implements EnvironmentProperties +{ + private static final String PROPERTY_PLACEHOLDER_PREFIX = "${"; + private static final String PROPERTY_PLACEHOLDER_SUFFIX = "}"; + private static final String PROPERTY_PLACEHOLDER_DEFAULT_VALUE_SEPARATOR = ":"; + + private static final PropertyPlaceholderHelper PROPERTY_PLACEHOLDER_HELPER = new PropertyPlaceholderHelper(PROPERTY_PLACEHOLDER_PREFIX, + PROPERTY_PLACEHOLDER_SUFFIX, PROPERTY_PLACEHOLDER_DEFAULT_VALUE_SEPARATOR, false); + + private final Properties props = new Properties(); + + /** + * Empty properties + */ + public DefaultEnvironmentProperties() + { + // empty properties + } + + /** + * Constructs instance from existing properties in a map + * + * @param envProps + * environment properties + */ + public DefaultEnvironmentProperties(Map envProps) + { + if (envProps == null) + { + return; + } + + for (final Entry envProp : envProps.entrySet()) + { + /* + * Property value must be a String! Using props.put(Object,Object) is misleading here as it makes falsely believe other datatypes would work + */ + props.setProperty(envProp.getKey().name(), envProp.getValue()); + } + } + + /** {@inheritDoc} */ + @Override + public String replacePlaceholders(String input) + { + if (input == null) + { + return null; + } + + return PROPERTY_PLACEHOLDER_HELPER.replacePlaceholders(input, props); + } +} diff --git a/src/main/java/org/ow2/authzforce/core/pdp/impl/DefaultRequestFilter.java b/src/main/java/org/ow2/authzforce/core/pdp/impl/DefaultRequestFilter.java index 49b897b4..29518f63 100644 --- a/src/main/java/org/ow2/authzforce/core/pdp/impl/DefaultRequestFilter.java +++ b/src/main/java/org/ow2/authzforce/core/pdp/impl/DefaultRequestFilter.java @@ -1,139 +1,142 @@ -/** - * Copyright (C) 2011-2015 Thales Services SAS. - * - * This file is part of AuthZForce. - * - * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software - * Foundation, either version 3 of the License, or (at your option) any later version. - * - * AuthZForce 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . - */ -package org.ow2.authzforce.core.pdp.impl; - -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.ow2.authzforce.core.pdp.api.BaseRequestFilter; -import org.ow2.authzforce.core.pdp.api.DatatypeFactoryRegistry; -import org.ow2.authzforce.core.pdp.api.IndeterminateEvaluationException; -import org.ow2.authzforce.core.pdp.api.IndividualDecisionRequest; -import org.ow2.authzforce.core.pdp.api.JaxbXACMLUtils.JaxbXACMLAttributesParser; -import org.ow2.authzforce.core.pdp.api.RequestFilter; -import org.ow2.authzforce.core.pdp.api.SingleCategoryAttributes; -import org.ow2.authzforce.core.pdp.api.StatusHelper; - -import net.sf.saxon.s9api.Processor; -import net.sf.saxon.s9api.XPathCompiler; -import oasis.names.tc.xacml._3_0.core.schema.wd_17.Attributes; - -/** - * Default Request filter for Individual Decision Requests only (no support of Multiple Decision Profile in particular) - * - */ -public final class DefaultRequestFilter extends BaseRequestFilter -{ - /** - * - * Factory for this type of request filter that allows duplicate <Attribute> with same meta-data in the same <Attributes> element of a Request - * (complying with XACML 3.0 core spec, §7.3.3). - * - */ - public static final class LaxFilterFactory implements RequestFilter.Factory - { - /** - * Request filter ID, as returned by {@link #getId()} - */ - public static final String ID = "urn:ow2:authzforce:xacml:request-filter:default-lax"; - - @Override - public String getId() - { - return ID; - } - - @Override - public RequestFilter getInstance(DatatypeFactoryRegistry datatypeFactoryRegistry, boolean strictAttributeIssuerMatch, boolean requireContentForXPath, Processor xmlProcessor) - { - return new DefaultRequestFilter(datatypeFactoryRegistry, strictAttributeIssuerMatch, true, requireContentForXPath, xmlProcessor); - } - - /** - * Singleton instance of Factory for DefaultRequestFilters - * - */ - public static final RequestFilter.Factory INSTANCE = new LaxFilterFactory(); - } - - /** - * - * Factory for this type of request filter that does NOT allow duplicate <Attribute> with same meta-data in the same <Attributes> element of a - * Request (NOT complying fully with XACML 3.0 core spec, §7.3.3). - * - */ - public static final class StrictFilterFactory implements RequestFilter.Factory - { - private static final String ID = "urn:ow2:authzforce:xacml:request-filter:default-strict"; - - @Override - public String getId() - { - return ID; - } - - @Override - public RequestFilter getInstance(DatatypeFactoryRegistry datatypeFactoryRegistry, boolean strictAttributeIssuerMatch, boolean requireContentForXPath, Processor xmlProcessor) - { - return new DefaultRequestFilter(datatypeFactoryRegistry, strictAttributeIssuerMatch, false, requireContentForXPath, xmlProcessor); - } - } - - private DefaultRequestFilter(DatatypeFactoryRegistry datatypeFactoryRegistry, boolean strictAttributeIssuerMatch, boolean allowAttributeDuplicates, boolean requireContentForXPath, Processor xmlProcessor) - { - super(datatypeFactoryRegistry, strictAttributeIssuerMatch, allowAttributeDuplicates, requireContentForXPath, xmlProcessor); - } - - @Override - public List filter(List attributesList, JaxbXACMLAttributesParser xacmlAttrsParser, boolean isApplicablePolicyIdListReturned, boolean combinedDecision, XPathCompiler xPathCompiler, Map namespaceURIsByPrefix) throws IndeterminateEvaluationException - { - - /* - * No support for Multiple Decision Profile -> no support for repeated categories as specified in Multiple Decision Profile. So we keep track of - * attribute categories to check duplicates. - */ - final Set attrCategoryNames = new HashSet<>(); - final MutableIndividualDecisionRequest individualDecisionRequest; - try - { - individualDecisionRequest = new MutableIndividualDecisionRequest(isApplicablePolicyIdListReturned); - } catch (IllegalArgumentException e) - { - throw new IndeterminateEvaluationException("Invalid RequestDefaults/XPathVersion", StatusHelper.STATUS_SYNTAX_ERROR, e); - } - - for (final Attributes jaxbAttributes : attributesList) - { - final String categoryName = jaxbAttributes.getCategory(); - if (!attrCategoryNames.add(categoryName)) - { - throw new IndeterminateEvaluationException("Unsupported repetition of Attributes[@Category='" + categoryName + "'] (feature 'urn:oasis:names:tc:xacml:3.0:profile:multiple:repeated-attribute-categories' is not supported)", StatusHelper.STATUS_SYNTAX_ERROR); - } - - final SingleCategoryAttributes categorySpecificAttributes = xacmlAttrsParser.parseAttributes(jaxbAttributes, xPathCompiler); - if (categorySpecificAttributes == null) - { - // skip this empty Attributes - continue; - } - - individualDecisionRequest.put(categoryName, categorySpecificAttributes); - } - - return Collections.singletonList(individualDecisionRequest); - } -} +/** + * Copyright (C) 2011-2015 Thales Services SAS. + * + * This file is part of AuthZForce. + * + * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * AuthZForce 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . + */ +package org.ow2.authzforce.core.pdp.impl; + +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.ow2.authzforce.core.pdp.api.BaseRequestFilter; +import org.ow2.authzforce.core.pdp.api.DatatypeFactoryRegistry; +import org.ow2.authzforce.core.pdp.api.IndeterminateEvaluationException; +import org.ow2.authzforce.core.pdp.api.IndividualDecisionRequest; +import org.ow2.authzforce.core.pdp.api.JaxbXACMLUtils.JaxbXACMLAttributesParser; +import org.ow2.authzforce.core.pdp.api.RequestFilter; +import org.ow2.authzforce.core.pdp.api.SingleCategoryAttributes; +import org.ow2.authzforce.core.pdp.api.StatusHelper; + +import net.sf.saxon.s9api.Processor; +import net.sf.saxon.s9api.XPathCompiler; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.Attributes; + +/** + * Default Request filter for Individual Decision Requests only (no support of Multiple Decision Profile in particular) + * + * @author cdangerv + * @version $Id: $ + */ +public final class DefaultRequestFilter extends BaseRequestFilter +{ + /** + * + * Factory for this type of request filter that allows duplicate <Attribute> with same meta-data in the same <Attributes> element of a Request + * (complying with XACML 3.0 core spec, §7.3.3). + * + */ + public static final class LaxFilterFactory implements RequestFilter.Factory + { + /** + * Request filter ID, as returned by {@link #getId()} + */ + public static final String ID = "urn:ow2:authzforce:xacml:request-filter:default-lax"; + + @Override + public String getId() + { + return ID; + } + + @Override + public RequestFilter getInstance(DatatypeFactoryRegistry datatypeFactoryRegistry, boolean strictAttributeIssuerMatch, boolean requireContentForXPath, Processor xmlProcessor) + { + return new DefaultRequestFilter(datatypeFactoryRegistry, strictAttributeIssuerMatch, true, requireContentForXPath, xmlProcessor); + } + + /** + * Singleton instance of Factory for DefaultRequestFilters + * + */ + public static final RequestFilter.Factory INSTANCE = new LaxFilterFactory(); + } + + /** + * + * Factory for this type of request filter that does NOT allow duplicate <Attribute> with same meta-data in the same <Attributes> element of a + * Request (NOT complying fully with XACML 3.0 core spec, §7.3.3). + * + */ + public static final class StrictFilterFactory implements RequestFilter.Factory + { + private static final String ID = "urn:ow2:authzforce:xacml:request-filter:default-strict"; + + @Override + public String getId() + { + return ID; + } + + @Override + public RequestFilter getInstance(DatatypeFactoryRegistry datatypeFactoryRegistry, boolean strictAttributeIssuerMatch, boolean requireContentForXPath, Processor xmlProcessor) + { + return new DefaultRequestFilter(datatypeFactoryRegistry, strictAttributeIssuerMatch, false, requireContentForXPath, xmlProcessor); + } + } + + private DefaultRequestFilter(DatatypeFactoryRegistry datatypeFactoryRegistry, boolean strictAttributeIssuerMatch, boolean allowAttributeDuplicates, boolean requireContentForXPath, Processor xmlProcessor) + { + super(datatypeFactoryRegistry, strictAttributeIssuerMatch, allowAttributeDuplicates, requireContentForXPath, xmlProcessor); + } + + /** {@inheritDoc} */ + @Override + public List filter(List attributesList, JaxbXACMLAttributesParser xacmlAttrsParser, boolean isApplicablePolicyIdListReturned, boolean combinedDecision, XPathCompiler xPathCompiler, Map namespaceURIsByPrefix) throws IndeterminateEvaluationException + { + + /* + * No support for Multiple Decision Profile -> no support for repeated categories as specified in Multiple Decision Profile. So we keep track of + * attribute categories to check duplicates. + */ + final Set attrCategoryNames = new HashSet<>(); + final MutableIndividualDecisionRequest individualDecisionRequest; + try + { + individualDecisionRequest = new MutableIndividualDecisionRequest(isApplicablePolicyIdListReturned); + } catch (IllegalArgumentException e) + { + throw new IndeterminateEvaluationException("Invalid RequestDefaults/XPathVersion", StatusHelper.STATUS_SYNTAX_ERROR, e); + } + + for (final Attributes jaxbAttributes : attributesList) + { + final String categoryName = jaxbAttributes.getCategory(); + if (!attrCategoryNames.add(categoryName)) + { + throw new IndeterminateEvaluationException("Unsupported repetition of Attributes[@Category='" + categoryName + "'] (feature 'urn:oasis:names:tc:xacml:3.0:profile:multiple:repeated-attribute-categories' is not supported)", StatusHelper.STATUS_SYNTAX_ERROR); + } + + final SingleCategoryAttributes categorySpecificAttributes = xacmlAttrsParser.parseAttributes(jaxbAttributes, xPathCompiler); + if (categorySpecificAttributes == null) + { + // skip this empty Attributes + continue; + } + + individualDecisionRequest.put(categoryName, categorySpecificAttributes); + } + + return Collections.singletonList(individualDecisionRequest); + } +} diff --git a/src/main/java/org/ow2/authzforce/core/pdp/impl/IndividualDecisionRequestContext.java b/src/main/java/org/ow2/authzforce/core/pdp/impl/IndividualDecisionRequestContext.java index f5eebe05..51ae5b01 100644 --- a/src/main/java/org/ow2/authzforce/core/pdp/impl/IndividualDecisionRequestContext.java +++ b/src/main/java/org/ow2/authzforce/core/pdp/impl/IndividualDecisionRequestContext.java @@ -38,7 +38,9 @@ import org.slf4j.LoggerFactory; /** * An {@link EvaluationContext} associated to an XACML Individual Decision Request, i.e. for evaluation to a single authorization decision Result (see Multiple * Decision Profile spec for more information on Individual Decision Request as opposed to Multiple Decision Request). - * + * + * @author cdangerv + * @version $Id: $ */ public class IndividualDecisionRequestContext implements EvaluationContext { @@ -72,7 +74,7 @@ public class IndividualDecisionRequestContext implements EvaluationContext /** * Constructs a new IndividualDecisionRequestContext based on the given request attributes and extra contents with support for XPath evaluation * against Content element in Attributes - * + * * @param namedAttributeMap * mutable named attribute map (attribute key and value pairs) from the original Request; null iff none. An attribute key is a global ID based on * attribute category,issuer,id. An attribute value is a bag of primitive values. @@ -80,7 +82,6 @@ public class IndividualDecisionRequestContext implements EvaluationContext * extra contents by attribute category (equivalent to XACML Attributes/Content elements); null iff no Content in the attribute category. * @param returnApplicablePolicyIdList * true iff list of IDs of policies matched during evaluation must be returned - * */ public IndividualDecisionRequestContext(Map> namedAttributeMap, Map extraContentsByAttributeCategory, boolean returnApplicablePolicyIdList) @@ -93,7 +94,7 @@ public class IndividualDecisionRequestContext implements EvaluationContext /** * Creates evaluation context from Individual Decision Request - * + * * @param individualDecisionReq * individual decision request */ @@ -103,6 +104,7 @@ public class IndividualDecisionRequestContext implements EvaluationContext .isApplicablePolicyIdentifiersReturned()); } + /** {@inheritDoc} */ @Override public Bag getAttributeDesignatorResult(AttributeGUID attributeGUID, Datatype attributeDatatype) throws IndeterminateEvaluationException @@ -134,6 +136,7 @@ public class IndividualDecisionRequestContext implements EvaluationContext return (Bag) bagResult; } + /** {@inheritDoc} */ @Override public boolean putAttributeDesignatorResultIfAbsent(AttributeGUID attributeGUID, Bag result) { @@ -154,12 +157,14 @@ public class IndividualDecisionRequestContext implements EvaluationContext return namedAttributes.put(attributeGUID, result) == null; } + /** {@inheritDoc} */ @Override public XdmNode getAttributesContent(String category) { return extraContentsByAttributeCategory == null ? null : extraContentsByAttributeCategory.get(category); } + /** {@inheritDoc} */ @Override public V getVariableValue(String variableId, Datatype expectedDatatype) throws IndeterminateEvaluationException { @@ -179,6 +184,7 @@ public class IndividualDecisionRequestContext implements EvaluationContext } } + /** {@inheritDoc} */ @Override public boolean putVariableIfAbsent(String variableId, Value value) { @@ -191,12 +197,14 @@ public class IndividualDecisionRequestContext implements EvaluationContext return varValsById.put(variableId, value) == null; } + /** {@inheritDoc} */ @Override public Value removeVariable(String variableId) { return varValsById.remove(variableId); } + /** {@inheritDoc} */ @Override public Bag getAttributeSelectorResult(AttributeSelectorId id, Datatype datatype) throws IndeterminateEvaluationException @@ -233,6 +241,7 @@ public class IndividualDecisionRequestContext implements EvaluationContext return (Bag) bagResult; } + /** {@inheritDoc} */ @Override public boolean putAttributeSelectorResultIfAbsent(AttributeSelectorId id, Bag result) throws IndeterminateEvaluationException { @@ -250,30 +259,35 @@ public class IndividualDecisionRequestContext implements EvaluationContext return attributeSelectorResults.put(id, result) == null; } + /** {@inheritDoc} */ @Override public Object getOther(String key) { return updatableProperties.get(key); } + /** {@inheritDoc} */ @Override public boolean containsKey(String key) { return updatableProperties.containsKey(key); } + /** {@inheritDoc} */ @Override public void putOther(String key, Object val) { updatableProperties.put(key, val); } + /** {@inheritDoc} */ @Override public Object remove(String key) { return updatableProperties.remove(key); } + /** {@inheritDoc} */ @Override public Iterator>> getAttributes() { @@ -281,6 +295,7 @@ public class IndividualDecisionRequestContext implements EvaluationContext return immutableAttributeSet.iterator(); } + /** {@inheritDoc} */ @Override public boolean isApplicablePolicyIdListReturned() { diff --git a/src/main/java/org/ow2/authzforce/core/pdp/impl/IndividualDecisionRequestEvaluator.java b/src/main/java/org/ow2/authzforce/core/pdp/impl/IndividualDecisionRequestEvaluator.java index e8e3adf3..4fd47c45 100644 --- a/src/main/java/org/ow2/authzforce/core/pdp/impl/IndividualDecisionRequestEvaluator.java +++ b/src/main/java/org/ow2/authzforce/core/pdp/impl/IndividualDecisionRequestEvaluator.java @@ -1,103 +1,119 @@ -/** - * Copyright (C) 2012-2015 Thales Services SAS. - * - * This file is part of AuthZForce CE. - * - * AuthZForce CE is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software - * Foundation, either version 3 of the License, or (at your option) any later version. - * - * AuthZForce CE 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with AuthZForce CE. If not, see . - */ -package org.ow2.authzforce.core.pdp.impl; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import javax.xml.bind.JAXBElement; - -import oasis.names.tc.xacml._3_0.core.schema.wd_17.Advice; -import oasis.names.tc.xacml._3_0.core.schema.wd_17.AssociatedAdvice; -import oasis.names.tc.xacml._3_0.core.schema.wd_17.DecisionType; -import oasis.names.tc.xacml._3_0.core.schema.wd_17.IdReferenceType; -import oasis.names.tc.xacml._3_0.core.schema.wd_17.Obligation; -import oasis.names.tc.xacml._3_0.core.schema.wd_17.Obligations; -import oasis.names.tc.xacml._3_0.core.schema.wd_17.PolicyIdentifierList; -import oasis.names.tc.xacml._3_0.core.schema.wd_17.Result; - -import org.ow2.authzforce.core.pdp.api.AttributeGUID; -import org.ow2.authzforce.core.pdp.api.Bag; -import org.ow2.authzforce.core.pdp.api.DecisionResult; -import org.ow2.authzforce.core.pdp.api.EvaluationContext; -import org.ow2.authzforce.core.pdp.api.IndividualDecisionRequest; -import org.ow2.authzforce.core.pdp.impl.policy.RootPolicyEvaluator; - -/** - * Individual decision request evaluator - * - */ -public abstract class IndividualDecisionRequestEvaluator -{ - private static final Result PERMIT = new Result(DecisionType.PERMIT, null, null, null, null, null); - private static final Result DENY = new Result(DecisionType.DENY, null, null, null, null, null); - - private final RootPolicyEvaluator rootPolicyEvaluator; - - /** - * Creates an evaluator - * - * @param rootPolicyEvaluator - * root policy evaluator that this request evaluator uses to evaluate individual decision request - */ - protected IndividualDecisionRequestEvaluator(RootPolicyEvaluator rootPolicyEvaluator) - { - assert rootPolicyEvaluator != null; - this.rootPolicyEvaluator = rootPolicyEvaluator; - } - - protected final Result evaluate(IndividualDecisionRequest request, Map> pdpIssuedAttributes) - { - assert request != null; - - // convert to EvaluationContext - /* - * The pdpIssuedAttributes may be re-used for many individual requests, so we must not modify it but clone it before individual decision request - * processing - */ - final Map> pdpEnhancedNamedAttributes = pdpIssuedAttributes == null ? new HashMap>() : new HashMap<>( - pdpIssuedAttributes); - final Map> reqNamedAttributes = request.getNamedAttributes(); - if (reqNamedAttributes != null) - { - pdpEnhancedNamedAttributes.putAll(reqNamedAttributes); - } - - final EvaluationContext ctx = new IndividualDecisionRequestContext(pdpEnhancedNamedAttributes, request.getExtraContentsByCategory(), - request.isApplicablePolicyIdentifiersReturned()); - final DecisionResult result = rootPolicyEvaluator.findAndEvaluate(ctx); - if (result == BaseDecisionResult.PERMIT) - { - return PERMIT; - } - - if (result == BaseDecisionResult.DENY) - { - return DENY; - } - - final List obligationList = result.getPepActions().getObligations(); - final List adviceList = result.getPepActions().getAdvices(); - final List> applicablePolicyIdList = result.getApplicablePolicyIdList(); - - return new Result(result.getDecision(), result.getStatus(), - obligationList == null || obligationList.isEmpty() ? null : new Obligations(obligationList), adviceList == null || adviceList.isEmpty() ? null - : new AssociatedAdvice(adviceList), request.getReturnedAttributes(), - applicablePolicyIdList == null || applicablePolicyIdList.isEmpty() ? null : new PolicyIdentifierList(applicablePolicyIdList)); - } - - protected abstract List evaluate(List individualDecisionRequests, - Map> pdpIssuedAttributes); -} \ No newline at end of file +/** + * Copyright (C) 2012-2015 Thales Services SAS. + * + * This file is part of AuthZForce CE. + * + * AuthZForce CE is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * AuthZForce CE 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with AuthZForce CE. If not, see . + */ +package org.ow2.authzforce.core.pdp.impl; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.xml.bind.JAXBElement; + +import oasis.names.tc.xacml._3_0.core.schema.wd_17.Advice; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.AssociatedAdvice; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.DecisionType; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.IdReferenceType; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.Obligation; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.Obligations; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.PolicyIdentifierList; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.Result; + +import org.ow2.authzforce.core.pdp.api.AttributeGUID; +import org.ow2.authzforce.core.pdp.api.Bag; +import org.ow2.authzforce.core.pdp.api.DecisionResult; +import org.ow2.authzforce.core.pdp.api.EvaluationContext; +import org.ow2.authzforce.core.pdp.api.IndividualDecisionRequest; +import org.ow2.authzforce.core.pdp.impl.policy.RootPolicyEvaluator; + +/** + * Individual decision request evaluator + * + * @author cdangerv + * @version $Id: $ + */ +public abstract class IndividualDecisionRequestEvaluator +{ + private static final Result PERMIT = new Result(DecisionType.PERMIT, null, null, null, null, null); + private static final Result DENY = new Result(DecisionType.DENY, null, null, null, null, null); + + private final RootPolicyEvaluator rootPolicyEvaluator; + + /** + * Creates an evaluator + * + * @param rootPolicyEvaluator + * root policy evaluator that this request evaluator uses to evaluate individual decision request + */ + protected IndividualDecisionRequestEvaluator(RootPolicyEvaluator rootPolicyEvaluator) + { + assert rootPolicyEvaluator != null; + this.rootPolicyEvaluator = rootPolicyEvaluator; + } + + /** + *

evaluate

+ * + * @param request a {@link org.ow2.authzforce.core.pdp.api.IndividualDecisionRequest} object. + * @param pdpIssuedAttributes a {@link java.util.Map} object. + * @return a {@link oasis.names.tc.xacml._3_0.core.schema.wd_17.Result} object. + */ + protected final Result evaluate(IndividualDecisionRequest request, Map> pdpIssuedAttributes) + { + assert request != null; + + // convert to EvaluationContext + /* + * The pdpIssuedAttributes may be re-used for many individual requests, so we must not modify it but clone it before individual decision request + * processing + */ + final Map> pdpEnhancedNamedAttributes = pdpIssuedAttributes == null ? new HashMap>() : new HashMap<>( + pdpIssuedAttributes); + final Map> reqNamedAttributes = request.getNamedAttributes(); + if (reqNamedAttributes != null) + { + pdpEnhancedNamedAttributes.putAll(reqNamedAttributes); + } + + final EvaluationContext ctx = new IndividualDecisionRequestContext(pdpEnhancedNamedAttributes, request.getExtraContentsByCategory(), + request.isApplicablePolicyIdentifiersReturned()); + final DecisionResult result = rootPolicyEvaluator.findAndEvaluate(ctx); + if (result == BaseDecisionResult.PERMIT) + { + return PERMIT; + } + + if (result == BaseDecisionResult.DENY) + { + return DENY; + } + + final List obligationList = result.getPepActions().getObligations(); + final List adviceList = result.getPepActions().getAdvices(); + final List> applicablePolicyIdList = result.getApplicablePolicyIdList(); + + return new Result(result.getDecision(), result.getStatus(), + obligationList == null || obligationList.isEmpty() ? null : new Obligations(obligationList), adviceList == null || adviceList.isEmpty() ? null + : new AssociatedAdvice(adviceList), request.getReturnedAttributes(), + applicablePolicyIdList == null || applicablePolicyIdList.isEmpty() ? null : new PolicyIdentifierList(applicablePolicyIdList)); + } + + /** + *

evaluate

+ * + * @param individualDecisionRequests a {@link java.util.List} object. + * @param pdpIssuedAttributes a {@link java.util.Map} object. + * @return a {@link java.util.List} object. + */ + protected abstract List evaluate(List individualDecisionRequests, + Map> pdpIssuedAttributes); +} diff --git a/src/main/java/org/ow2/authzforce/core/pdp/impl/MatchEvaluator.java b/src/main/java/org/ow2/authzforce/core/pdp/impl/MatchEvaluator.java index 858d10a1..98fb4820 100644 --- a/src/main/java/org/ow2/authzforce/core/pdp/impl/MatchEvaluator.java +++ b/src/main/java/org/ow2/authzforce/core/pdp/impl/MatchEvaluator.java @@ -34,6 +34,9 @@ import org.ow2.authzforce.core.pdp.impl.value.BooleanValue; /** * XACML Match evaluator. This is the part of the Target that actually evaluates whether the specified attribute values in the Target match the corresponding attribute values in the request context. + * + * @author cdangerv + * @version $Id: $ */ public class MatchEvaluator { @@ -47,14 +50,14 @@ public class MatchEvaluator /** * Instantiates Match evaluator from XACML-Schema-derived JAXB Match - * + * * @param jaxbMatch * XACML-Schema-derived JAXB Match * @param expFactory * bagExpression factory * @param xPathCompiler * XPath compiler corresponding to enclosing policy(set) default XPath version - * @throws IllegalArgumentException + * @throws java.lang.IllegalArgumentException * invalid jaxbMatch */ public MatchEvaluator(Match jaxbMatch, XPathCompiler xPathCompiler, ExpressionFactory expFactory) throws IllegalArgumentException @@ -104,12 +107,11 @@ public class MatchEvaluator /** * Determines whether this Match matches the input request (whether it is applicable) - * + * * @param context * the evaluation context - * * @return true iff the context matches - * @throws IndeterminateEvaluationException + * @throws org.ow2.authzforce.core.pdp.api.IndeterminateEvaluationException * error occurred evaluating the Match element in this evaluation {@code context} */ public boolean match(EvaluationContext context) throws IndeterminateEvaluationException diff --git a/src/main/java/org/ow2/authzforce/core/pdp/impl/ModularAttributeProvider.java b/src/main/java/org/ow2/authzforce/core/pdp/impl/ModularAttributeProvider.java index 8deb0bc0..df7afe65 100644 --- a/src/main/java/org/ow2/authzforce/core/pdp/impl/ModularAttributeProvider.java +++ b/src/main/java/org/ow2/authzforce/core/pdp/impl/ModularAttributeProvider.java @@ -1,178 +1,182 @@ -/** - * Copyright (C) 2011-2015 Thales Services SAS. - * - * This file is part of AuthZForce. - * - * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software - * Foundation, either version 3 of the License, or (at your option) any later version. - * - * AuthZForce 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . - */ -package org.ow2.authzforce.core.pdp.impl; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeDesignatorType; - -import org.ow2.authzforce.core.pdp.api.AttributeGUID; -import org.ow2.authzforce.core.pdp.api.AttributeProvider; -import org.ow2.authzforce.core.pdp.api.AttributeProviderModule; -import org.ow2.authzforce.core.pdp.api.AttributeValue; -import org.ow2.authzforce.core.pdp.api.Bag; -import org.ow2.authzforce.core.pdp.api.Bags; -import org.ow2.authzforce.core.pdp.api.Datatype; -import org.ow2.authzforce.core.pdp.api.EvaluationContext; -import org.ow2.authzforce.core.pdp.api.IndeterminateEvaluationException; -import org.ow2.authzforce.core.pdp.api.StatusHelper; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * AttributeProvider working with sub-modules, each responsible of finding specific attributes in a specific way from a specific source. This attribute Provider - * tries to resolve attribute values in current evaluation context first, then if not there, query the sub-modules. - */ -public class ModularAttributeProvider implements AttributeProvider -{ - private static final Logger LOGGER = LoggerFactory.getLogger(ModularAttributeProvider.class); - - // AttributeDesignator Provider modules by supported/provided attribute ID (global ID: category, issuer, - // AttributeId) - private final Map designatorModsByAttrId; - - /** - * Instantiates attribute Provider that tries to find attribute values in evaluation context, then, if not there, query sub-modules providing the requested - * attribute ID, if any. - * - * @param attributeProviderModulesByAttributeId - * attribute Provider modules sorted by supported attribute ID; may be null if none - */ - public ModularAttributeProvider(Map attributeProviderModulesByAttributeId) - { - this(attributeProviderModulesByAttributeId, null); - } - - /** - * Instantiates attribute Provider that tries to find attribute values in evaluation context, then, if not there, query sub-modules providing the requested - * attribute ID, if any. - * - * @param attributeProviderModulesByAttributeId - * attribute Provider modules sorted by supported attribute ID; may be null if none - * @param selectedAttributeSupport - * (optional) selection of attributes to be supported, i.e. only attributes from this set may be supported/resolved by this attribute Provider; - * therefore, only the part of {@code attributeProviderModulesByAttributeId} matching these attributes are to be used by this Provider. If - * {@code selectedAttributeSupport == null}, this parameter is ignored, i.e. this is equivalent to - * {@link ModularAttributeProvider#ModularAttributeProvider(Map)} - */ - public ModularAttributeProvider(Map attributeProviderModulesByAttributeId, - Set selectedAttributeSupport) - { - if (attributeProviderModulesByAttributeId == null) - { - this.designatorModsByAttrId = Collections.emptyMap(); - } else if (selectedAttributeSupport == null) - { - designatorModsByAttrId = new HashMap<>(attributeProviderModulesByAttributeId); - } else - { - designatorModsByAttrId = new HashMap<>(selectedAttributeSupport.size()); - for (final AttributeDesignatorType requiredAttr : selectedAttributeSupport) - { - final AttributeGUID requiredAttrGUID = new AttributeGUID(requiredAttr); - final AttributeProviderModule requiredAttrProviderMod = attributeProviderModulesByAttributeId.get(requiredAttrGUID); - // requiredAttrProviderMod = null means it should be provided by the request - // context (in the initial request from PEP) - if (requiredAttrProviderMod != null) - { - - designatorModsByAttrId.put(requiredAttrGUID, requiredAttrProviderMod); - } - } - } - } - - @Override - public final Bag get(AttributeGUID attributeGUID, Datatype attributeDatatype, EvaluationContext context) - throws IndeterminateEvaluationException - { - try - { - final Bag contextBag = context.getAttributeDesignatorResult(attributeGUID, attributeDatatype); - if (contextBag != null) - { - LOGGER.debug("Values of attribute {}, type={} found in evaluation context: {}", attributeGUID, attributeDatatype, contextBag); - return contextBag; - } - - // else attribute not found in context, ask the Provider modules, if any - LOGGER.debug("Requesting attribute {} from Provider modules (by provided attribute ID): {}", attributeGUID, designatorModsByAttrId); - final AttributeProviderModule attrProviderModule = designatorModsByAttrId.get(attributeGUID); - if (attrProviderModule == null) - { - LOGGER.debug("No value found for required attribute {}, type={} in evaluation context and not supported by any attribute Provider module", - attributeGUID, attributeDatatype); - throw new IndeterminateEvaluationException("Not in context and no attribute Provider module supporting attribute: " + attributeGUID, - StatusHelper.STATUS_MISSING_ATTRIBUTE); - } - - final Bag result = attrProviderModule.get(attributeGUID, attributeDatatype, context); - - /* - * Cache the attribute value(s) in context to avoid waste of time querying the module twice for same attribute - */ - context.putAttributeDesignatorResultIfAbsent(attributeGUID, result); - LOGGER.debug("Values of attribute {}, type={} returned by attribute Provider module #{} (cached in context): {}", attributeGUID, attributeDatatype, - attrProviderModule, result); - return result; - } catch (IndeterminateEvaluationException e) - { - /* - * This error does not necessarily matter, it depends on whether the attribute is required, i.e. MustBePresent=true for AttributeDesignator/Selector - * So we let AttributeDesignator/Select#evaluate() method log the errors if MustBePresent=true. Here debug level is enough - */ - LOGGER.debug("Error finding attribute {}, type={}", attributeGUID, attributeDatatype, e); - - /** - * If error occurred, we put the empty value to prevent retry in the same context, which may succeed at another time in the same context, resulting - * in different value of the same attribute at different times during evaluation within the same context, therefore inconsistencies. The value(s) - * must remain constant during the evaluation context, as explained in section 7.3.5 Attribute Retrieval of XACML core spec: - *

- * Regardless of any dynamic modifications of the request context during policy evaluation, the PDP SHALL behave as if each bag of attribute values - * is fully populated in the context before it is first tested, and is thereafter immutable during evaluation. (That is, every subsequent test of - * that attribute shall use 3313 the same bag of values that was initially tested.) - *

- * Therefore, if no value found, we keep it that way until evaluation is done for the current request context. - *

- * We could put the null value to indicate the evaluation error, instead of an empty Bag, but it would make the result of the code used at the start - * of this method ambiguous/confusing: - *

- * - * final Bag contextBag = context.getAttributeDesignatorResult(attributeGUID,...) - * - *

- *

- * Indeed, contextBag could be null for one of these two reasons: - *

    - *
  1. The attribute ('attributeGUID') has never been requested in this context; - *
  2. It has been requested before in this context but could not be found: error occurred (IndeterminateEvaluationException)
  3. - *
- * To avoid this confusion, we put an empty Bag (with some error info saying why this is empty). - *

- */ - final Bag result = Bags.empty(attributeDatatype, e); - /* - * NOTE: It might happen - e.g. in conformance test IIB033 (Request's resource-id attribute datatype is different from datatype used in Policy) - - * that context.getAttributeDesignatorResult(attributeGUID, bagDatatype) threw IndeterminateEvaluationException although a value for 'attributeGUID' - * exists in context, because the existing datatype is different from requested 'bagDatatype'. In this case, the call below will return false (the - * value should not be overridden). We don't care about the result; what matters is that the value is set to an empty bag if there was no value. - */ - context.putAttributeDesignatorResultIfAbsent(attributeGUID, result); - return result; - } - } -} +/** + * Copyright (C) 2011-2015 Thales Services SAS. + * + * This file is part of AuthZForce. + * + * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * AuthZForce 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . + */ +package org.ow2.authzforce.core.pdp.impl; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeDesignatorType; + +import org.ow2.authzforce.core.pdp.api.AttributeGUID; +import org.ow2.authzforce.core.pdp.api.AttributeProvider; +import org.ow2.authzforce.core.pdp.api.AttributeProviderModule; +import org.ow2.authzforce.core.pdp.api.AttributeValue; +import org.ow2.authzforce.core.pdp.api.Bag; +import org.ow2.authzforce.core.pdp.api.Bags; +import org.ow2.authzforce.core.pdp.api.Datatype; +import org.ow2.authzforce.core.pdp.api.EvaluationContext; +import org.ow2.authzforce.core.pdp.api.IndeterminateEvaluationException; +import org.ow2.authzforce.core.pdp.api.StatusHelper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * AttributeProvider working with sub-modules, each responsible of finding specific attributes in a specific way from a specific source. This attribute Provider + * tries to resolve attribute values in current evaluation context first, then if not there, query the sub-modules. + * + * @author cdangerv + * @version $Id: $ + */ +public class ModularAttributeProvider implements AttributeProvider +{ + private static final Logger LOGGER = LoggerFactory.getLogger(ModularAttributeProvider.class); + + // AttributeDesignator Provider modules by supported/provided attribute ID (global ID: category, issuer, + // AttributeId) + private final Map designatorModsByAttrId; + + /** + * Instantiates attribute Provider that tries to find attribute values in evaluation context, then, if not there, query sub-modules providing the requested + * attribute ID, if any. + * + * @param attributeProviderModulesByAttributeId + * attribute Provider modules sorted by supported attribute ID; may be null if none + */ + public ModularAttributeProvider(Map attributeProviderModulesByAttributeId) + { + this(attributeProviderModulesByAttributeId, null); + } + + /** + * Instantiates attribute Provider that tries to find attribute values in evaluation context, then, if not there, query sub-modules providing the requested + * attribute ID, if any. + * + * @param attributeProviderModulesByAttributeId + * attribute Provider modules sorted by supported attribute ID; may be null if none + * @param selectedAttributeSupport + * (optional) selection of attributes to be supported, i.e. only attributes from this set may be supported/resolved by this attribute Provider; + * therefore, only the part of {@code attributeProviderModulesByAttributeId} matching these attributes are to be used by this Provider. If + * {@code selectedAttributeSupport == null}, this parameter is ignored, i.e. this is equivalent to + * {@link ModularAttributeProvider#ModularAttributeProvider(Map)} + */ + public ModularAttributeProvider(Map attributeProviderModulesByAttributeId, + Set selectedAttributeSupport) + { + if (attributeProviderModulesByAttributeId == null) + { + this.designatorModsByAttrId = Collections.emptyMap(); + } else if (selectedAttributeSupport == null) + { + designatorModsByAttrId = new HashMap<>(attributeProviderModulesByAttributeId); + } else + { + designatorModsByAttrId = new HashMap<>(selectedAttributeSupport.size()); + for (final AttributeDesignatorType requiredAttr : selectedAttributeSupport) + { + final AttributeGUID requiredAttrGUID = new AttributeGUID(requiredAttr); + final AttributeProviderModule requiredAttrProviderMod = attributeProviderModulesByAttributeId.get(requiredAttrGUID); + // requiredAttrProviderMod = null means it should be provided by the request + // context (in the initial request from PEP) + if (requiredAttrProviderMod != null) + { + + designatorModsByAttrId.put(requiredAttrGUID, requiredAttrProviderMod); + } + } + } + } + + /** {@inheritDoc} */ + @Override + public final Bag get(AttributeGUID attributeGUID, Datatype attributeDatatype, EvaluationContext context) + throws IndeterminateEvaluationException + { + try + { + final Bag contextBag = context.getAttributeDesignatorResult(attributeGUID, attributeDatatype); + if (contextBag != null) + { + LOGGER.debug("Values of attribute {}, type={} found in evaluation context: {}", attributeGUID, attributeDatatype, contextBag); + return contextBag; + } + + // else attribute not found in context, ask the Provider modules, if any + LOGGER.debug("Requesting attribute {} from Provider modules (by provided attribute ID): {}", attributeGUID, designatorModsByAttrId); + final AttributeProviderModule attrProviderModule = designatorModsByAttrId.get(attributeGUID); + if (attrProviderModule == null) + { + LOGGER.debug("No value found for required attribute {}, type={} in evaluation context and not supported by any attribute Provider module", + attributeGUID, attributeDatatype); + throw new IndeterminateEvaluationException("Not in context and no attribute Provider module supporting attribute: " + attributeGUID, + StatusHelper.STATUS_MISSING_ATTRIBUTE); + } + + final Bag result = attrProviderModule.get(attributeGUID, attributeDatatype, context); + + /* + * Cache the attribute value(s) in context to avoid waste of time querying the module twice for same attribute + */ + context.putAttributeDesignatorResultIfAbsent(attributeGUID, result); + LOGGER.debug("Values of attribute {}, type={} returned by attribute Provider module #{} (cached in context): {}", attributeGUID, attributeDatatype, + attrProviderModule, result); + return result; + } catch (IndeterminateEvaluationException e) + { + /* + * This error does not necessarily matter, it depends on whether the attribute is required, i.e. MustBePresent=true for AttributeDesignator/Selector + * So we let AttributeDesignator/Select#evaluate() method log the errors if MustBePresent=true. Here debug level is enough + */ + LOGGER.debug("Error finding attribute {}, type={}", attributeGUID, attributeDatatype, e); + + /** + * If error occurred, we put the empty value to prevent retry in the same context, which may succeed at another time in the same context, resulting + * in different value of the same attribute at different times during evaluation within the same context, therefore inconsistencies. The value(s) + * must remain constant during the evaluation context, as explained in section 7.3.5 Attribute Retrieval of XACML core spec: + *

+ * Regardless of any dynamic modifications of the request context during policy evaluation, the PDP SHALL behave as if each bag of attribute values + * is fully populated in the context before it is first tested, and is thereafter immutable during evaluation. (That is, every subsequent test of + * that attribute shall use 3313 the same bag of values that was initially tested.) + *

+ * Therefore, if no value found, we keep it that way until evaluation is done for the current request context. + *

+ * We could put the null value to indicate the evaluation error, instead of an empty Bag, but it would make the result of the code used at the start + * of this method ambiguous/confusing: + *

+ * + * final Bag contextBag = context.getAttributeDesignatorResult(attributeGUID,...) + * + *

+ *

+ * Indeed, contextBag could be null for one of these two reasons: + *

    + *
  1. The attribute ('attributeGUID') has never been requested in this context; + *
  2. It has been requested before in this context but could not be found: error occurred (IndeterminateEvaluationException)
  3. + *
+ * To avoid this confusion, we put an empty Bag (with some error info saying why this is empty). + *

+ */ + final Bag result = Bags.empty(attributeDatatype, e); + /* + * NOTE: It might happen - e.g. in conformance test IIB033 (Request's resource-id attribute datatype is different from datatype used in Policy) - + * that context.getAttributeDesignatorResult(attributeGUID, bagDatatype) threw IndeterminateEvaluationException although a value for 'attributeGUID' + * exists in context, because the existing datatype is different from requested 'bagDatatype'. In this case, the call below will return false (the + * value should not be overridden). We don't care about the result; what matters is that the value is set to an empty bag if there was no value. + */ + context.putAttributeDesignatorResultIfAbsent(attributeGUID, result); + return result; + } + } +} diff --git a/src/main/java/org/ow2/authzforce/core/pdp/impl/MultiDecisionRequestFilter.java b/src/main/java/org/ow2/authzforce/core/pdp/impl/MultiDecisionRequestFilter.java index 924740d7..a51da93a 100644 --- a/src/main/java/org/ow2/authzforce/core/pdp/impl/MultiDecisionRequestFilter.java +++ b/src/main/java/org/ow2/authzforce/core/pdp/impl/MultiDecisionRequestFilter.java @@ -1,200 +1,203 @@ -/** - * Copyright (C) 2011-2015 Thales Services SAS. - * - * This file is part of AuthZForce. - * - * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software - * Foundation, either version 3 of the License, or (at your option) any later version. - * - * AuthZForce 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . - */ -package org.ow2.authzforce.core.pdp.impl; - -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.ListIterator; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Queue; - -import org.ow2.authzforce.core.pdp.api.BaseRequestFilter; -import org.ow2.authzforce.core.pdp.api.DatatypeFactoryRegistry; -import org.ow2.authzforce.core.pdp.api.IndeterminateEvaluationException; -import org.ow2.authzforce.core.pdp.api.IndividualDecisionRequest; -import org.ow2.authzforce.core.pdp.api.JaxbXACMLUtils.JaxbXACMLAttributesParser; -import org.ow2.authzforce.core.pdp.api.RequestFilter; -import org.ow2.authzforce.core.pdp.api.SingleCategoryAttributes; -import org.ow2.authzforce.core.pdp.api.StatusHelper; - -import net.sf.saxon.s9api.Processor; -import net.sf.saxon.s9api.XPathCompiler; -import oasis.names.tc.xacml._3_0.core.schema.wd_17.Attributes; - -/** - * Request filter implementing Multiple Decision Profile, section 2.3 (repeated attribute categories). Other schemes are not supported. - * - */ -public final class MultiDecisionRequestFilter extends BaseRequestFilter -{ - /** - * - * Factory for this type of request filter that allows duplicate <Attribute> with same meta-data in the same <Attributes> element of a Request - * (complying with XACML 3.0 core spec, §7.3.3). - * - */ - public static final class LaxFilterFactory implements RequestFilter.Factory - { - /** - * Request filter ID, returned by {@link #getId()} - */ - public static final String ID = "urn:ow2:authzforce:xacml:request-filter:multiple:repeated-attribute-categories-lax"; - - @Override - public String getId() - { - return ID; - } - - @Override - public RequestFilter getInstance(DatatypeFactoryRegistry datatypeFactoryRegistry, boolean strictAttributeIssuerMatch, boolean requireContentForXPath, Processor xmlProcessor) - { - return new MultiDecisionRequestFilter(datatypeFactoryRegistry, strictAttributeIssuerMatch, true, requireContentForXPath, xmlProcessor); - } - } - - /** - * - * Factory for this type of request filter that does NOT allow duplicate <Attribute> with same meta-data in the same <Attributes> element of a - * Request (NOT complying with XACML 3.0 core spec, §7.3.3). - * - */ - public static final class StrictFilterFactory implements RequestFilter.Factory - { - /** - * Request filter ID, returned by {@link #getId()} - */ - public static final String ID = "urn:ow2:authzforce:xacml:request-filter:multiple:repeated-attribute-categories-strict"; - - @Override - public String getId() - { - return ID; - } - - @Override - public RequestFilter getInstance(DatatypeFactoryRegistry datatypeFactoryRegistry, boolean strictAttributeIssuerMatch, boolean requireContentForXPath, Processor xmlProcessor) - { - return new MultiDecisionRequestFilter(datatypeFactoryRegistry, strictAttributeIssuerMatch, false, requireContentForXPath, xmlProcessor); - } - } - - // private static Logger LOGGER = LoggerFactory.getLogger(MultiDecisionRequestFilter.class); - - private MultiDecisionRequestFilter(DatatypeFactoryRegistry datatypeFactoryRegistry, boolean strictAttributeIssuerMatch, boolean allowAttributeDuplicates, boolean requireContentForXPath, Processor xmlProcessor) - { - super(datatypeFactoryRegistry, strictAttributeIssuerMatch, allowAttributeDuplicates, requireContentForXPath, xmlProcessor); - } - - @Override - public List filter(List attributesList, JaxbXACMLAttributesParser xacmlAttrsParser, boolean isApplicablePolicyIdListReturned, boolean combinedDecision, XPathCompiler xPathCompiler, Map namespaceURIsByPrefix) throws IndeterminateEvaluationException - { - /* - * Parse Request attributes and group possibly repeated categories to implement Multiple Decision Profile, §2.3. - */ - final Map>> multiReqAttrAlternativesByCategory = new HashMap<>(); - for (final Attributes jaxbAttributes : attributesList) - { - final String categoryName = jaxbAttributes.getCategory(); - final SingleCategoryAttributes categoryAttributesAlternative = xacmlAttrsParser.parseAttributes(jaxbAttributes, xPathCompiler); - if (categoryAttributesAlternative == null) - { - // skip this empty Attributes - continue; - } - - final Queue> oldAttrAlternatives = multiReqAttrAlternativesByCategory.get(categoryName); - final Queue> newAttrAlternatives; - if (oldAttrAlternatives == null) - { - newAttrAlternatives = new ArrayDeque<>(); - multiReqAttrAlternativesByCategory.put(categoryName, newAttrAlternatives); - } else - { - newAttrAlternatives = oldAttrAlternatives; - } - - newAttrAlternatives.add(categoryAttributesAlternative); - } - - /* - * Create initial individual request from which all others will be created/cloned - */ - // returnPolicyIdList not supported so always set to false - final MutableIndividualDecisionRequest initialIndividualReq; - try - { - initialIndividualReq = new MutableIndividualDecisionRequest(isApplicablePolicyIdListReturned); - } catch (IllegalArgumentException e) - { - throw new IndeterminateEvaluationException("Invalid RequestDefaults/XPathVersion", StatusHelper.STATUS_SYNTAX_ERROR, e); - } - /* - * Generate the Multiple Individual Decision Requests starting with initialIndividualReq and cloning/adding new attributes/content for each new - * attribute category's Attributes alternative in requestAttrAlternativesByCategory - */ - /* - * XACML Multiple Decision Profile, § 2.3.3: "For each combination of repeated elements, one Individual Decision Request SHALL be created. - * This Individual Request SHALL be identical to the original request context with one exception: only one element of each repeated - * category SHALL be present." - */ - final List individualRequests = new ArrayList<>(); - individualRequests.add(initialIndividualReq); - // for each attribute category - for (final Entry>> multiReqAttrAlternativesByCategoryEntry : multiReqAttrAlternativesByCategory.entrySet()) - { - final String categoryName = multiReqAttrAlternativesByCategoryEntry.getKey(); - final Queue> categoryAlternatives = multiReqAttrAlternativesByCategoryEntry.getValue(); - /* - * Get the first category () alternative to be added to the individual requests existing in the individualRequests already, i.e. the - * "old" ones; whereas the other alternatives (if any) will be added to new individual request cloned from these "old" ones. - */ - final SingleCategoryAttributes categoryAlternative0 = categoryAlternatives.poll(); - if (categoryAlternative0 == null) - { - // no alternative / no repeated category - continue; - } - - final ListIterator individualRequestsIterator = individualRequests.listIterator(); - while (individualRequestsIterator.hasNext()) - { - final MutableIndividualDecisionRequest oldReq = individualRequestsIterator.next(); - /* - * Before we add the first category alternative (categoryAlternative0) to the oldReq already created (the "old" one), we clone it for every - * other alternative, then add this other alternative to the new clone. Note that we called categoryAlternatives.poll() before, removing the - * first alternative, so categoryAlternatives only contains the other alternatives now. - */ - for (final SingleCategoryAttributes otherCategoryAlternative : categoryAlternatives) - { - // clone the request - final MutableIndividualDecisionRequest newReq = new MutableIndividualDecisionRequest(oldReq); - newReq.put(categoryName, otherCategoryAlternative); - // add it to the final list of individual requests - individualRequestsIterator.add(newReq); - } - - // Now we are done cloning, we can add the first category alternative to - // individualReqCtx - oldReq.put(categoryName, categoryAlternative0); - } - - } - - return individualRequests; - } -} +/** + * Copyright (C) 2011-2015 Thales Services SAS. + * + * This file is part of AuthZForce. + * + * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * AuthZForce 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . + */ +package org.ow2.authzforce.core.pdp.impl; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Queue; + +import org.ow2.authzforce.core.pdp.api.BaseRequestFilter; +import org.ow2.authzforce.core.pdp.api.DatatypeFactoryRegistry; +import org.ow2.authzforce.core.pdp.api.IndeterminateEvaluationException; +import org.ow2.authzforce.core.pdp.api.IndividualDecisionRequest; +import org.ow2.authzforce.core.pdp.api.JaxbXACMLUtils.JaxbXACMLAttributesParser; +import org.ow2.authzforce.core.pdp.api.RequestFilter; +import org.ow2.authzforce.core.pdp.api.SingleCategoryAttributes; +import org.ow2.authzforce.core.pdp.api.StatusHelper; + +import net.sf.saxon.s9api.Processor; +import net.sf.saxon.s9api.XPathCompiler; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.Attributes; + +/** + * Request filter implementing Multiple Decision Profile, section 2.3 (repeated attribute categories). Other schemes are not supported. + * + * @author cdangerv + * @version $Id: $ + */ +public final class MultiDecisionRequestFilter extends BaseRequestFilter +{ + /** + * + * Factory for this type of request filter that allows duplicate <Attribute> with same meta-data in the same <Attributes> element of a Request + * (complying with XACML 3.0 core spec, §7.3.3). + * + */ + public static final class LaxFilterFactory implements RequestFilter.Factory + { + /** + * Request filter ID, returned by {@link #getId()} + */ + public static final String ID = "urn:ow2:authzforce:xacml:request-filter:multiple:repeated-attribute-categories-lax"; + + @Override + public String getId() + { + return ID; + } + + @Override + public RequestFilter getInstance(DatatypeFactoryRegistry datatypeFactoryRegistry, boolean strictAttributeIssuerMatch, boolean requireContentForXPath, Processor xmlProcessor) + { + return new MultiDecisionRequestFilter(datatypeFactoryRegistry, strictAttributeIssuerMatch, true, requireContentForXPath, xmlProcessor); + } + } + + /** + * + * Factory for this type of request filter that does NOT allow duplicate <Attribute> with same meta-data in the same <Attributes> element of a + * Request (NOT complying with XACML 3.0 core spec, §7.3.3). + * + */ + public static final class StrictFilterFactory implements RequestFilter.Factory + { + /** + * Request filter ID, returned by {@link #getId()} + */ + public static final String ID = "urn:ow2:authzforce:xacml:request-filter:multiple:repeated-attribute-categories-strict"; + + @Override + public String getId() + { + return ID; + } + + @Override + public RequestFilter getInstance(DatatypeFactoryRegistry datatypeFactoryRegistry, boolean strictAttributeIssuerMatch, boolean requireContentForXPath, Processor xmlProcessor) + { + return new MultiDecisionRequestFilter(datatypeFactoryRegistry, strictAttributeIssuerMatch, false, requireContentForXPath, xmlProcessor); + } + } + + // private static Logger LOGGER = LoggerFactory.getLogger(MultiDecisionRequestFilter.class); + + private MultiDecisionRequestFilter(DatatypeFactoryRegistry datatypeFactoryRegistry, boolean strictAttributeIssuerMatch, boolean allowAttributeDuplicates, boolean requireContentForXPath, Processor xmlProcessor) + { + super(datatypeFactoryRegistry, strictAttributeIssuerMatch, allowAttributeDuplicates, requireContentForXPath, xmlProcessor); + } + + /** {@inheritDoc} */ + @Override + public List filter(List attributesList, JaxbXACMLAttributesParser xacmlAttrsParser, boolean isApplicablePolicyIdListReturned, boolean combinedDecision, XPathCompiler xPathCompiler, Map namespaceURIsByPrefix) throws IndeterminateEvaluationException + { + /* + * Parse Request attributes and group possibly repeated categories to implement Multiple Decision Profile, §2.3. + */ + final Map>> multiReqAttrAlternativesByCategory = new HashMap<>(); + for (final Attributes jaxbAttributes : attributesList) + { + final String categoryName = jaxbAttributes.getCategory(); + final SingleCategoryAttributes categoryAttributesAlternative = xacmlAttrsParser.parseAttributes(jaxbAttributes, xPathCompiler); + if (categoryAttributesAlternative == null) + { + // skip this empty Attributes + continue; + } + + final Queue> oldAttrAlternatives = multiReqAttrAlternativesByCategory.get(categoryName); + final Queue> newAttrAlternatives; + if (oldAttrAlternatives == null) + { + newAttrAlternatives = new ArrayDeque<>(); + multiReqAttrAlternativesByCategory.put(categoryName, newAttrAlternatives); + } else + { + newAttrAlternatives = oldAttrAlternatives; + } + + newAttrAlternatives.add(categoryAttributesAlternative); + } + + /* + * Create initial individual request from which all others will be created/cloned + */ + // returnPolicyIdList not supported so always set to false + final MutableIndividualDecisionRequest initialIndividualReq; + try + { + initialIndividualReq = new MutableIndividualDecisionRequest(isApplicablePolicyIdListReturned); + } catch (IllegalArgumentException e) + { + throw new IndeterminateEvaluationException("Invalid RequestDefaults/XPathVersion", StatusHelper.STATUS_SYNTAX_ERROR, e); + } + /* + * Generate the Multiple Individual Decision Requests starting with initialIndividualReq and cloning/adding new attributes/content for each new + * attribute category's Attributes alternative in requestAttrAlternativesByCategory + */ + /* + * XACML Multiple Decision Profile, § 2.3.3: "For each combination of repeated elements, one Individual Decision Request SHALL be created. + * This Individual Request SHALL be identical to the original request context with one exception: only one element of each repeated + * category SHALL be present." + */ + final List individualRequests = new ArrayList<>(); + individualRequests.add(initialIndividualReq); + // for each attribute category + for (final Entry>> multiReqAttrAlternativesByCategoryEntry : multiReqAttrAlternativesByCategory.entrySet()) + { + final String categoryName = multiReqAttrAlternativesByCategoryEntry.getKey(); + final Queue> categoryAlternatives = multiReqAttrAlternativesByCategoryEntry.getValue(); + /* + * Get the first category () alternative to be added to the individual requests existing in the individualRequests already, i.e. the + * "old" ones; whereas the other alternatives (if any) will be added to new individual request cloned from these "old" ones. + */ + final SingleCategoryAttributes categoryAlternative0 = categoryAlternatives.poll(); + if (categoryAlternative0 == null) + { + // no alternative / no repeated category + continue; + } + + final ListIterator individualRequestsIterator = individualRequests.listIterator(); + while (individualRequestsIterator.hasNext()) + { + final MutableIndividualDecisionRequest oldReq = individualRequestsIterator.next(); + /* + * Before we add the first category alternative (categoryAlternative0) to the oldReq already created (the "old" one), we clone it for every + * other alternative, then add this other alternative to the new clone. Note that we called categoryAlternatives.poll() before, removing the + * first alternative, so categoryAlternatives only contains the other alternatives now. + */ + for (final SingleCategoryAttributes otherCategoryAlternative : categoryAlternatives) + { + // clone the request + final MutableIndividualDecisionRequest newReq = new MutableIndividualDecisionRequest(oldReq); + newReq.put(categoryName, otherCategoryAlternative); + // add it to the final list of individual requests + individualRequestsIterator.add(newReq); + } + + // Now we are done cloning, we can add the first category alternative to + // individualReqCtx + oldReq.put(categoryName, categoryAlternative0); + } + + } + + return individualRequests; + } +} diff --git a/src/main/java/org/ow2/authzforce/core/pdp/impl/MutableIndividualDecisionRequest.java b/src/main/java/org/ow2/authzforce/core/pdp/impl/MutableIndividualDecisionRequest.java index 1bf517a4..3f8e52df 100644 --- a/src/main/java/org/ow2/authzforce/core/pdp/impl/MutableIndividualDecisionRequest.java +++ b/src/main/java/org/ow2/authzforce/core/pdp/impl/MutableIndividualDecisionRequest.java @@ -1,157 +1,161 @@ -/** - * Copyright (C) 2011-2015 Thales Services SAS. - * - * This file is part of AuthZForce. - * - * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software - * Foundation, either version 3 of the License, or (at your option) any later version. - * - * AuthZForce 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . - */ -package org.ow2.authzforce.core.pdp.impl; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -import net.sf.saxon.s9api.XdmNode; -import oasis.names.tc.xacml._3_0.core.schema.wd_17.Attributes; - -import org.ow2.authzforce.core.pdp.api.AttributeGUID; -import org.ow2.authzforce.core.pdp.api.Bag; -import org.ow2.authzforce.core.pdp.api.IndividualDecisionRequest; -import org.ow2.authzforce.core.pdp.api.SingleCategoryAttributes; - -/** - * Mutable Individual Decision Request - */ -public class MutableIndividualDecisionRequest implements IndividualDecisionRequest -{ - private final Map> namedAttributes; - private final Map extraContentsByCategory; - private final List attributesToIncludeInResult; - private final boolean returnApplicablePolicyIdList; - - /** - * Creates empty request (no attribute) - * - * @param returnPolicyIdList - * equivalent of XACML ReturnPolicyIdList - */ - public MutableIndividualDecisionRequest(boolean returnPolicyIdList) - { - // these maps/lists may be updated later by put(...) method defined in this class - namedAttributes = new HashMap<>(); - extraContentsByCategory = new HashMap<>(); - attributesToIncludeInResult = new ArrayList<>(); - returnApplicablePolicyIdList = returnPolicyIdList; - } - - /** - * Create new instance as a clone of an existing request. - * - * @param baseRequest - * replicated existing request. Further changes to it are not reflected back to this new instance. - */ - public MutableIndividualDecisionRequest(IndividualDecisionRequest baseRequest) - { - // these maps/lists may be updated later by put(...) method defined in this class - final Map> baseNamedAttributes = baseRequest.getNamedAttributes(); - final Map baseExtraContentsByCategory = baseRequest.getExtraContentsByCategory(); - final List baseReturnedAttributes = baseRequest.getReturnedAttributes(); - namedAttributes = baseNamedAttributes == null ? new HashMap>() : new HashMap<>(baseNamedAttributes); - extraContentsByCategory = baseExtraContentsByCategory == null ? new HashMap() : new HashMap<>(baseExtraContentsByCategory); - attributesToIncludeInResult = baseReturnedAttributes == null ? new ArrayList() : new ArrayList<>(baseRequest.getReturnedAttributes()); - returnApplicablePolicyIdList = baseRequest.isApplicablePolicyIdentifiersReturned(); - } - - /** - * Put attributes of a specific category in request. - * - * @param categoryName - * category URI - * @param categorySpecificAttributes - * attributes in category {@code categoryName} - * @throws IllegalArgumentException - * if {@code categoryName} or {@code attributes} is null - */ - public void put(String categoryName, SingleCategoryAttributes categorySpecificAttributes) throws IllegalArgumentException - { - if (categoryName == null) - { - throw new IllegalArgumentException("Undefined attribute category"); - } - - if (categorySpecificAttributes == null) - { - throw new IllegalArgumentException("Undefined attributes"); - } - - /* - * Convert growable (therefore mutable) bag of attribute values to immutable ones. Indeed, we must guarantee that attribute values remain constant - * during the evaluation of the request, as mandated by the XACML spec, section 7.3.5:

- * "Regardless of any dynamic modifications of the request context during policy evaluation, the PDP SHALL behave as if each bag of attribute values is fully populated in the context before it is first tested, and is thereafter immutable during evaluation. (That is, every subsequent test of that attribute shall use the same bag of values that was initially tested.)" - *

- */ - for (final Entry> attrEntry : categorySpecificAttributes) - { - namedAttributes.put(attrEntry.getKey(), attrEntry.getValue()); - } - - extraContentsByCategory.put(categoryName, categorySpecificAttributes.getExtraContent()); - final Attributes catSpecificAttrsToIncludeInResult = categorySpecificAttributes.getAttributesToIncludeInResult(); - if (catSpecificAttrsToIncludeInResult != null) - { - attributesToIncludeInResult.add(catSpecificAttrsToIncludeInResult); - } - - } - - /* - * (non-Javadoc) - * - * @see org.ow2.authzforce.core.IndividualDecisionRequest#getNamedAttributes() - */ - @Override - public Map> getNamedAttributes() - { - return namedAttributes; - } - - /* - * (non-Javadoc) - * - * @see org.ow2.authzforce.core.IndividualDecisionRequest#getAttributesIncludedInResult() - */ - @Override - public List getReturnedAttributes() - { - return this.attributesToIncludeInResult; - } - - /* - * (non-Javadoc) - * - * @see org.ow2.authzforce.core.IndividualDecisionRequest#getExtraContentsByCategory() - */ - @Override - public Map getExtraContentsByCategory() - { - return this.extraContentsByCategory; - } - - /** - * @return the returnApplicablePolicyIdList - */ - @Override - public boolean isApplicablePolicyIdentifiersReturned() - { - return returnApplicablePolicyIdList; - } - -} +/** + * Copyright (C) 2011-2015 Thales Services SAS. + * + * This file is part of AuthZForce. + * + * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * AuthZForce 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . + */ +package org.ow2.authzforce.core.pdp.impl; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import net.sf.saxon.s9api.XdmNode; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.Attributes; + +import org.ow2.authzforce.core.pdp.api.AttributeGUID; +import org.ow2.authzforce.core.pdp.api.Bag; +import org.ow2.authzforce.core.pdp.api.IndividualDecisionRequest; +import org.ow2.authzforce.core.pdp.api.SingleCategoryAttributes; + +/** + * Mutable Individual Decision Request + * + * @author cdangerv + * @version $Id: $ + */ +public class MutableIndividualDecisionRequest implements IndividualDecisionRequest +{ + private final Map> namedAttributes; + private final Map extraContentsByCategory; + private final List attributesToIncludeInResult; + private final boolean returnApplicablePolicyIdList; + + /** + * Creates empty request (no attribute) + * + * @param returnPolicyIdList + * equivalent of XACML ReturnPolicyIdList + */ + public MutableIndividualDecisionRequest(boolean returnPolicyIdList) + { + // these maps/lists may be updated later by put(...) method defined in this class + namedAttributes = new HashMap<>(); + extraContentsByCategory = new HashMap<>(); + attributesToIncludeInResult = new ArrayList<>(); + returnApplicablePolicyIdList = returnPolicyIdList; + } + + /** + * Create new instance as a clone of an existing request. + * + * @param baseRequest + * replicated existing request. Further changes to it are not reflected back to this new instance. + */ + public MutableIndividualDecisionRequest(IndividualDecisionRequest baseRequest) + { + // these maps/lists may be updated later by put(...) method defined in this class + final Map> baseNamedAttributes = baseRequest.getNamedAttributes(); + final Map baseExtraContentsByCategory = baseRequest.getExtraContentsByCategory(); + final List baseReturnedAttributes = baseRequest.getReturnedAttributes(); + namedAttributes = baseNamedAttributes == null ? new HashMap>() : new HashMap<>(baseNamedAttributes); + extraContentsByCategory = baseExtraContentsByCategory == null ? new HashMap() : new HashMap<>(baseExtraContentsByCategory); + attributesToIncludeInResult = baseReturnedAttributes == null ? new ArrayList() : new ArrayList<>(baseRequest.getReturnedAttributes()); + returnApplicablePolicyIdList = baseRequest.isApplicablePolicyIdentifiersReturned(); + } + + /** + * Put attributes of a specific category in request. + * + * @param categoryName + * category URI + * @param categorySpecificAttributes + * attributes in category {@code categoryName} + * @throws java.lang.IllegalArgumentException + * if {@code categoryName} or {@code attributes} is null + */ + public void put(String categoryName, SingleCategoryAttributes categorySpecificAttributes) throws IllegalArgumentException + { + if (categoryName == null) + { + throw new IllegalArgumentException("Undefined attribute category"); + } + + if (categorySpecificAttributes == null) + { + throw new IllegalArgumentException("Undefined attributes"); + } + + /* + * Convert growable (therefore mutable) bag of attribute values to immutable ones. Indeed, we must guarantee that attribute values remain constant + * during the evaluation of the request, as mandated by the XACML spec, section 7.3.5:

+ * "Regardless of any dynamic modifications of the request context during policy evaluation, the PDP SHALL behave as if each bag of attribute values is fully populated in the context before it is first tested, and is thereafter immutable during evaluation. (That is, every subsequent test of that attribute shall use the same bag of values that was initially tested.)" + *

+ */ + for (final Entry> attrEntry : categorySpecificAttributes) + { + namedAttributes.put(attrEntry.getKey(), attrEntry.getValue()); + } + + extraContentsByCategory.put(categoryName, categorySpecificAttributes.getExtraContent()); + final Attributes catSpecificAttrsToIncludeInResult = categorySpecificAttributes.getAttributesToIncludeInResult(); + if (catSpecificAttrsToIncludeInResult != null) + { + attributesToIncludeInResult.add(catSpecificAttrsToIncludeInResult); + } + + } + + /* + * (non-Javadoc) + * + * @see org.ow2.authzforce.core.IndividualDecisionRequest#getNamedAttributes() + */ + /** {@inheritDoc} */ + @Override + public Map> getNamedAttributes() + { + return namedAttributes; + } + + /* + * (non-Javadoc) + * + * @see org.ow2.authzforce.core.IndividualDecisionRequest#getAttributesIncludedInResult() + */ + /** {@inheritDoc} */ + @Override + public List getReturnedAttributes() + { + return this.attributesToIncludeInResult; + } + + /* + * (non-Javadoc) + * + * @see org.ow2.authzforce.core.IndividualDecisionRequest#getExtraContentsByCategory() + */ + /** {@inheritDoc} */ + @Override + public Map getExtraContentsByCategory() + { + return this.extraContentsByCategory; + } + + /** {@inheritDoc} */ + @Override + public boolean isApplicablePolicyIdentifiersReturned() + { + return returnApplicablePolicyIdList; + } + +} diff --git a/src/main/java/org/ow2/authzforce/core/pdp/impl/PDPImpl.java b/src/main/java/org/ow2/authzforce/core/pdp/impl/PDPImpl.java index c520ab62..947584f6 100644 --- a/src/main/java/org/ow2/authzforce/core/pdp/impl/PDPImpl.java +++ b/src/main/java/org/ow2/authzforce/core/pdp/impl/PDPImpl.java @@ -1,391 +1,395 @@ -/** - * Copyright (C) 2012-2015 Thales Services SAS. - * - * This file is part of AuthZForce CE. - * - * AuthZForce CE is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software - * Foundation, either version 3 of the License, or (at your option) any later version. - * - * AuthZForce CE 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with AuthZForce CE. If not, see . - */ -package org.ow2.authzforce.core.pdp.impl; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.GregorianCalendar; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -import javax.xml.datatype.XMLGregorianCalendar; - -import org.ow2.authzforce.core.pdp.api.AttributeGUID; -import org.ow2.authzforce.core.pdp.api.Bag; -import org.ow2.authzforce.core.pdp.api.Bags; -import org.ow2.authzforce.core.pdp.api.CloseablePDP; -import org.ow2.authzforce.core.pdp.api.CombiningAlgRegistry; -import org.ow2.authzforce.core.pdp.api.DatatypeFactoryRegistry; -import org.ow2.authzforce.core.pdp.api.DecisionCache; -import org.ow2.authzforce.core.pdp.api.DecisionResultFilter; -import org.ow2.authzforce.core.pdp.api.EnvironmentProperties; -import org.ow2.authzforce.core.pdp.api.IndeterminateEvaluationException; -import org.ow2.authzforce.core.pdp.api.IndividualDecisionRequest; -import org.ow2.authzforce.core.pdp.api.RequestFilter; -import org.ow2.authzforce.core.pdp.api.StatusHelper; -import org.ow2.authzforce.core.pdp.api.XMLUtils; -import org.ow2.authzforce.core.pdp.impl.func.FunctionRegistry; -import org.ow2.authzforce.core.pdp.impl.policy.RootPolicyEvaluator; -import org.ow2.authzforce.core.pdp.impl.policy.StaticApplicablePolicyView; -import org.ow2.authzforce.core.pdp.impl.value.DatatypeConstants; -import org.ow2.authzforce.core.pdp.impl.value.DateTimeValue; -import org.ow2.authzforce.core.pdp.impl.value.DateValue; -import org.ow2.authzforce.core.pdp.impl.value.TimeValue; -import org.ow2.authzforce.xacml.identifiers.XACMLAttributeId; -import org.ow2.authzforce.xacml.identifiers.XACMLCategory; -import org.ow2.authzforce.xmlns.pdp.ext.AbstractAttributeProvider; -import org.ow2.authzforce.xmlns.pdp.ext.AbstractDecisionCache; -import org.ow2.authzforce.xmlns.pdp.ext.AbstractPolicyProvider; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import oasis.names.tc.xacml._3_0.core.schema.wd_17.DecisionType; -import oasis.names.tc.xacml._3_0.core.schema.wd_17.Request; -import oasis.names.tc.xacml._3_0.core.schema.wd_17.Response; -import oasis.names.tc.xacml._3_0.core.schema.wd_17.Result; - -/** - * This is the core XACML PDP engine implementation. To build an XACML policy engine, you start by instantiating this object directly or in a easier and - * preferred way, using {@link PdpConfigurationParser}. - * - */ -public class PDPImpl implements CloseablePDP -{ - private static class NonCachingIndividualDecisionRequestEvaluator extends IndividualDecisionRequestEvaluator - { - private NonCachingIndividualDecisionRequestEvaluator(RootPolicyEvaluator rootPolicyEvaluator) - { - super(rootPolicyEvaluator); - } - - @Override - protected List evaluate(List individualDecisionRequests, Map> pdpIssuedAttributes) - { - final List results = new ArrayList<>(individualDecisionRequests.size()); - for (final IndividualDecisionRequest individuaDecisionRequest : individualDecisionRequests) - { - if (individuaDecisionRequest == null) - { - throw new RuntimeException("One of the individual decision requests returned by the request filter is invalid (null)."); - } - - final Result result = evaluate(individuaDecisionRequest, pdpIssuedAttributes); - results.add(result); - } - - return results; - } - - } - - private static class CachingIndividualRequestEvaluator extends IndividualDecisionRequestEvaluator - { - // the logger we'll use for all messages - private static final Logger _LOGGER = LoggerFactory.getLogger(CachingIndividualRequestEvaluator.class); - - private static final Result INVALID_DECISION_CACHE_RESULT = new Result(DecisionType.INDETERMINATE, new StatusHelper(StatusHelper.STATUS_PROCESSING_ERROR, "Internal error"), null, null, null, null); - - private final DecisionCache decisionCache; - - private CachingIndividualRequestEvaluator(RootPolicyEvaluator rootPolicyEvaluator, DecisionCache decisionCache) - { - super(rootPolicyEvaluator); - assert decisionCache != null; - this.decisionCache = decisionCache; - } - - @Override - public final List evaluate(List individualDecisionRequests, Map> pdpIssuedAttributes) - { - final Map cachedResultsByRequest = decisionCache.getAll(individualDecisionRequests); - if (cachedResultsByRequest == null) - { - // error, return indeterminate result as only result - _LOGGER.error("Invalid decision cache result: null"); - return Collections.singletonList(INVALID_DECISION_CACHE_RESULT); - } - - // At least check that we have as many results from cache as input - // requests - // (For each request with no result in cache, there must still be an - // entry with value - // null.) - if (cachedResultsByRequest.size() != individualDecisionRequests.size()) - { - // error, return indeterminate result as only result - _LOGGER.error("Invalid decision cache result: number of returned decision results ({}) != number of input (individual) decision requests ({})", cachedResultsByRequest.size(), individualDecisionRequests.size()); - return Collections.singletonList(INVALID_DECISION_CACHE_RESULT); - } - - final Set> cachedRequestResultEntries = cachedResultsByRequest.entrySet(); - final List results = new ArrayList<>(cachedRequestResultEntries.size()); - final Map newResultsByRequest = new HashMap<>(); - for (final Entry cachedRequestResultPair : cachedRequestResultEntries) - { - final Result finalResult; - final Result cachedResult = cachedRequestResultPair.getValue(); - if (cachedResult == null) - { - // result not in cache -> evaluate request - final IndividualDecisionRequest individuaDecisionRequest = cachedRequestResultPair.getKey(); - if (individuaDecisionRequest == null) - { - throw new RuntimeException("One of the entry keys (individual decision request) returned by the decision cache implementation '" + decisionCache + "' is invalid (null)."); - } - - finalResult = super.evaluate(individuaDecisionRequest, pdpIssuedAttributes); - newResultsByRequest.put(individuaDecisionRequest, finalResult); - } else - { - finalResult = cachedResult; - } - - results.add(finalResult); - } - - decisionCache.putAll(newResultsByRequest); - return results; - } - } - - private static final IllegalArgumentException ILLEGAL_ARGUMENT_EXCEPTION = new IllegalArgumentException("No input Individual Decision Request"); - - // the logger we'll use for all messages - private static final Logger LOGGER = LoggerFactory.getLogger(PDPImpl.class); - - /** - * Indeterminate response iff CombinedDecision element not supported because the request parser does not support any scheme from MultipleDecisionProfile - * section 2. - */ - private static final Response UNSUPPORTED_COMBINED_DECISION_RESPONSE = new Response(Collections. singletonList(new Result(DecisionType.INDETERMINATE, new StatusHelper(StatusHelper.STATUS_SYNTAX_ERROR, "Unsupported feature: CombinedDecision='true'"), null, null, null, null))); - - private static final AttributeGUID ENVIRONMENT_CURRENT_TIME_ATTRIBUTE_GUID = new AttributeGUID(XACMLCategory.XACML_3_0_ENVIRONMENT_CATEGORY_ENVIRONMENT.value(), null, XACMLAttributeId.XACML_1_0_ENVIRONMENT_CURRENT_TIME.value()); - - private static final AttributeGUID ENVIRONMENT_CURRENT_DATE_ATTRIBUTE_GUID = new AttributeGUID(XACMLCategory.XACML_3_0_ENVIRONMENT_CATEGORY_ENVIRONMENT.value(), null, XACMLAttributeId.XACML_1_0_ENVIRONMENT_CURRENT_DATE.value()); - - private static final AttributeGUID ENVIRONMENT_CURRENT_DATETIME_ATTRIBUTE_GUID = new AttributeGUID(XACMLCategory.XACML_3_0_ENVIRONMENT_CATEGORY_ENVIRONMENT.value(), null, XACMLAttributeId.XACML_1_0_ENVIRONMENT_CURRENT_DATETIME.value()); - - private static final DecisionResultFilter DEFAULT_RESULT_FILTER = new DecisionResultFilter() - { - private static final String ID = "urn:ow2:authzforce:xacml:result-filter:default"; - - @Override - public String getId() - { - return ID; - } - - @Override - public List filter(List results) - { - return results; - } - - @Override - public boolean supportsMultipleDecisionCombining() - { - return false; - } - - }; - - private final RootPolicyEvaluator rootPolicyEvaluator; - private final DecisionCache decisionCache; - private final RequestFilter reqFilter; - private final IndividualDecisionRequestEvaluator individualReqEvaluator; - private final DecisionResultFilter resultFilter; - - /** - * Constructs a new PDP object with the given configuration information. - * - * @param attributeFactory - * attribute value factory - mandatory - * @param functionRegistry - * function registry - mandatory - * @param jaxbAttributeProviderConfs - * XML/JAXB configurations of Attribute Providers for AttributeDesignator/AttributeSelector evaluation; may be null for static expression - * evaluation (out of context), in which case AttributeSelectors/AttributeDesignators are not supported - * @param maxVariableReferenceDepth - * max depth of VariableReference chaining: VariableDefinition -> VariableDefinition ->... ('->' represents a VariableReference); strictly - * negative value means no limit - * @param enableXPath - * allow XPath evaluation, i.e. AttributeSelectors and xpathExpressions (experimental, not for production, use with caution) - * @param requestFilterId - * ID of request filter (XACML Request processing prior to policy evaluation) - mandatory - * @param decisionResultFilter - * decision result filter (XACML Result processing after policy evaluation, before creating/returning final XACML Response) - * @param jaxbDecisionCacheConf - * decision response cache XML/JAXB configuration - * @param jaxbRootPolicyProviderConf - * root policy Provider's XML/JAXB configuration - mandatory - * @param combiningAlgRegistry - * XACML policy/rule combining algorithm registry - mandatory - * @param jaxbRefPolicyProviderConf - * policy-by-reference Provider's XML/JAXB configuration, for resolving policies referred to by Policy(Set)IdReference in policies found by root - * policy Provider - * @param maxPolicySetRefDepth - * max allowed PolicySetIdReference chain: PolicySet1 (PolicySetIdRef1) -> PolicySet2 (PolicySetIdRef2) -> ...; a strictly negative value means - * no limit - * @param strictAttributeIssuerMatch - * true iff strict Attribute Issuer matching is enabled, i.e. AttributeDesignators without Issuer only match request Attributes without Issuer - * (and same AttributeId, Category...). This mode is not fully compliant with XACML 3.0, §5.29, in the case that the Issuer is indeed not present - * on a AttributeDesignator; but it performs better and is recommended when all AttributeDesignators have an Issuer (best practice). Reminder: - * the XACML 3.0 specification for AttributeDesignator evaluation (5.29) says: "If the Issuer is not present in the attribute designator, then - * the matching of the attribute to the named attribute SHALL be governed by AttributeId and DataType attributes alone." if one of the mandatory - * arguments is null - * @param environmentProperties - * PDP configuration environment properties - * @throws IllegalArgumentException - * if there is not any extension found for type {@link org.ow2.authzforce.core.pdp.api.RequestFilter.Factory} with ID {@code requestFilterId}; - * or if one of the mandatory arguments is null; or if any Attribute Provider module created from {@code jaxbAttributeProviderConfs} does not - * provide any attribute; or it is in conflict with another one already registered to provide the same or part of the same attributes; of if - * there is no extension supporting {@code jaxbDecisionCacheConf} - * - * @throws IOException - * error closing the root policy Provider when static resolution is to be used; or error closing the attribute Provider modules created from - * {@code jaxbAttributeProviderConfs}, when and before an {@link IllegalArgumentException} is raised - * - */ - public PDPImpl(DatatypeFactoryRegistry attributeFactory, FunctionRegistry functionRegistry, List jaxbAttributeProviderConfs, int maxVariableReferenceDepth, boolean enableXPath, CombiningAlgRegistry combiningAlgRegistry, AbstractPolicyProvider jaxbRootPolicyProviderConf, AbstractPolicyProvider jaxbRefPolicyProviderConf, int maxPolicySetRefDepth, String requestFilterId, boolean strictAttributeIssuerMatch, DecisionResultFilter decisionResultFilter, AbstractDecisionCache jaxbDecisionCacheConf, EnvironmentProperties environmentProperties) throws IllegalArgumentException, IOException - { - final RequestFilter.Factory requestFilterFactory = requestFilterId == null ? DefaultRequestFilter.LaxFilterFactory.INSTANCE : PdpExtensionLoader.getExtension(RequestFilter.Factory.class, requestFilterId); - - final RequestFilter requestFilter = requestFilterFactory.getInstance(attributeFactory, strictAttributeIssuerMatch, enableXPath, XMLUtils.SAXON_PROCESSOR); - - final RootPolicyEvaluator.Base candidateRootPolicyEvaluator = new RootPolicyEvaluator.Base(attributeFactory, functionRegistry, jaxbAttributeProviderConfs, maxVariableReferenceDepth, enableXPath, combiningAlgRegistry, jaxbRootPolicyProviderConf, jaxbRefPolicyProviderConf, maxPolicySetRefDepth, strictAttributeIssuerMatch, environmentProperties); - // Use static resolution if possible - final RootPolicyEvaluator staticRootPolicyEvaluator = candidateRootPolicyEvaluator.toStatic(); - if (staticRootPolicyEvaluator == null) - { - this.rootPolicyEvaluator = candidateRootPolicyEvaluator; - } else - { - this.rootPolicyEvaluator = staticRootPolicyEvaluator; - } - - this.reqFilter = requestFilter; - - // decision cache - if (jaxbDecisionCacheConf == null) - { - this.decisionCache = null; - } else - { - final DecisionCache.Factory responseCacheStoreFactory = PdpExtensionLoader.getJaxbBoundExtension(DecisionCache.Factory.class, jaxbDecisionCacheConf.getClass()); - this.decisionCache = responseCacheStoreFactory.getInstance(jaxbDecisionCacheConf); - } - - this.individualReqEvaluator = this.decisionCache == null ? new NonCachingIndividualDecisionRequestEvaluator(rootPolicyEvaluator) : new CachingIndividualRequestEvaluator(rootPolicyEvaluator, this.decisionCache); - this.resultFilter = decisionResultFilter == null ? DEFAULT_RESULT_FILTER : decisionResultFilter; - } - - @Override - public List evaluate(List individualDecisionRequests) - { - if (individualDecisionRequests == null) - { - throw ILLEGAL_ARGUMENT_EXCEPTION; - } - - /* - * Every request context (named attributes) is completed with common current date/time attribute (same values) set/"issued" locally (here by the PDP - * engine) according to XACML core spec: - * "This identifier indicates the current time at the context handler. In practice it is the time at which the request context was created." (� B.7). - */ - final Map> pdpIssuedAttributes = new HashMap<>(); - // current datetime - final DateTimeValue currentDateTimeValue = new DateTimeValue(new GregorianCalendar()); - pdpIssuedAttributes.put(ENVIRONMENT_CURRENT_DATETIME_ATTRIBUTE_GUID, Bags.singleton(DatatypeConstants.DATETIME.TYPE, currentDateTimeValue)); - // current date - pdpIssuedAttributes.put(ENVIRONMENT_CURRENT_DATE_ATTRIBUTE_GUID, Bags.singleton(DatatypeConstants.DATE.TYPE, DateValue.getInstance((XMLGregorianCalendar) currentDateTimeValue.getUnderlyingValue().clone()))); - // current time - pdpIssuedAttributes.put(ENVIRONMENT_CURRENT_TIME_ATTRIBUTE_GUID, Bags.singleton(DatatypeConstants.TIME.TYPE, TimeValue.getInstance((XMLGregorianCalendar) currentDateTimeValue.getUnderlyingValue().clone()))); - - // evaluate the individual decision requests with the extra common - // attributes set previously - final List results = individualReqEvaluator.evaluate(individualDecisionRequests, pdpIssuedAttributes); - return resultFilter.filter(results); - } - - @Override - public Response evaluate(Request request, Map namespaceURIsByPrefix) - { - if (request == null) - { - throw ILLEGAL_ARGUMENT_EXCEPTION; - } - - /* - * No support for CombinedDecision = true if no decisionCombiner defined. (The use of the CombinedDecision attribute is specified in Multiple Decision - * Profile.) - */ - if (request.isCombinedDecision() && !resultFilter.supportsMultipleDecisionCombining()) - { - /* - * According to XACML core spec, 5.42, "If the PDP does not implement the relevant functionality in [Multiple Decision Profile], then the PDP must - * return an Indeterminate with a status code of urn:oasis:names:tc:xacml:1.0:status:processing-error if it receives a request with this attribute - * set to �true�. - */ - return UNSUPPORTED_COMBINED_DECISION_RESPONSE; - } - - /* - * The request parser may return multiple individual decision requests from a single Request, e.g. if the request parser implements the Multiple - * Decision profile or Hierarchical Resource profile - */ - final List individualDecisionRequests; - try - { - individualDecisionRequests = reqFilter.filter(request, namespaceURIsByPrefix); - } catch (IndeterminateEvaluationException e) - { - LOGGER.info("Invalid or unsupported input XACML Request syntax", e); - return new Response(Collections. singletonList(new Result(DecisionType.INDETERMINATE, e.getStatus(), null, null, null, null))); - } - - final List results = evaluate(individualDecisionRequests); - return new Response(results); - } - - @Override - public void close() throws IOException - { - rootPolicyEvaluator.close(); - if (decisionCache != null) - { - decisionCache.close(); - } - } - - @Override - public Response evaluate(Request request) - { - return evaluate(request, null); - } - - /** - * Get the PDP's root policy and policies referenced - directly or indirectly - from the root policy, if all are statically resolved - * - * @return the root and referenced policies; null if any of these policies is not statically resolved (once and for all) - */ - public StaticApplicablePolicyView getStaticApplicablePolicies() - { - return this.rootPolicyEvaluator.getStaticApplicablePolicies(); - } - -} +/** + * Copyright (C) 2012-2015 Thales Services SAS. + * + * This file is part of AuthZForce CE. + * + * AuthZForce CE is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * AuthZForce CE 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with AuthZForce CE. If not, see . + */ +package org.ow2.authzforce.core.pdp.impl; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.GregorianCalendar; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import javax.xml.datatype.XMLGregorianCalendar; + +import org.ow2.authzforce.core.pdp.api.AttributeGUID; +import org.ow2.authzforce.core.pdp.api.Bag; +import org.ow2.authzforce.core.pdp.api.Bags; +import org.ow2.authzforce.core.pdp.api.CloseablePDP; +import org.ow2.authzforce.core.pdp.api.CombiningAlgRegistry; +import org.ow2.authzforce.core.pdp.api.DatatypeFactoryRegistry; +import org.ow2.authzforce.core.pdp.api.DecisionCache; +import org.ow2.authzforce.core.pdp.api.DecisionResultFilter; +import org.ow2.authzforce.core.pdp.api.EnvironmentProperties; +import org.ow2.authzforce.core.pdp.api.IndeterminateEvaluationException; +import org.ow2.authzforce.core.pdp.api.IndividualDecisionRequest; +import org.ow2.authzforce.core.pdp.api.RequestFilter; +import org.ow2.authzforce.core.pdp.api.StatusHelper; +import org.ow2.authzforce.core.pdp.api.XMLUtils; +import org.ow2.authzforce.core.pdp.impl.func.FunctionRegistry; +import org.ow2.authzforce.core.pdp.impl.policy.RootPolicyEvaluator; +import org.ow2.authzforce.core.pdp.impl.policy.StaticApplicablePolicyView; +import org.ow2.authzforce.core.pdp.impl.value.DatatypeConstants; +import org.ow2.authzforce.core.pdp.impl.value.DateTimeValue; +import org.ow2.authzforce.core.pdp.impl.value.DateValue; +import org.ow2.authzforce.core.pdp.impl.value.TimeValue; +import org.ow2.authzforce.xacml.identifiers.XACMLAttributeId; +import org.ow2.authzforce.xacml.identifiers.XACMLCategory; +import org.ow2.authzforce.xmlns.pdp.ext.AbstractAttributeProvider; +import org.ow2.authzforce.xmlns.pdp.ext.AbstractDecisionCache; +import org.ow2.authzforce.xmlns.pdp.ext.AbstractPolicyProvider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import oasis.names.tc.xacml._3_0.core.schema.wd_17.DecisionType; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.Request; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.Response; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.Result; + +/** + * This is the core XACML PDP engine implementation. To build an XACML policy engine, you start by instantiating this object directly or in a easier and + * preferred way, using {@link PdpConfigurationParser}. + * + * @author cdangerv + * @version $Id: $ + */ +public class PDPImpl implements CloseablePDP +{ + private static class NonCachingIndividualDecisionRequestEvaluator extends IndividualDecisionRequestEvaluator + { + private NonCachingIndividualDecisionRequestEvaluator(RootPolicyEvaluator rootPolicyEvaluator) + { + super(rootPolicyEvaluator); + } + + @Override + protected List evaluate(List individualDecisionRequests, Map> pdpIssuedAttributes) + { + final List results = new ArrayList<>(individualDecisionRequests.size()); + for (final IndividualDecisionRequest individuaDecisionRequest : individualDecisionRequests) + { + if (individuaDecisionRequest == null) + { + throw new RuntimeException("One of the individual decision requests returned by the request filter is invalid (null)."); + } + + final Result result = evaluate(individuaDecisionRequest, pdpIssuedAttributes); + results.add(result); + } + + return results; + } + + } + + private static class CachingIndividualRequestEvaluator extends IndividualDecisionRequestEvaluator + { + // the logger we'll use for all messages + private static final Logger _LOGGER = LoggerFactory.getLogger(CachingIndividualRequestEvaluator.class); + + private static final Result INVALID_DECISION_CACHE_RESULT = new Result(DecisionType.INDETERMINATE, new StatusHelper(StatusHelper.STATUS_PROCESSING_ERROR, "Internal error"), null, null, null, null); + + private final DecisionCache decisionCache; + + private CachingIndividualRequestEvaluator(RootPolicyEvaluator rootPolicyEvaluator, DecisionCache decisionCache) + { + super(rootPolicyEvaluator); + assert decisionCache != null; + this.decisionCache = decisionCache; + } + + @Override + public final List evaluate(List individualDecisionRequests, Map> pdpIssuedAttributes) + { + final Map cachedResultsByRequest = decisionCache.getAll(individualDecisionRequests); + if (cachedResultsByRequest == null) + { + // error, return indeterminate result as only result + _LOGGER.error("Invalid decision cache result: null"); + return Collections.singletonList(INVALID_DECISION_CACHE_RESULT); + } + + // At least check that we have as many results from cache as input + // requests + // (For each request with no result in cache, there must still be an + // entry with value + // null.) + if (cachedResultsByRequest.size() != individualDecisionRequests.size()) + { + // error, return indeterminate result as only result + _LOGGER.error("Invalid decision cache result: number of returned decision results ({}) != number of input (individual) decision requests ({})", cachedResultsByRequest.size(), individualDecisionRequests.size()); + return Collections.singletonList(INVALID_DECISION_CACHE_RESULT); + } + + final Set> cachedRequestResultEntries = cachedResultsByRequest.entrySet(); + final List results = new ArrayList<>(cachedRequestResultEntries.size()); + final Map newResultsByRequest = new HashMap<>(); + for (final Entry cachedRequestResultPair : cachedRequestResultEntries) + { + final Result finalResult; + final Result cachedResult = cachedRequestResultPair.getValue(); + if (cachedResult == null) + { + // result not in cache -> evaluate request + final IndividualDecisionRequest individuaDecisionRequest = cachedRequestResultPair.getKey(); + if (individuaDecisionRequest == null) + { + throw new RuntimeException("One of the entry keys (individual decision request) returned by the decision cache implementation '" + decisionCache + "' is invalid (null)."); + } + + finalResult = super.evaluate(individuaDecisionRequest, pdpIssuedAttributes); + newResultsByRequest.put(individuaDecisionRequest, finalResult); + } else + { + finalResult = cachedResult; + } + + results.add(finalResult); + } + + decisionCache.putAll(newResultsByRequest); + return results; + } + } + + private static final IllegalArgumentException ILLEGAL_ARGUMENT_EXCEPTION = new IllegalArgumentException("No input Individual Decision Request"); + + // the logger we'll use for all messages + private static final Logger LOGGER = LoggerFactory.getLogger(PDPImpl.class); + + /** + * Indeterminate response iff CombinedDecision element not supported because the request parser does not support any scheme from MultipleDecisionProfile + * section 2. + */ + private static final Response UNSUPPORTED_COMBINED_DECISION_RESPONSE = new Response(Collections. singletonList(new Result(DecisionType.INDETERMINATE, new StatusHelper(StatusHelper.STATUS_SYNTAX_ERROR, "Unsupported feature: CombinedDecision='true'"), null, null, null, null))); + + private static final AttributeGUID ENVIRONMENT_CURRENT_TIME_ATTRIBUTE_GUID = new AttributeGUID(XACMLCategory.XACML_3_0_ENVIRONMENT_CATEGORY_ENVIRONMENT.value(), null, XACMLAttributeId.XACML_1_0_ENVIRONMENT_CURRENT_TIME.value()); + + private static final AttributeGUID ENVIRONMENT_CURRENT_DATE_ATTRIBUTE_GUID = new AttributeGUID(XACMLCategory.XACML_3_0_ENVIRONMENT_CATEGORY_ENVIRONMENT.value(), null, XACMLAttributeId.XACML_1_0_ENVIRONMENT_CURRENT_DATE.value()); + + private static final AttributeGUID ENVIRONMENT_CURRENT_DATETIME_ATTRIBUTE_GUID = new AttributeGUID(XACMLCategory.XACML_3_0_ENVIRONMENT_CATEGORY_ENVIRONMENT.value(), null, XACMLAttributeId.XACML_1_0_ENVIRONMENT_CURRENT_DATETIME.value()); + + private static final DecisionResultFilter DEFAULT_RESULT_FILTER = new DecisionResultFilter() + { + private static final String ID = "urn:ow2:authzforce:xacml:result-filter:default"; + + @Override + public String getId() + { + return ID; + } + + @Override + public List filter(List results) + { + return results; + } + + @Override + public boolean supportsMultipleDecisionCombining() + { + return false; + } + + }; + + private final RootPolicyEvaluator rootPolicyEvaluator; + private final DecisionCache decisionCache; + private final RequestFilter reqFilter; + private final IndividualDecisionRequestEvaluator individualReqEvaluator; + private final DecisionResultFilter resultFilter; + + /** + * Constructs a new PDP object with the given configuration information. + * + * @param attributeFactory + * attribute value factory - mandatory + * @param functionRegistry + * function registry - mandatory + * @param jaxbAttributeProviderConfs + * XML/JAXB configurations of Attribute Providers for AttributeDesignator/AttributeSelector evaluation; may be null for static expression + * evaluation (out of context), in which case AttributeSelectors/AttributeDesignators are not supported + * @param maxVariableReferenceDepth + * max depth of VariableReference chaining: VariableDefinition -> VariableDefinition ->... ('->' represents a VariableReference); strictly + * negative value means no limit + * @param enableXPath + * allow XPath evaluation, i.e. AttributeSelectors and xpathExpressions (experimental, not for production, use with caution) + * @param requestFilterId + * ID of request filter (XACML Request processing prior to policy evaluation) - mandatory + * @param decisionResultFilter + * decision result filter (XACML Result processing after policy evaluation, before creating/returning final XACML Response) + * @param jaxbDecisionCacheConf + * decision response cache XML/JAXB configuration + * @param jaxbRootPolicyProviderConf + * root policy Provider's XML/JAXB configuration - mandatory + * @param combiningAlgRegistry + * XACML policy/rule combining algorithm registry - mandatory + * @param jaxbRefPolicyProviderConf + * policy-by-reference Provider's XML/JAXB configuration, for resolving policies referred to by Policy(Set)IdReference in policies found by root + * policy Provider + * @param maxPolicySetRefDepth + * max allowed PolicySetIdReference chain: PolicySet1 (PolicySetIdRef1) -> PolicySet2 (PolicySetIdRef2) -> ...; a strictly negative value means + * no limit + * @param strictAttributeIssuerMatch + * true iff strict Attribute Issuer matching is enabled, i.e. AttributeDesignators without Issuer only match request Attributes without Issuer + * (and same AttributeId, Category...). This mode is not fully compliant with XACML 3.0, §5.29, in the case that the Issuer is indeed not present + * on a AttributeDesignator; but it performs better and is recommended when all AttributeDesignators have an Issuer (best practice). Reminder: + * the XACML 3.0 specification for AttributeDesignator evaluation (5.29) says: "If the Issuer is not present in the attribute designator, then + * the matching of the attribute to the named attribute SHALL be governed by AttributeId and DataType attributes alone." if one of the mandatory + * arguments is null + * @param environmentProperties + * PDP configuration environment properties + * @throws java.lang.IllegalArgumentException + * if there is not any extension found for type {@link org.ow2.authzforce.core.pdp.api.RequestFilter.Factory} with ID {@code requestFilterId}; + * or if one of the mandatory arguments is null; or if any Attribute Provider module created from {@code jaxbAttributeProviderConfs} does not + * provide any attribute; or it is in conflict with another one already registered to provide the same or part of the same attributes; of if + * there is no extension supporting {@code jaxbDecisionCacheConf} + * @throws java.io.IOException + * error closing the root policy Provider when static resolution is to be used; or error closing the attribute Provider modules created from + * {@code jaxbAttributeProviderConfs}, when and before an {@link IllegalArgumentException} is raised + */ + public PDPImpl(DatatypeFactoryRegistry attributeFactory, FunctionRegistry functionRegistry, List jaxbAttributeProviderConfs, int maxVariableReferenceDepth, boolean enableXPath, CombiningAlgRegistry combiningAlgRegistry, AbstractPolicyProvider jaxbRootPolicyProviderConf, AbstractPolicyProvider jaxbRefPolicyProviderConf, int maxPolicySetRefDepth, String requestFilterId, boolean strictAttributeIssuerMatch, DecisionResultFilter decisionResultFilter, AbstractDecisionCache jaxbDecisionCacheConf, EnvironmentProperties environmentProperties) throws IllegalArgumentException, IOException + { + final RequestFilter.Factory requestFilterFactory = requestFilterId == null ? DefaultRequestFilter.LaxFilterFactory.INSTANCE : PdpExtensionLoader.getExtension(RequestFilter.Factory.class, requestFilterId); + + final RequestFilter requestFilter = requestFilterFactory.getInstance(attributeFactory, strictAttributeIssuerMatch, enableXPath, XMLUtils.SAXON_PROCESSOR); + + final RootPolicyEvaluator.Base candidateRootPolicyEvaluator = new RootPolicyEvaluator.Base(attributeFactory, functionRegistry, jaxbAttributeProviderConfs, maxVariableReferenceDepth, enableXPath, combiningAlgRegistry, jaxbRootPolicyProviderConf, jaxbRefPolicyProviderConf, maxPolicySetRefDepth, strictAttributeIssuerMatch, environmentProperties); + // Use static resolution if possible + final RootPolicyEvaluator staticRootPolicyEvaluator = candidateRootPolicyEvaluator.toStatic(); + if (staticRootPolicyEvaluator == null) + { + this.rootPolicyEvaluator = candidateRootPolicyEvaluator; + } else + { + this.rootPolicyEvaluator = staticRootPolicyEvaluator; + } + + this.reqFilter = requestFilter; + + // decision cache + if (jaxbDecisionCacheConf == null) + { + this.decisionCache = null; + } else + { + final DecisionCache.Factory responseCacheStoreFactory = PdpExtensionLoader.getJaxbBoundExtension(DecisionCache.Factory.class, jaxbDecisionCacheConf.getClass()); + this.decisionCache = responseCacheStoreFactory.getInstance(jaxbDecisionCacheConf); + } + + this.individualReqEvaluator = this.decisionCache == null ? new NonCachingIndividualDecisionRequestEvaluator(rootPolicyEvaluator) : new CachingIndividualRequestEvaluator(rootPolicyEvaluator, this.decisionCache); + this.resultFilter = decisionResultFilter == null ? DEFAULT_RESULT_FILTER : decisionResultFilter; + } + + /** {@inheritDoc} */ + @Override + public List evaluate(List individualDecisionRequests) + { + if (individualDecisionRequests == null) + { + throw ILLEGAL_ARGUMENT_EXCEPTION; + } + + /* + * Every request context (named attributes) is completed with common current date/time attribute (same values) set/"issued" locally (here by the PDP + * engine) according to XACML core spec: + * "This identifier indicates the current time at the context handler. In practice it is the time at which the request context was created." (� B.7). + */ + final Map> pdpIssuedAttributes = new HashMap<>(); + // current datetime + final DateTimeValue currentDateTimeValue = new DateTimeValue(new GregorianCalendar()); + pdpIssuedAttributes.put(ENVIRONMENT_CURRENT_DATETIME_ATTRIBUTE_GUID, Bags.singleton(DatatypeConstants.DATETIME.TYPE, currentDateTimeValue)); + // current date + pdpIssuedAttributes.put(ENVIRONMENT_CURRENT_DATE_ATTRIBUTE_GUID, Bags.singleton(DatatypeConstants.DATE.TYPE, DateValue.getInstance((XMLGregorianCalendar) currentDateTimeValue.getUnderlyingValue().clone()))); + // current time + pdpIssuedAttributes.put(ENVIRONMENT_CURRENT_TIME_ATTRIBUTE_GUID, Bags.singleton(DatatypeConstants.TIME.TYPE, TimeValue.getInstance((XMLGregorianCalendar) currentDateTimeValue.getUnderlyingValue().clone()))); + + // evaluate the individual decision requests with the extra common + // attributes set previously + final List results = individualReqEvaluator.evaluate(individualDecisionRequests, pdpIssuedAttributes); + return resultFilter.filter(results); + } + + /** {@inheritDoc} */ + @Override + public Response evaluate(Request request, Map namespaceURIsByPrefix) + { + if (request == null) + { + throw ILLEGAL_ARGUMENT_EXCEPTION; + } + + /* + * No support for CombinedDecision = true if no decisionCombiner defined. (The use of the CombinedDecision attribute is specified in Multiple Decision + * Profile.) + */ + if (request.isCombinedDecision() && !resultFilter.supportsMultipleDecisionCombining()) + { + /* + * According to XACML core spec, 5.42, "If the PDP does not implement the relevant functionality in [Multiple Decision Profile], then the PDP must + * return an Indeterminate with a status code of urn:oasis:names:tc:xacml:1.0:status:processing-error if it receives a request with this attribute + * set to �true�. + */ + return UNSUPPORTED_COMBINED_DECISION_RESPONSE; + } + + /* + * The request parser may return multiple individual decision requests from a single Request, e.g. if the request parser implements the Multiple + * Decision profile or Hierarchical Resource profile + */ + final List individualDecisionRequests; + try + { + individualDecisionRequests = reqFilter.filter(request, namespaceURIsByPrefix); + } catch (IndeterminateEvaluationException e) + { + LOGGER.info("Invalid or unsupported input XACML Request syntax", e); + return new Response(Collections. singletonList(new Result(DecisionType.INDETERMINATE, e.getStatus(), null, null, null, null))); + } + + final List results = evaluate(individualDecisionRequests); + return new Response(results); + } + + /** {@inheritDoc} */ + @Override + public void close() throws IOException + { + rootPolicyEvaluator.close(); + if (decisionCache != null) + { + decisionCache.close(); + } + } + + /** {@inheritDoc} */ + @Override + public Response evaluate(Request request) + { + return evaluate(request, null); + } + + /** + * Get the PDP's root policy and policies referenced - directly or indirectly - from the root policy, if all are statically resolved + * + * @return the root and referenced policies; null if any of these policies is not statically resolved (once and for all) + */ + public StaticApplicablePolicyView getStaticApplicablePolicies() + { + return this.rootPolicyEvaluator.getStaticApplicablePolicies(); + } + +} diff --git a/src/main/java/org/ow2/authzforce/core/pdp/impl/PdpBean.java b/src/main/java/org/ow2/authzforce/core/pdp/impl/PdpBean.java index 01cbb83c..3fd95a8d 100644 --- a/src/main/java/org/ow2/authzforce/core/pdp/impl/PdpBean.java +++ b/src/main/java/org/ow2/authzforce/core/pdp/impl/PdpBean.java @@ -1,160 +1,165 @@ -/** - * Copyright (C) 2011-2015 Thales Services SAS. - * - * This file is part of AuthZForce. - * - * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software - * Foundation, either version 3 of the License, or (at your option) any later version. - * - * AuthZForce 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . - */ -package org.ow2.authzforce.core.pdp.impl; - -import java.io.IOException; -import java.util.List; -import java.util.Map; - -import oasis.names.tc.xacml._3_0.core.schema.wd_17.Request; -import oasis.names.tc.xacml._3_0.core.schema.wd_17.Response; -import oasis.names.tc.xacml._3_0.core.schema.wd_17.Result; - -import org.ow2.authzforce.core.pdp.api.IndividualDecisionRequest; -import org.ow2.authzforce.core.pdp.api.PDP; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.util.SystemPropertyUtils; - -/** - * JavaBean for the PDP to be used/called as JNDI resource. - * - * In JEE application servers such as Glassfish, you could use class org.glassfish.resources.custom.factory.JavaBeanFactory for registering the custom JNDI resource. More info: - * http://docs.oracle.com/cd/E26576_01/doc.312/e24930/jndi.htm#giywi - * - * For Tomcat, see http://tomcat.apache.org/tomcat-7.0-doc/jndi-resources-howto.html# Adding_Custom_Resource_Factories. - * - */ -public final class PdpBean implements PDP -{ - private final static Logger LOGGER = LoggerFactory.getLogger(PdpBean.class); - - private PDP pdp; - - private String confLocation = null; - - private boolean initialized = false; - - private String extSchemaLocation = null; - - private String catalogLocation = null; - - /** - * Configuration file. Only the 'defaultPDP' configuration will be loaded, i.e. 'pdp' element with 'name' matching the 'defaultPDP' attribute of the root 'config' element - * - * @param filePath - * configuration file path used as argument to {@link org.springframework.core.io.DefaultResourceLoader#getResource(String)} to resolve the resource; any placeholder ${...} in the path - * will be replaced with the corresponding system property value - * @throws IllegalArgumentException - * if there is an unresolvable placeholder in {@code filePath} - */ - public void setConfigFile(String filePath) throws IllegalArgumentException - { - confLocation = SystemPropertyUtils.resolvePlaceholders(filePath); - init(); - } - - /** - * Configuration schema file. Used only for validating XML configurations (enclosed with 'xml' tag) of PDP extension modules in PDP configuration file set with {@link #setConfigFile(String)} - * - * @param filePath - * configuration file path used as argument to {@link org.springframework.core.io.DefaultResourceLoader#getResource(String)} to resolve the resource; any placeholder ${...} in the path - * will be replaced with the corresponding system property value - * @throws IllegalArgumentException - * if there is an unresolvable placeholder in {@code filePath} - */ - public void setSchemaFile(String filePath) throws IllegalArgumentException - { - extSchemaLocation = SystemPropertyUtils.resolvePlaceholders(filePath); - init(); - } - - /** - * Set XML catalog for resolving XML entities used in XML schema - * - * @param filePath - * configuration file path used as argument to {@link org.springframework.core.io.DefaultResourceLoader#getResource(String)} to resolve the resource; any placeholder ${...} in the path - * will be replaced with the corresponding system property value - * @throws IllegalArgumentException - * if there is an unresolvable placeholder in {@code filePath} - */ - public void setCatalogFile(String filePath) throws IllegalArgumentException - { - catalogLocation = SystemPropertyUtils.resolvePlaceholders(filePath); - init(); - } - - private boolean init() - { - if (!initialized && catalogLocation != null && extSchemaLocation != null && confLocation != null) - { - LOGGER.info("Loading PDP configuration from file {} with extension schema location '{}' and XML catalog location '{}'", new Object[] { confLocation, extSchemaLocation, catalogLocation }); - try - { - pdp = PdpConfigurationParser.getPDP(confLocation, catalogLocation, extSchemaLocation); - } catch (IOException | IllegalArgumentException e) - { - throw new RuntimeException("Error parsing PDP configuration from location: " + confLocation, e); - } - - initialized = true; - } - - return initialized; - } - - @Override - public Response evaluate(Request request) - { - return evaluate(request, null); - } - - private void checkInit() - { - if (!initialized) - { - final String cause; - if (confLocation == null) - { - cause = "Missing parameter: configuration file"; - } else if (extSchemaLocation == null) - { - cause = "Missing parameter: extension schema file"; - } else if (catalogLocation == null) - { - cause = "Missing parameter: XML catalog file"; - } else - { - cause = "Check previous errors."; - } - - throw new RuntimeException("PDP not initialized: " + cause); - } - } - - @Override - public List evaluate(List individualDecisionRequests) - { - checkInit(); - return pdp.evaluate(individualDecisionRequests); - } - - @Override - public Response evaluate(Request request, Map namespaceURIsByPrefix) - { - checkInit(); - return pdp.evaluate(request, namespaceURIsByPrefix); - } - -} +/** + * Copyright (C) 2011-2015 Thales Services SAS. + * + * This file is part of AuthZForce. + * + * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * AuthZForce 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . + */ +package org.ow2.authzforce.core.pdp.impl; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +import oasis.names.tc.xacml._3_0.core.schema.wd_17.Request; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.Response; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.Result; + +import org.ow2.authzforce.core.pdp.api.IndividualDecisionRequest; +import org.ow2.authzforce.core.pdp.api.PDP; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.util.SystemPropertyUtils; + +/** + * JavaBean for the PDP to be used/called as JNDI resource. + * + * In JEE application servers such as Glassfish, you could use class org.glassfish.resources.custom.factory.JavaBeanFactory for registering the custom JNDI resource. More info: + * http://docs.oracle.com/cd/E26576_01/doc.312/e24930/jndi.htm#giywi + * + * For Tomcat, see http://tomcat.apache.org/tomcat-7.0-doc/jndi-resources-howto.html# Adding_Custom_Resource_Factories. + * + * @author cdangerv + * @version $Id: $ + */ +public final class PdpBean implements PDP +{ + private final static Logger LOGGER = LoggerFactory.getLogger(PdpBean.class); + + private PDP pdp; + + private String confLocation = null; + + private boolean initialized = false; + + private String extSchemaLocation = null; + + private String catalogLocation = null; + + /** + * Configuration file. Only the 'defaultPDP' configuration will be loaded, i.e. 'pdp' element with 'name' matching the 'defaultPDP' attribute of the root 'config' element + * + * @param filePath + * configuration file path used as argument to {@link org.springframework.core.io.DefaultResourceLoader#getResource(String)} to resolve the resource; any placeholder ${...} in the path + * will be replaced with the corresponding system property value + * @throws java.lang.IllegalArgumentException + * if there is an unresolvable placeholder in {@code filePath} + */ + public void setConfigFile(String filePath) throws IllegalArgumentException + { + confLocation = SystemPropertyUtils.resolvePlaceholders(filePath); + init(); + } + + /** + * Configuration schema file. Used only for validating XML configurations (enclosed with 'xml' tag) of PDP extension modules in PDP configuration file set with {@link #setConfigFile(String)} + * + * @param filePath + * configuration file path used as argument to {@link org.springframework.core.io.DefaultResourceLoader#getResource(String)} to resolve the resource; any placeholder ${...} in the path + * will be replaced with the corresponding system property value + * @throws java.lang.IllegalArgumentException + * if there is an unresolvable placeholder in {@code filePath} + */ + public void setSchemaFile(String filePath) throws IllegalArgumentException + { + extSchemaLocation = SystemPropertyUtils.resolvePlaceholders(filePath); + init(); + } + + /** + * Set XML catalog for resolving XML entities used in XML schema + * + * @param filePath + * configuration file path used as argument to {@link org.springframework.core.io.DefaultResourceLoader#getResource(String)} to resolve the resource; any placeholder ${...} in the path + * will be replaced with the corresponding system property value + * @throws java.lang.IllegalArgumentException + * if there is an unresolvable placeholder in {@code filePath} + */ + public void setCatalogFile(String filePath) throws IllegalArgumentException + { + catalogLocation = SystemPropertyUtils.resolvePlaceholders(filePath); + init(); + } + + private boolean init() + { + if (!initialized && catalogLocation != null && extSchemaLocation != null && confLocation != null) + { + LOGGER.info("Loading PDP configuration from file {} with extension schema location '{}' and XML catalog location '{}'", new Object[] { confLocation, extSchemaLocation, catalogLocation }); + try + { + pdp = PdpConfigurationParser.getPDP(confLocation, catalogLocation, extSchemaLocation); + } catch (IOException | IllegalArgumentException e) + { + throw new RuntimeException("Error parsing PDP configuration from location: " + confLocation, e); + } + + initialized = true; + } + + return initialized; + } + + /** {@inheritDoc} */ + @Override + public Response evaluate(Request request) + { + return evaluate(request, null); + } + + private void checkInit() + { + if (!initialized) + { + final String cause; + if (confLocation == null) + { + cause = "Missing parameter: configuration file"; + } else if (extSchemaLocation == null) + { + cause = "Missing parameter: extension schema file"; + } else if (catalogLocation == null) + { + cause = "Missing parameter: XML catalog file"; + } else + { + cause = "Check previous errors."; + } + + throw new RuntimeException("PDP not initialized: " + cause); + } + } + + /** {@inheritDoc} */ + @Override + public List evaluate(List individualDecisionRequests) + { + checkInit(); + return pdp.evaluate(individualDecisionRequests); + } + + /** {@inheritDoc} */ + @Override + public Response evaluate(Request request, Map namespaceURIsByPrefix) + { + checkInit(); + return pdp.evaluate(request, namespaceURIsByPrefix); + } + +} diff --git a/src/main/java/org/ow2/authzforce/core/pdp/impl/PdpConfigurationParser.java b/src/main/java/org/ow2/authzforce/core/pdp/impl/PdpConfigurationParser.java index f77a0c2e..d64a3c88 100644 --- a/src/main/java/org/ow2/authzforce/core/pdp/impl/PdpConfigurationParser.java +++ b/src/main/java/org/ow2/authzforce/core/pdp/impl/PdpConfigurationParser.java @@ -1,412 +1,406 @@ -/** - * Copyright (C) 2011-2015 Thales Services SAS. - * - * This file is part of AuthZForce. - * - * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software - * Foundation, either version 3 of the License, or (at your option) any later version. - * - * AuthZForce 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . - */ -package org.ow2.authzforce.core.pdp.impl; - -import java.io.Closeable; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.util.Collections; -import java.util.List; - -import javax.xml.bind.JAXBException; -import javax.xml.transform.stream.StreamSource; - -import org.ow2.authzforce.core.pdp.api.CombiningAlg; -import org.ow2.authzforce.core.pdp.api.CombiningAlgRegistry; -import org.ow2.authzforce.core.pdp.api.Datatype; -import org.ow2.authzforce.core.pdp.api.DatatypeFactory; -import org.ow2.authzforce.core.pdp.api.DatatypeFactoryRegistry; -import org.ow2.authzforce.core.pdp.api.DecisionResultFilter; -import org.ow2.authzforce.core.pdp.api.EnvironmentProperties; -import org.ow2.authzforce.core.pdp.api.EnvironmentPropertyName; -import org.ow2.authzforce.core.pdp.api.FirstOrderFunction; -import org.ow2.authzforce.core.pdp.api.Function; -import org.ow2.authzforce.core.pdp.api.FunctionSet; -import org.ow2.authzforce.core.pdp.impl.combining.BaseCombiningAlgRegistry; -import org.ow2.authzforce.core.pdp.impl.combining.StandardCombiningAlgRegistry; -import org.ow2.authzforce.core.pdp.impl.func.FunctionRegistry; -import org.ow2.authzforce.core.pdp.impl.func.StandardFunctionRegistry; -import org.ow2.authzforce.core.pdp.impl.value.BaseDatatypeFactoryRegistry; -import org.ow2.authzforce.core.pdp.impl.value.StandardDatatypeFactoryRegistry; -import org.ow2.authzforce.core.xmlns.pdp.Pdp; -import org.ow2.authzforce.xacml.identifiers.XACMLDatatypeId; -import org.ow2.authzforce.xmlns.pdp.ext.AbstractDecisionCache; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.util.ResourceUtils; - -/** - * XML-based PDP Configuration parser - * - */ -public class PdpConfigurationParser -{ - private static final IllegalArgumentException NULL_PDP_MODEL_HANDLER_ARGUMENT_EXCEPTION = new IllegalArgumentException("Undefined PDP configuration model handler"); - private final static Logger LOGGER = LoggerFactory.getLogger(PdpConfigurationParser.class); - - /** - * Create PDP instance. - * - * @param confLocation - * location of PDP configuration XML file, compliant with the PDP - * XML schema (pdp.xsd). This location may be any resource string - * supported by Spring ResourceLoader. For example: - * classpath:com/myapp/aaa.xsd, file:///data/bbb.xsd, - * http://myserver/ccc.xsd... More info: - * http://docs.spring.io/spring/docs/current/spring-framework- - * reference/html/resources.html - * @return PDP instance - * - * @throws IOException - * I/O error reading from {@code confLocation} - * @throws IllegalArgumentException - * Invalid PDP configuration at {@code confLocation} - * - */ - public static PDPImpl getPDP(String confLocation) throws IOException, IllegalArgumentException - { - return getPDP(confLocation, null, null); - } - - /** - * Create PDP instance. Locations here may be any resource string supported - * by Spring ResourceLoader. More info: - * http://docs.spring.io/spring/docs/current/spring-framework-reference/html - * /resources.html - * - * For example: classpath:com/myapp/aaa.xsd, file:///data/bbb.xsd, - * http://myserver/ccc.xsd... - * - * @param confLocation - * location of PDP configuration XML file, compliant with the PDP - * XML schema (pdp.xsd) - * - * @param extensionXsdLocation - * location of user-defined extension XSD (may be null if no - * extension to load), if exists; in such XSD, there must be a - * XSD import for each extension, where the 'schemaLocation' - * attribute value must be - * ${fully_qualidifed_jaxb_class_bound_to_extension_XML_type}. - * xsd, for example: - * - *
-	 * {@literal
-	 * 		   
-	 * 		  
-	 * 
-	 *            
-	 * 
-	 *            
-	 * 			}
-	 *            
- * - * In this example, - * 'com.thalesgroup.authzforce.model._3_0.Provider.attribute.rest - * .RESTfulAttributeFinde r ' is the JAXB-annotated class bound - * to XML type 'RESTfulAttributeProvider'. We assume that this - * XML type is an extension of one the PDP extension base types, - * 'AbstractAttributeProvider' (that extends - * 'AbstractPdpExtension' like all other extension base types) in - * this case. - * - * @param catalogLocation - * location of XML catalog for resolving XSDs imported by the - * pdp.xsd (PDP configuration schema) and the extension XSD - * specified as 'extensionXsdLocation' argument (may be null) - * @return PDP instance - * @throws IOException - * I/O error reading from {@code confLocation} - * @throws IllegalArgumentException - * Invalid PDP configuration at {@code confLocation} - * - */ - public static PDPImpl getPDP(String confLocation, String catalogLocation, String extensionXsdLocation) throws IOException, IllegalArgumentException - { - return getPDP(confLocation, new PdpModelHandler(catalogLocation, extensionXsdLocation)); - } - - /** - * Create PDP instance. Locations here can be any resource string supported - * by Spring ResourceLoader. More info: - * http://docs.spring.io/spring/docs/current/spring-framework-reference/html - * /resources.html - * - * For example: classpath:com/myapp/aaa.xsd, file:///data/bbb.xsd, - * http://myserver/ccc.xsd... - * - * @param confFile - * PDP configuration XML file, compliant with the PDP XML schema - * (pdp.xsd) - * - * @param extensionXsdLocation - * location of user-defined extension XSD (may be null if no - * extension to load), if exists; in such XSD, there must be a - * XSD import for each extension, where the 'schemaLocation' - * attribute value must be - * ${fully_qualidifed_jaxb_class_bound_to_extension_XML_type}. - * xsd, for example: - * - *
-	 * {@literal
-	 * 		   
-	 * 		  
-	 * 
-	 *            
-	 * 
-	 *            
-	 * 			}
-	 *            
- * - * In this example, - * 'com.thalesgroup.authzforce.model._3_0.Provider.attribute.rest - * .RESTfulAttributeFinde r ' is the JAXB-annotated class bound - * to XML type 'RESTfulAttributeProvider'. We assume that this - * XML type is an extension of one the PDP extension base types, - * 'AbstractAttributeProvider' (that extends - * 'AbstractPdpExtension' like all other extension base types) in - * this case. - * - * @param catalogLocation - * location of XML catalog for resolving XSDs imported by the - * pdp.xsd (PDP configuration schema) and the extension XSD - * specified as 'extensionXsdLocation' argument (may be null) - * @return PDP instance - * @throws IOException - * I/O error reading from {@code confLocation} - * @throws IllegalArgumentException - * Invalid PDP configuration at {@code confLocation} - * - */ - public static PDPImpl getPDP(File confFile, String catalogLocation, String extensionXsdLocation) throws IOException, IllegalArgumentException - { - return getPDP(confFile, new PdpModelHandler(catalogLocation, extensionXsdLocation)); - } - - /** - * Create PDP instance. Locations here can be any resource string supported - * by Spring ResourceLoader. More info: - * http://docs.spring.io/spring/docs/current/spring-framework-reference/html - * /resources.html. - *

- * To allow using file paths relative to the parent folder of the - * configuration file (located at confLocation) anywhere in this - * configuration file (including in PDP extensions'), we define a property - * 'PARENT_DIR', so that the placeholder ${PARENT_DIR} can be used as prefix - * for file paths in the configuration file. E.g. if confLocation = - * 'file:///path/to/configurationfile', then ${PARENT_DIR} will be replaced - * by 'file:///path/to'. If confLocation is not a file on the filesystem, - * then ${PARENT_DIR} is undefined. - * - * @param confLocation - * location of PDP configuration file - * @param modelHandler - * PDP configuration model handler - * @return PDP instance - * @throws IOException - * I/O error reading from {@code confLocation} - * @throws IllegalArgumentException - * Invalid PDP configuration at {@code confLocation} - */ - public static PDPImpl getPDP(String confLocation, PdpModelHandler modelHandler) throws IOException, IllegalArgumentException - { - File confFile = null; - try - { - confFile = ResourceUtils.getFile(confLocation); - } catch (FileNotFoundException e) - { - throw new IllegalArgumentException("Invalid PDP configuration location: " + confLocation, e); - } - - return getPDP(confFile, modelHandler); - } - - /** - * Create PDP instance - *

- * To allow using file paths relative to the parent folder of the - * configuration file (located at confLocation) anywhere in this - * configuration file (including in PDP extensions'), we define a property - * 'PARENT_DIR', so that the placeholder ${PARENT_DIR} can be used as prefix - * for file paths in the configuration file. E.g. if confLocation = - * 'file:///path/to/configurationfile', then ${PARENT_DIR} will be replaced - * by 'file:///path/to'. If confLocation is not a file on the filesystem, - * then ${PARENT_DIR} is undefined. - * - * @param confFile - * PDP configuration file - * @param modelHandler - * PDP configuration model handler - * @return PDP instance - * @throws IOException - * I/O error reading from {@code confFile} - * @throws IllegalArgumentException - * Invalid PDP configuration in {@code confFile} - */ - public static PDPImpl getPDP(File confFile, PdpModelHandler modelHandler) throws IOException, IllegalArgumentException - { - if (confFile == null || !confFile.exists()) - { - // no property replacement of PARENT_DIR - throw new IllegalArgumentException("Invalid configuration file location: No file exists at: " + confFile); - } - - if (modelHandler == null) - { - throw NULL_PDP_MODEL_HANDLER_ARGUMENT_EXCEPTION; - } - - // configuration file exists - final Pdp pdpJaxbConf; - try - { - pdpJaxbConf = modelHandler.unmarshal(new StreamSource(confFile), Pdp.class); - } catch (JAXBException e) - { - throw new IllegalArgumentException("Invalid PDP configuration file", e); - } - - // Set property PARENT_DIR in environment properties for future - // replacement in configuration strings by PDP extensions using file - // paths - final String propVal = confFile.getParentFile().toURI().toString(); - LOGGER.debug("Property {} = {}", EnvironmentPropertyName.PARENT_DIR, propVal); - final EnvironmentProperties envProps = new DefaultEnvironmentProperties(Collections.singletonMap(EnvironmentPropertyName.PARENT_DIR, propVal)); - return getPDP(pdpJaxbConf, envProps); - } - - /** - * Get PDP instance - * - * @param pdpJaxbConf - * (JAXB-bound) PDP configuration - * @param envProps - * PDP configuration environment properties (e.g. PARENT_DIR) - * @return PDP instance - * @throws IllegalArgumentException - * invalid PDP configuration - * @throws IOException - * if any error occurred closing already created - * {@link Closeable} modules (policy Providers, attribute - * Providers, decision cache) - */ - public static PDPImpl getPDP(Pdp pdpJaxbConf, EnvironmentProperties envProps) throws IllegalArgumentException, IOException - { - /* - * Initialize all parameters of ExpressionFactoryImpl: attribute - * datatype factories, functions, etc. - */ - - final boolean enableXPath = pdpJaxbConf.isEnableXPath(); - - // Attribute datatypes - final DatatypeFactoryRegistry attributeFactory = new BaseDatatypeFactoryRegistry(pdpJaxbConf.isUseStandardDatatypes() ? (enableXPath ? StandardDatatypeFactoryRegistry.ALL_DATATYPES : StandardDatatypeFactoryRegistry.MANDATORY_DATATYPES) : null); - for (final String attrDatatypeURI : pdpJaxbConf.getAttributeDatatypes()) - { - final DatatypeFactory datatypeFactory = PdpExtensionLoader.getExtension(DatatypeFactory.class, attrDatatypeURI); - attributeFactory.addExtension(datatypeFactory); - } - - // Functions - final FunctionRegistry functionRegistry = new FunctionRegistry(pdpJaxbConf.isUseStandardFunctions() ? StandardFunctionRegistry.getInstance(enableXPath) : null); - for (final String funcId : pdpJaxbConf.getFunctions()) - { - final Function function = PdpExtensionLoader.getExtension(Function.class, funcId); - if (!enableXPath && isXpathBased(function)) - { - throw new IllegalArgumentException("XPath-based function not allowed (because configuration parameter 'enableXPath' = false): " + function); - } - - functionRegistry.addFunction(function); - } - - for (final String funcSetId : pdpJaxbConf.getFunctionSets()) - { - final FunctionSet functionSet = PdpExtensionLoader.getExtension(FunctionSet.class, funcSetId); - for (final Function function : functionSet.getSupportedFunctions()) - { - if (!enableXPath && isXpathBased(function)) - { - throw new IllegalArgumentException("XPath-based function not allowed (because configuration parameter 'enableXPath' = false): " + function); - } - - functionRegistry.addFunction(function); - } - } - - // Combining Algorithms - final CombiningAlgRegistry combiningAlgRegistry = new BaseCombiningAlgRegistry(pdpJaxbConf.isUseStandardCombiningAlgorithms() ? StandardCombiningAlgRegistry.INSTANCE : null); - for (final String algId : pdpJaxbConf.getCombiningAlgorithms()) - { - final CombiningAlg alg = PdpExtensionLoader.getExtension(CombiningAlg.class, algId); - combiningAlgRegistry.addExtension(alg); - } - - // Decision combiner - final String resultFilterId = pdpJaxbConf.getResultFilter(); - final DecisionResultFilter decisionResultFilter = resultFilterId == null ? null : PdpExtensionLoader.getExtension(DecisionResultFilter.class, resultFilterId); - - // decision cache - final AbstractDecisionCache jaxbDecisionCache = pdpJaxbConf.getDecisionCache(); - - final Integer maxVarRefDepth = pdpJaxbConf.getMaxVariableRefDepth(); - final Integer maxPolicyRefDepth = pdpJaxbConf.getMaxPolicyRefDepth(); - return new PDPImpl(attributeFactory, functionRegistry, pdpJaxbConf.getAttributeProviders(), maxVarRefDepth == null ? -1 : maxVarRefDepth, enableXPath, combiningAlgRegistry, pdpJaxbConf.getRootPolicyProvider(), pdpJaxbConf.getRefPolicyProvider(), maxPolicyRefDepth == null ? -1 : maxPolicyRefDepth, pdpJaxbConf.getRequestFilter(), pdpJaxbConf.isStrictAttributeIssuerMatch(), decisionResultFilter, jaxbDecisionCache, envProps); - } - - private static boolean isXpathBased(Function function) - { - /* - * A function is said "XPath-based" iff it takes at least one - * XPathExpression parameter. Regarding higher-order function, as of - * now, we only provide higher-order functions defined in the XACML - * (3.0) Core specification, which are not XPath-based, or if a - * higher-order function happens to take a XPathExpression parameter, it - * is actually a parameter to the first-order sub-function. Plus it is - * not possible to add extensions that are higher-order functions in - * this PDP implementation. Therefore, it is enough to check first-order - * functions (class FirstOrderFunction) only. (Remember that such - * functions may be used as parameter to a higher-order function.) - */ - if (function instanceof FirstOrderFunction) - { - final List> paramTypes = ((FirstOrderFunction) function).getParameterTypes(); - for (final Datatype paramType : paramTypes) - { - if (paramType.getId().equals(XACMLDatatypeId.XPATH_EXPRESSION.value())) - { - return true; - } - } - } - - return false; - } - -} +/** + * Copyright (C) 2011-2015 Thales Services SAS. + * + * This file is part of AuthZForce. + * + * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * AuthZForce 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . + */ +package org.ow2.authzforce.core.pdp.impl; + +import java.io.Closeable; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.Collections; +import java.util.List; + +import javax.xml.bind.JAXBException; +import javax.xml.transform.stream.StreamSource; + +import org.ow2.authzforce.core.pdp.api.CombiningAlg; +import org.ow2.authzforce.core.pdp.api.CombiningAlgRegistry; +import org.ow2.authzforce.core.pdp.api.Datatype; +import org.ow2.authzforce.core.pdp.api.DatatypeFactory; +import org.ow2.authzforce.core.pdp.api.DatatypeFactoryRegistry; +import org.ow2.authzforce.core.pdp.api.DecisionResultFilter; +import org.ow2.authzforce.core.pdp.api.EnvironmentProperties; +import org.ow2.authzforce.core.pdp.api.EnvironmentPropertyName; +import org.ow2.authzforce.core.pdp.api.FirstOrderFunction; +import org.ow2.authzforce.core.pdp.api.Function; +import org.ow2.authzforce.core.pdp.api.FunctionSet; +import org.ow2.authzforce.core.pdp.impl.combining.BaseCombiningAlgRegistry; +import org.ow2.authzforce.core.pdp.impl.combining.StandardCombiningAlgRegistry; +import org.ow2.authzforce.core.pdp.impl.func.FunctionRegistry; +import org.ow2.authzforce.core.pdp.impl.func.StandardFunctionRegistry; +import org.ow2.authzforce.core.pdp.impl.value.BaseDatatypeFactoryRegistry; +import org.ow2.authzforce.core.pdp.impl.value.StandardDatatypeFactoryRegistry; +import org.ow2.authzforce.core.xmlns.pdp.Pdp; +import org.ow2.authzforce.xacml.identifiers.XACMLDatatypeId; +import org.ow2.authzforce.xmlns.pdp.ext.AbstractDecisionCache; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.util.ResourceUtils; + +/** + * XML-based PDP Configuration parser + * + * @author cdangerv + * @version $Id: $ + */ +public class PdpConfigurationParser +{ + private static final IllegalArgumentException NULL_PDP_MODEL_HANDLER_ARGUMENT_EXCEPTION = new IllegalArgumentException("Undefined PDP configuration model handler"); + private final static Logger LOGGER = LoggerFactory.getLogger(PdpConfigurationParser.class); + + /** + * Create PDP instance. + * + * @param confLocation + * location of PDP configuration XML file, compliant with the PDP + * XML schema (pdp.xsd). This location may be any resource string + * supported by Spring ResourceLoader. For example: + * classpath:com/myapp/aaa.xsd, file:///data/bbb.xsd, + * http://myserver/ccc.xsd... More info: + * http://docs.spring.io/spring/docs/current/spring-framework- + * reference/html/resources.html + * @return PDP instance + * @throws java.io.IOException + * I/O error reading from {@code confLocation} + * @throws java.lang.IllegalArgumentException + * Invalid PDP configuration at {@code confLocation} + */ + public static PDPImpl getPDP(String confLocation) throws IOException, IllegalArgumentException + { + return getPDP(confLocation, null, null); + } + + /** + * Create PDP instance. Locations here may be any resource string supported + * by Spring ResourceLoader. More info: + * http://docs.spring.io/spring/docs/current/spring-framework-reference/html + * /resources.html + * + * For example: classpath:com/myapp/aaa.xsd, file:///data/bbb.xsd, + * http://myserver/ccc.xsd... + * + * @param confLocation + * location of PDP configuration XML file, compliant with the PDP + * XML schema (pdp.xsd) + * @param extensionXsdLocation + * location of user-defined extension XSD (may be null if no + * extension to load), if exists; in such XSD, there must be a + * XSD import for each extension, where the 'schemaLocation' + * attribute value must be + * ${fully_qualidifed_jaxb_class_bound_to_extension_XML_type}. + * xsd, for example: + * + *

+	 * {@literal
+	 * 		  
+	 * 		  
+	 *
+	 *            
+	 *
+	 *            
+	 * 			}
+	 *            
+ * + * In this example, + * 'com.thalesgroup.authzforce.model._3_0.Provider.attribute.rest + * .RESTfulAttributeFinde r ' is the JAXB-annotated class bound + * to XML type 'RESTfulAttributeProvider'. We assume that this + * XML type is an extension of one the PDP extension base types, + * 'AbstractAttributeProvider' (that extends + * 'AbstractPdpExtension' like all other extension base types) in + * this case. + * @param catalogLocation + * location of XML catalog for resolving XSDs imported by the + * pdp.xsd (PDP configuration schema) and the extension XSD + * specified as 'extensionXsdLocation' argument (may be null) + * @return PDP instance + * @throws java.io.IOException + * I/O error reading from {@code confLocation} + * @throws java.lang.IllegalArgumentException + * Invalid PDP configuration at {@code confLocation} + */ + public static PDPImpl getPDP(String confLocation, String catalogLocation, String extensionXsdLocation) throws IOException, IllegalArgumentException + { + return getPDP(confLocation, new PdpModelHandler(catalogLocation, extensionXsdLocation)); + } + + /** + * Create PDP instance. Locations here can be any resource string supported + * by Spring ResourceLoader. More info: + * http://docs.spring.io/spring/docs/current/spring-framework-reference/html + * /resources.html + * + * For example: classpath:com/myapp/aaa.xsd, file:///data/bbb.xsd, + * http://myserver/ccc.xsd... + * + * @param confFile + * PDP configuration XML file, compliant with the PDP XML schema + * (pdp.xsd) + * @param extensionXsdLocation + * location of user-defined extension XSD (may be null if no + * extension to load), if exists; in such XSD, there must be a + * XSD import for each extension, where the 'schemaLocation' + * attribute value must be + * ${fully_qualidifed_jaxb_class_bound_to_extension_XML_type}. + * xsd, for example: + * + *
+	 * {@literal
+	 * 		  
+	 * 		  
+	 *
+	 *            
+	 *
+	 *            
+	 * 			}
+	 *            
+ * + * In this example, + * 'com.thalesgroup.authzforce.model._3_0.Provider.attribute.rest + * .RESTfulAttributeFinde r ' is the JAXB-annotated class bound + * to XML type 'RESTfulAttributeProvider'. We assume that this + * XML type is an extension of one the PDP extension base types, + * 'AbstractAttributeProvider' (that extends + * 'AbstractPdpExtension' like all other extension base types) in + * this case. + * @param catalogLocation + * location of XML catalog for resolving XSDs imported by the + * pdp.xsd (PDP configuration schema) and the extension XSD + * specified as 'extensionXsdLocation' argument (may be null) + * @return PDP instance + * @throws java.io.IOException + * I/O error reading from {@code confLocation} + * @throws java.lang.IllegalArgumentException + * Invalid PDP configuration at {@code confLocation} + */ + public static PDPImpl getPDP(File confFile, String catalogLocation, String extensionXsdLocation) throws IOException, IllegalArgumentException + { + return getPDP(confFile, new PdpModelHandler(catalogLocation, extensionXsdLocation)); + } + + /** + * Create PDP instance. Locations here can be any resource string supported + * by Spring ResourceLoader. More info: + * http://docs.spring.io/spring/docs/current/spring-framework-reference/html + * /resources.html. + *

+ * To allow using file paths relative to the parent folder of the + * configuration file (located at confLocation) anywhere in this + * configuration file (including in PDP extensions'), we define a property + * 'PARENT_DIR', so that the placeholder ${PARENT_DIR} can be used as prefix + * for file paths in the configuration file. E.g. if confLocation = + * 'file:///path/to/configurationfile', then ${PARENT_DIR} will be replaced + * by 'file:///path/to'. If confLocation is not a file on the filesystem, + * then ${PARENT_DIR} is undefined. + * + * @param confLocation + * location of PDP configuration file + * @param modelHandler + * PDP configuration model handler + * @return PDP instance + * @throws java.io.IOException + * I/O error reading from {@code confLocation} + * @throws java.lang.IllegalArgumentException + * Invalid PDP configuration at {@code confLocation} + */ + public static PDPImpl getPDP(String confLocation, PdpModelHandler modelHandler) throws IOException, IllegalArgumentException + { + File confFile = null; + try + { + confFile = ResourceUtils.getFile(confLocation); + } catch (FileNotFoundException e) + { + throw new IllegalArgumentException("Invalid PDP configuration location: " + confLocation, e); + } + + return getPDP(confFile, modelHandler); + } + + /** + * Create PDP instance + *

+ * To allow using file paths relative to the parent folder of the + * configuration file (located at confLocation) anywhere in this + * configuration file (including in PDP extensions'), we define a property + * 'PARENT_DIR', so that the placeholder ${PARENT_DIR} can be used as prefix + * for file paths in the configuration file. E.g. if confLocation = + * 'file:///path/to/configurationfile', then ${PARENT_DIR} will be replaced + * by 'file:///path/to'. If confLocation is not a file on the filesystem, + * then ${PARENT_DIR} is undefined. + * + * @param confFile + * PDP configuration file + * @param modelHandler + * PDP configuration model handler + * @return PDP instance + * @throws java.io.IOException + * I/O error reading from {@code confFile} + * @throws java.lang.IllegalArgumentException + * Invalid PDP configuration in {@code confFile} + */ + public static PDPImpl getPDP(File confFile, PdpModelHandler modelHandler) throws IOException, IllegalArgumentException + { + if (confFile == null || !confFile.exists()) + { + // no property replacement of PARENT_DIR + throw new IllegalArgumentException("Invalid configuration file location: No file exists at: " + confFile); + } + + if (modelHandler == null) + { + throw NULL_PDP_MODEL_HANDLER_ARGUMENT_EXCEPTION; + } + + // configuration file exists + final Pdp pdpJaxbConf; + try + { + pdpJaxbConf = modelHandler.unmarshal(new StreamSource(confFile), Pdp.class); + } catch (JAXBException e) + { + throw new IllegalArgumentException("Invalid PDP configuration file", e); + } + + // Set property PARENT_DIR in environment properties for future + // replacement in configuration strings by PDP extensions using file + // paths + final String propVal = confFile.getParentFile().toURI().toString(); + LOGGER.debug("Property {} = {}", EnvironmentPropertyName.PARENT_DIR, propVal); + final EnvironmentProperties envProps = new DefaultEnvironmentProperties(Collections.singletonMap(EnvironmentPropertyName.PARENT_DIR, propVal)); + return getPDP(pdpJaxbConf, envProps); + } + + /** + * Get PDP instance + * + * @param pdpJaxbConf + * (JAXB-bound) PDP configuration + * @param envProps + * PDP configuration environment properties (e.g. PARENT_DIR) + * @return PDP instance + * @throws java.lang.IllegalArgumentException + * invalid PDP configuration + * @throws java.io.IOException + * if any error occurred closing already created + * {@link Closeable} modules (policy Providers, attribute + * Providers, decision cache) + */ + public static PDPImpl getPDP(Pdp pdpJaxbConf, EnvironmentProperties envProps) throws IllegalArgumentException, IOException + { + /* + * Initialize all parameters of ExpressionFactoryImpl: attribute + * datatype factories, functions, etc. + */ + + final boolean enableXPath = pdpJaxbConf.isEnableXPath(); + + // Attribute datatypes + final DatatypeFactoryRegistry attributeFactory = new BaseDatatypeFactoryRegistry(pdpJaxbConf.isUseStandardDatatypes() ? (enableXPath ? StandardDatatypeFactoryRegistry.ALL_DATATYPES : StandardDatatypeFactoryRegistry.MANDATORY_DATATYPES) : null); + for (final String attrDatatypeURI : pdpJaxbConf.getAttributeDatatypes()) + { + final DatatypeFactory datatypeFactory = PdpExtensionLoader.getExtension(DatatypeFactory.class, attrDatatypeURI); + attributeFactory.addExtension(datatypeFactory); + } + + // Functions + final FunctionRegistry functionRegistry = new FunctionRegistry(pdpJaxbConf.isUseStandardFunctions() ? StandardFunctionRegistry.getInstance(enableXPath) : null); + for (final String funcId : pdpJaxbConf.getFunctions()) + { + final Function function = PdpExtensionLoader.getExtension(Function.class, funcId); + if (!enableXPath && isXpathBased(function)) + { + throw new IllegalArgumentException("XPath-based function not allowed (because configuration parameter 'enableXPath' = false): " + function); + } + + functionRegistry.addFunction(function); + } + + for (final String funcSetId : pdpJaxbConf.getFunctionSets()) + { + final FunctionSet functionSet = PdpExtensionLoader.getExtension(FunctionSet.class, funcSetId); + for (final Function function : functionSet.getSupportedFunctions()) + { + if (!enableXPath && isXpathBased(function)) + { + throw new IllegalArgumentException("XPath-based function not allowed (because configuration parameter 'enableXPath' = false): " + function); + } + + functionRegistry.addFunction(function); + } + } + + // Combining Algorithms + final CombiningAlgRegistry combiningAlgRegistry = new BaseCombiningAlgRegistry(pdpJaxbConf.isUseStandardCombiningAlgorithms() ? StandardCombiningAlgRegistry.INSTANCE : null); + for (final String algId : pdpJaxbConf.getCombiningAlgorithms()) + { + final CombiningAlg alg = PdpExtensionLoader.getExtension(CombiningAlg.class, algId); + combiningAlgRegistry.addExtension(alg); + } + + // Decision combiner + final String resultFilterId = pdpJaxbConf.getResultFilter(); + final DecisionResultFilter decisionResultFilter = resultFilterId == null ? null : PdpExtensionLoader.getExtension(DecisionResultFilter.class, resultFilterId); + + // decision cache + final AbstractDecisionCache jaxbDecisionCache = pdpJaxbConf.getDecisionCache(); + + final Integer maxVarRefDepth = pdpJaxbConf.getMaxVariableRefDepth(); + final Integer maxPolicyRefDepth = pdpJaxbConf.getMaxPolicyRefDepth(); + return new PDPImpl(attributeFactory, functionRegistry, pdpJaxbConf.getAttributeProviders(), maxVarRefDepth == null ? -1 : maxVarRefDepth, enableXPath, combiningAlgRegistry, pdpJaxbConf.getRootPolicyProvider(), pdpJaxbConf.getRefPolicyProvider(), maxPolicyRefDepth == null ? -1 : maxPolicyRefDepth, pdpJaxbConf.getRequestFilter(), pdpJaxbConf.isStrictAttributeIssuerMatch(), decisionResultFilter, jaxbDecisionCache, envProps); + } + + private static boolean isXpathBased(Function function) + { + /* + * A function is said "XPath-based" iff it takes at least one + * XPathExpression parameter. Regarding higher-order function, as of + * now, we only provide higher-order functions defined in the XACML + * (3.0) Core specification, which are not XPath-based, or if a + * higher-order function happens to take a XPathExpression parameter, it + * is actually a parameter to the first-order sub-function. Plus it is + * not possible to add extensions that are higher-order functions in + * this PDP implementation. Therefore, it is enough to check first-order + * functions (class FirstOrderFunction) only. (Remember that such + * functions may be used as parameter to a higher-order function.) + */ + if (function instanceof FirstOrderFunction) + { + final List> paramTypes = ((FirstOrderFunction) function).getParameterTypes(); + for (final Datatype paramType : paramTypes) + { + if (paramType.getId().equals(XACMLDatatypeId.XPATH_EXPRESSION.value())) + { + return true; + } + } + } + + return false; + } + +} diff --git a/src/main/java/org/ow2/authzforce/core/pdp/impl/PdpExtensionLoader.java b/src/main/java/org/ow2/authzforce/core/pdp/impl/PdpExtensionLoader.java index a2221580..11be9444 100644 --- a/src/main/java/org/ow2/authzforce/core/pdp/impl/PdpExtensionLoader.java +++ b/src/main/java/org/ow2/authzforce/core/pdp/impl/PdpExtensionLoader.java @@ -1,336 +1,340 @@ -/** - * Copyright (C) 2011-2015 Thales Services SAS. - * - * This file is part of AuthZForce. - * - * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software - * Foundation, either version 3 of the License, or (at your option) any later version. - * - * AuthZForce 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . - */ -package org.ow2.authzforce.core.pdp.impl; - -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.ServiceLoader; -import java.util.Set; - -import org.ow2.authzforce.core.pdp.api.CombiningAlg; -import org.ow2.authzforce.core.pdp.api.DatatypeFactory; -import org.ow2.authzforce.core.pdp.api.DecisionResultFilter; -import org.ow2.authzforce.core.pdp.api.Function; -import org.ow2.authzforce.core.pdp.api.FunctionSet; -import org.ow2.authzforce.core.pdp.api.JaxbBoundPdpExtension; -import org.ow2.authzforce.core.pdp.api.PdpExtension; -import org.ow2.authzforce.core.pdp.api.RequestFilter; -import org.ow2.authzforce.core.pdp.impl.combining.CombiningAlgSet; -import org.ow2.authzforce.xmlns.pdp.ext.AbstractPdpExtension; - -/** - * Loads PDP extensions (implementing {@link PdpExtension}) from classpath using {@link ServiceLoader}. - * - * - */ -public class PdpExtensionLoader -{ - // private static final Logger LOGGER = LoggerFactory.getLogger(PdpExtensionLoader.class); - - /* - * For each type of extension, we build the maps allowing to get the compatible/supporting extension class, using {@link ServiceLoader} API, to discover - * these classes from files 'META-INF/services/com.thalesgroup.authzforce.core.PdpExtension' on the classpath, in the format described by {@link - * ServiceLoader} API documentation. - */ - - /* - * For each type of XML/JAXB-bound extension, map XML/JAXB conf class to corresponding extension (we assume a one-to-one relationship between the XML/JAXB - * type and the extension class) - */ - private final static Map, JaxbBoundPdpExtension> JAXB_BOUND_EXTENSIONS_BY_JAXB_CLASS = new HashMap<>(); - - /* - * Types of zero-conf (non-JAXB-bound) extesnsion - */ - private static final Set> NON_JAXB_BOUND_EXTENSION_CLASSES = new HashSet<>(Arrays.asList(DatatypeFactory.class, - Function.class, FunctionSet.class, CombiningAlg.class, RequestFilter.Factory.class, DecisionResultFilter.class)); - /* - * For each type of zero-conf (non-JAXB-bound) extension, have a map (extension ID -> extension instance), so that the extension ID is scoped to the - * extension type among the ones listed in NON_JAXB_BOUND_EXTENSION_CLASSES (you can have same ID but for different types of extensions). - */ - private final static Map, Map> NON_JAXB_BOUND_EXTENSIONS_BY_CLASS_AND_ID = new HashMap<>(); - - static - { - /* - * REMINDER: every service provider (implementation class) loaded by ServiceLoader MUST HAVE a ZERO-ARGUMENT CONSTRUCTOR. - */ - final ServiceLoader extensionLoader = ServiceLoader.load(PdpExtension.class); - for (final PdpExtension extension : extensionLoader) - { - boolean isValidExt = false; - if (extension instanceof JaxbBoundPdpExtension) - { - final JaxbBoundPdpExtension jaxbBoundExt = (JaxbBoundPdpExtension) extension; - final JaxbBoundPdpExtension conflictingExt = JAXB_BOUND_EXTENSIONS_BY_JAXB_CLASS.put(jaxbBoundExt.getJaxbClass(), jaxbBoundExt); - if (conflictingExt != null) - { - throw new IllegalArgumentException("Extension " + jaxbBoundExt + " (" + jaxbBoundExt.getClass() + ") is conflicting with " + conflictingExt - + "(" + conflictingExt.getClass() + ") for the same XML/JAXB configuration class: " + jaxbBoundExt.getJaxbClass()); - } - - isValidExt = true; - } else - { - for (final Class extClass : NON_JAXB_BOUND_EXTENSION_CLASSES) - { - if (extClass.isInstance(extension)) - { - final Map oldMap = NON_JAXB_BOUND_EXTENSIONS_BY_CLASS_AND_ID.get(extClass); - final Map newMap; - if (oldMap == null) - { - newMap = new HashMap<>(); - NON_JAXB_BOUND_EXTENSIONS_BY_CLASS_AND_ID.put(extClass, newMap); - } else - { - newMap = oldMap; - } - - final PdpExtension conflictingExt = newMap.put(extension.getId(), extension); - if (conflictingExt != null) - { - throw new IllegalArgumentException("Extension " + extension + " is conflicting with " + conflictingExt - + " registered with same ID: " + extension.getId()); - } - - isValidExt = true; - break; - } - } - } - - if (!isValidExt) - { - throw new UnsupportedOperationException("Unsupported/invalid type of PDP extension: " + extension.getClass() + " (extension ID = " - + extension.getId() + ")"); - } - } - } - - /** - * Get PDP extension configuration classes (JAXB-generated from XML schema) - * - * @return classes representing datamodels of configurations of all PDP extensions - */ - public static Set> getExtensionJaxbClasses() - { - return Collections.unmodifiableSet(JAXB_BOUND_EXTENSIONS_BY_JAXB_CLASS.keySet()); - } - - /** - * Get non-JAXB-bound (aka zero-configuration) extension - * - * @param extensionType - * type of extension: {@link DatatypeFactory }, {@link Function}, {@link CombiningAlgSet}, etc. - * @param id - * extension ID - * @return PDP extension instance of class {@code extensionType} and such that its method {@link PdpExtension#getId()} returns {@code id} - * @throws IllegalArgumentException - * if there is not any extension found for type {@code extensionType} with ID {@code id} - */ - public static T getExtension(Class extensionType, String id) throws IllegalArgumentException - { - if(!NON_JAXB_BOUND_EXTENSIONS_BY_CLASS_AND_ID.containsKey(extensionType)) - { - throw new IllegalArgumentException("Invalid (non-JAXB-bound) PDP extension type: " + extensionType + ". Expected types: " - + NON_JAXB_BOUND_EXTENSION_CLASSES); - } - - final Map typeSpecificExtsById = NON_JAXB_BOUND_EXTENSIONS_BY_CLASS_AND_ID.get(extensionType); - if (typeSpecificExtsById == null) - { - throw new IllegalArgumentException("No PDP extension of type '" + extensionType + "' found"); - } - - final PdpExtension ext = typeSpecificExtsById.get(id); - if (ext == null) - { - throw new IllegalArgumentException("No PDP extension of type '" + extensionType + "' found with ID: " + id + ". Expected IDs: " - + typeSpecificExtsById.keySet()); - } - - return extensionType.cast(ext); - } - - /** - * Get XML/JAXB-bound extension - * - * @param extensionType - * type of extension, e.g. {@link org.ow2.authzforce.core.pdp.api.RootPolicyProviderModule.Factory}, etc. - * @param jaxbPdpExtensionClass - * JAXB class representing XML configuration type that the extension must support - * @return PDP extension instance of class {@code extensionType} and such that its method {@link JaxbBoundPdpExtension#getClass()} returns - * {@code jaxbPdpExtensionClass} - * @throws IllegalArgumentException - * if there is no extension supporting {@code jaxbPdpExtensionClass} - */ - public static > T getJaxbBoundExtension(Class extensionType, - Class jaxbPdpExtensionClass) throws IllegalArgumentException - { - final JaxbBoundPdpExtension ext = JAXB_BOUND_EXTENSIONS_BY_JAXB_CLASS.get(jaxbPdpExtensionClass); - if (ext == null) - { - throw new IllegalArgumentException("No PDP extension found supporting JAXB (configuration) type: " + jaxbPdpExtensionClass + ". Expected types: " - + JAXB_BOUND_EXTENSIONS_BY_JAXB_CLASS.keySet()); - } - - return extensionType.cast(ext); - } - - /** - * Create instance of PDP extension (AttributeProvider, ReferencedPolicyProvider...) with input configuration. The extension implementation class has been - * discovered by {@link ServiceLoader} from files 'META-INF/services/com.thalesgroup.authzforce.core.IPdpExtensionFactory' on the classpath, in the format - * described by {@link ServiceLoader} API documentation. Such class must have a constructor matching {@code constructorArgs} that is called to instantiate - * the extension, or a default constructor that is called instead if none matching such parameters; and it must implement {@code IPdpExtensionFactory} and - * therefore have a {@code IPdpExtensionFactory#init(EXTENSION_CONF_CLASS conf)} method to initialize the instance. - * - * @param extensionConf - * extension configuration (instance of custom type of PDP extension defined in XML schema) - * @param constructorArgs - * optional Constructor arguments - * @return extension instance - * @throws IllegalArgumentException - * handlerClass is not compatible with handlerconf - */ - // public static EXTENSION_CLASS getInstance( - // EXTENSION_CONF_CLASS extensionConf, Object... constructorArgs) - // { - // @SuppressWarnings("rawtypes") - // final Class implClass = - // EXTENSIONS_BY_CONF_TYPE.get(extensionConf.getClass()); - // final Class[] constructorParameters = new Class[constructorArgs.length]; - // for (int i = 0; i < constructorArgs.length; i++) - // { - // constructorParameters[i] = constructorArgs[i].getClass(); - // } - // - // Constructor implConstructor; - // try - // { - // implConstructor = implClass.getConstructor(constructorParameters); - // } catch (NoSuchMethodException | SecurityException e) - // { - // LOGGER.info("PDP extension '{}' has no constructor matching parameters {}. Falling back to default constructor.", - // implClass, - // constructorParameters, e); - // implConstructor = null; - // } - // - // final PdpExtension extImpl; - // try - // { - // extImpl = implConstructor == null ? implClass.newInstance() : - // implConstructor.newInstance(constructorArgs); - // extImpl.init(extensionConf); - // @SuppressWarnings("unchecked") - // final EXTENSION_CLASS extInstance = (EXTENSION_CLASS) extImpl; - // return extInstance; - // } catch (ClassCastException e) - // { - // throw new IllegalArgumentException("'" + implClass + - // "' defined in one of the files 'META-INF/services/" - // + IPdpExtensionFactory.class.getName() + - // "' on the classpath is not a valid extension class for configuration type '" - // + extensionConf.getClass() + "'", e); - // } catch (Exception e) - // { - // throw new RuntimeException("Failed to instantiate extension implementation " + implClass + - // " for configuration '" - // + extensionConf.getClass() + "'", e); - // } - // } - - // /** - // * Get class implementing specific PDP extension from class name. - // * - // * @param classname - // * name of implementation class - // * @param superclass - // * mandatory superclass of class whose name is specified as first argument. - // * - // * @return implementation class - // * @throws IllegalArgumentException - // * implementation is not subclass of extension superclass or class unknown (not on - // * classpath) - // */ - // public static Class - // getExtensionClass(String classname, - // Class superclass) - // { - // final Class implClass; - // try - // { - // implClass = Class.forName(classname); - // } catch (ClassNotFoundException e) - // { - // throw new IllegalArgumentException("Extension class '" + classname + - // "' not found in classpath", e); - // } - // - // if (!superclass.isAssignableFrom(implClass)) - // { - // throw new IllegalArgumentException(implClass + " is not a subclass of " + superclass); - // } - // - // return implClass.asSubclass(superclass); - // } - - // /** - // * Create instance of T using the default constructor of the class given as first argument. - // * - // * @param classname - // * name of class with default constructor used to create the instance - // * @param superclass - // * class of which the returned instance must be a sub-class - // * - // * @return instance of superclass (type of extension) - // * @throws IllegalArgumentException - // * handlerClass is not compatible with handlerconf - // */ - // public static T getInstance(String classname, Class superclass) - // { - // final Class instanceClass; - // try - // { - // instanceClass = Class.forName(classname); - // } catch (ClassNotFoundException e) - // { - // throw new IllegalArgumentException("Extension class '" + classname + - // "' not found in classpath", e); - // } - // - // if (!superclass.isAssignableFrom(instanceClass)) - // { - // throw new IllegalArgumentException(instanceClass + " is not a subclass of " + superclass); - // } - // - // try - // { - // return (T) instanceClass.newInstance(); - // } catch (InstantiationException ie) - // { - // throw new IllegalArgumentException("Cannot instantiate " + instanceClass + - // " with default constructor.", ie); - // } catch (IllegalAccessException iae) - // { - // throw new RuntimeException("Cannot access any default constructor of " + instanceClass, iae); - // } - // } - -} +/** + * Copyright (C) 2011-2015 Thales Services SAS. + * + * This file is part of AuthZForce. + * + * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * AuthZForce 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . + */ +package org.ow2.authzforce.core.pdp.impl; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.ServiceLoader; +import java.util.Set; + +import org.ow2.authzforce.core.pdp.api.CombiningAlg; +import org.ow2.authzforce.core.pdp.api.DatatypeFactory; +import org.ow2.authzforce.core.pdp.api.DecisionResultFilter; +import org.ow2.authzforce.core.pdp.api.Function; +import org.ow2.authzforce.core.pdp.api.FunctionSet; +import org.ow2.authzforce.core.pdp.api.JaxbBoundPdpExtension; +import org.ow2.authzforce.core.pdp.api.PdpExtension; +import org.ow2.authzforce.core.pdp.api.RequestFilter; +import org.ow2.authzforce.core.pdp.impl.combining.CombiningAlgSet; +import org.ow2.authzforce.xmlns.pdp.ext.AbstractPdpExtension; + +/** + * Loads PDP extensions (implementing {@link PdpExtension}) from classpath using {@link ServiceLoader}. + * + * @author cdangerv + * @version $Id: $ + */ +public class PdpExtensionLoader +{ + // private static final Logger LOGGER = LoggerFactory.getLogger(PdpExtensionLoader.class); + + /* + * For each type of extension, we build the maps allowing to get the compatible/supporting extension class, using {@link ServiceLoader} API, to discover + * these classes from files 'META-INF/services/com.thalesgroup.authzforce.core.PdpExtension' on the classpath, in the format described by {@link + * ServiceLoader} API documentation. + */ + + /* + * For each type of XML/JAXB-bound extension, map XML/JAXB conf class to corresponding extension (we assume a one-to-one relationship between the XML/JAXB + * type and the extension class) + */ + private final static Map, JaxbBoundPdpExtension> JAXB_BOUND_EXTENSIONS_BY_JAXB_CLASS = new HashMap<>(); + + /* + * Types of zero-conf (non-JAXB-bound) extesnsion + */ + private static final Set> NON_JAXB_BOUND_EXTENSION_CLASSES = new HashSet<>(Arrays.asList(DatatypeFactory.class, + Function.class, FunctionSet.class, CombiningAlg.class, RequestFilter.Factory.class, DecisionResultFilter.class)); + /* + * For each type of zero-conf (non-JAXB-bound) extension, have a map (extension ID -> extension instance), so that the extension ID is scoped to the + * extension type among the ones listed in NON_JAXB_BOUND_EXTENSION_CLASSES (you can have same ID but for different types of extensions). + */ + private final static Map, Map> NON_JAXB_BOUND_EXTENSIONS_BY_CLASS_AND_ID = new HashMap<>(); + + static + { + /* + * REMINDER: every service provider (implementation class) loaded by ServiceLoader MUST HAVE a ZERO-ARGUMENT CONSTRUCTOR. + */ + final ServiceLoader extensionLoader = ServiceLoader.load(PdpExtension.class); + for (final PdpExtension extension : extensionLoader) + { + boolean isValidExt = false; + if (extension instanceof JaxbBoundPdpExtension) + { + final JaxbBoundPdpExtension jaxbBoundExt = (JaxbBoundPdpExtension) extension; + final JaxbBoundPdpExtension conflictingExt = JAXB_BOUND_EXTENSIONS_BY_JAXB_CLASS.put(jaxbBoundExt.getJaxbClass(), jaxbBoundExt); + if (conflictingExt != null) + { + throw new IllegalArgumentException("Extension " + jaxbBoundExt + " (" + jaxbBoundExt.getClass() + ") is conflicting with " + conflictingExt + + "(" + conflictingExt.getClass() + ") for the same XML/JAXB configuration class: " + jaxbBoundExt.getJaxbClass()); + } + + isValidExt = true; + } else + { + for (final Class extClass : NON_JAXB_BOUND_EXTENSION_CLASSES) + { + if (extClass.isInstance(extension)) + { + final Map oldMap = NON_JAXB_BOUND_EXTENSIONS_BY_CLASS_AND_ID.get(extClass); + final Map newMap; + if (oldMap == null) + { + newMap = new HashMap<>(); + NON_JAXB_BOUND_EXTENSIONS_BY_CLASS_AND_ID.put(extClass, newMap); + } else + { + newMap = oldMap; + } + + final PdpExtension conflictingExt = newMap.put(extension.getId(), extension); + if (conflictingExt != null) + { + throw new IllegalArgumentException("Extension " + extension + " is conflicting with " + conflictingExt + + " registered with same ID: " + extension.getId()); + } + + isValidExt = true; + break; + } + } + } + + if (!isValidExt) + { + throw new UnsupportedOperationException("Unsupported/invalid type of PDP extension: " + extension.getClass() + " (extension ID = " + + extension.getId() + ")"); + } + } + } + + /** + * Get PDP extension configuration classes (JAXB-generated from XML schema) + * + * @return classes representing datamodels of configurations of all PDP extensions + */ + public static Set> getExtensionJaxbClasses() + { + return Collections.unmodifiableSet(JAXB_BOUND_EXTENSIONS_BY_JAXB_CLASS.keySet()); + } + + /** + * Get non-JAXB-bound (aka zero-configuration) extension + * + * @param extensionType + * type of extension: {@link DatatypeFactory }, {@link Function}, {@link CombiningAlgSet}, etc. + * @param id + * extension ID + * @return PDP extension instance of class {@code extensionType} and such that its method {@link PdpExtension#getId()} returns {@code id} + * @throws java.lang.IllegalArgumentException + * if there is not any extension found for type {@code extensionType} with ID {@code id} + * @param a T object. + */ + public static T getExtension(Class extensionType, String id) throws IllegalArgumentException + { + if(!NON_JAXB_BOUND_EXTENSIONS_BY_CLASS_AND_ID.containsKey(extensionType)) + { + throw new IllegalArgumentException("Invalid (non-JAXB-bound) PDP extension type: " + extensionType + ". Expected types: " + + NON_JAXB_BOUND_EXTENSION_CLASSES); + } + + final Map typeSpecificExtsById = NON_JAXB_BOUND_EXTENSIONS_BY_CLASS_AND_ID.get(extensionType); + if (typeSpecificExtsById == null) + { + throw new IllegalArgumentException("No PDP extension of type '" + extensionType + "' found"); + } + + final PdpExtension ext = typeSpecificExtsById.get(id); + if (ext == null) + { + throw new IllegalArgumentException("No PDP extension of type '" + extensionType + "' found with ID: " + id + ". Expected IDs: " + + typeSpecificExtsById.keySet()); + } + + return extensionType.cast(ext); + } + + /** + * Get XML/JAXB-bound extension + * + * @param extensionType + * type of extension, e.g. {@link org.ow2.authzforce.core.pdp.api.RootPolicyProviderModule.Factory}, etc. + * @param jaxbPdpExtensionClass + * JAXB class representing XML configuration type that the extension must support + * @return PDP extension instance of class {@code extensionType} and such that its method {@link JaxbBoundPdpExtension#getClass()} returns + * {@code jaxbPdpExtensionClass} + * @throws java.lang.IllegalArgumentException + * if there is no extension supporting {@code jaxbPdpExtensionClass} + * @param a JAXB_T object. + * @param a T object. + */ + public static > T getJaxbBoundExtension(Class extensionType, + Class jaxbPdpExtensionClass) throws IllegalArgumentException + { + final JaxbBoundPdpExtension ext = JAXB_BOUND_EXTENSIONS_BY_JAXB_CLASS.get(jaxbPdpExtensionClass); + if (ext == null) + { + throw new IllegalArgumentException("No PDP extension found supporting JAXB (configuration) type: " + jaxbPdpExtensionClass + ". Expected types: " + + JAXB_BOUND_EXTENSIONS_BY_JAXB_CLASS.keySet()); + } + + return extensionType.cast(ext); + } + + /** + * Create instance of PDP extension (AttributeProvider, ReferencedPolicyProvider...) with input configuration. The extension implementation class has been + * discovered by {@link ServiceLoader} from files 'META-INF/services/com.thalesgroup.authzforce.core.IPdpExtensionFactory' on the classpath, in the format + * described by {@link ServiceLoader} API documentation. Such class must have a constructor matching {@code constructorArgs} that is called to instantiate + * the extension, or a default constructor that is called instead if none matching such parameters; and it must implement {@code IPdpExtensionFactory} and + * therefore have a {@code IPdpExtensionFactory#init(EXTENSION_CONF_CLASS conf)} method to initialize the instance. + * + * @param extensionConf + * extension configuration (instance of custom type of PDP extension defined in XML schema) + * @param constructorArgs + * optional Constructor arguments + * @return extension instance + * @throws IllegalArgumentException + * handlerClass is not compatible with handlerconf + */ + // public static EXTENSION_CLASS getInstance( + // EXTENSION_CONF_CLASS extensionConf, Object... constructorArgs) + // { + // @SuppressWarnings("rawtypes") + // final Class implClass = + // EXTENSIONS_BY_CONF_TYPE.get(extensionConf.getClass()); + // final Class[] constructorParameters = new Class[constructorArgs.length]; + // for (int i = 0; i < constructorArgs.length; i++) + // { + // constructorParameters[i] = constructorArgs[i].getClass(); + // } + // + // Constructor implConstructor; + // try + // { + // implConstructor = implClass.getConstructor(constructorParameters); + // } catch (NoSuchMethodException | SecurityException e) + // { + // LOGGER.info("PDP extension '{}' has no constructor matching parameters {}. Falling back to default constructor.", + // implClass, + // constructorParameters, e); + // implConstructor = null; + // } + // + // final PdpExtension extImpl; + // try + // { + // extImpl = implConstructor == null ? implClass.newInstance() : + // implConstructor.newInstance(constructorArgs); + // extImpl.init(extensionConf); + // @SuppressWarnings("unchecked") + // final EXTENSION_CLASS extInstance = (EXTENSION_CLASS) extImpl; + // return extInstance; + // } catch (ClassCastException e) + // { + // throw new IllegalArgumentException("'" + implClass + + // "' defined in one of the files 'META-INF/services/" + // + IPdpExtensionFactory.class.getName() + + // "' on the classpath is not a valid extension class for configuration type '" + // + extensionConf.getClass() + "'", e); + // } catch (Exception e) + // { + // throw new RuntimeException("Failed to instantiate extension implementation " + implClass + + // " for configuration '" + // + extensionConf.getClass() + "'", e); + // } + // } + + // /** + // * Get class implementing specific PDP extension from class name. + // * + // * @param classname + // * name of implementation class + // * @param superclass + // * mandatory superclass of class whose name is specified as first argument. + // * + // * @return implementation class + // * @throws IllegalArgumentException + // * implementation is not subclass of extension superclass or class unknown (not on + // * classpath) + // */ + // public static Class + // getExtensionClass(String classname, + // Class superclass) + // { + // final Class implClass; + // try + // { + // implClass = Class.forName(classname); + // } catch (ClassNotFoundException e) + // { + // throw new IllegalArgumentException("Extension class '" + classname + + // "' not found in classpath", e); + // } + // + // if (!superclass.isAssignableFrom(implClass)) + // { + // throw new IllegalArgumentException(implClass + " is not a subclass of " + superclass); + // } + // + // return implClass.asSubclass(superclass); + // } + + // /** + // * Create instance of T using the default constructor of the class given as first argument. + // * + // * @param classname + // * name of class with default constructor used to create the instance + // * @param superclass + // * class of which the returned instance must be a sub-class + // * + // * @return instance of superclass (type of extension) + // * @throws IllegalArgumentException + // * handlerClass is not compatible with handlerconf + // */ + // public static T getInstance(String classname, Class superclass) + // { + // final Class instanceClass; + // try + // { + // instanceClass = Class.forName(classname); + // } catch (ClassNotFoundException e) + // { + // throw new IllegalArgumentException("Extension class '" + classname + + // "' not found in classpath", e); + // } + // + // if (!superclass.isAssignableFrom(instanceClass)) + // { + // throw new IllegalArgumentException(instanceClass + " is not a subclass of " + superclass); + // } + // + // try + // { + // return (T) instanceClass.newInstance(); + // } catch (InstantiationException ie) + // { + // throw new IllegalArgumentException("Cannot instantiate " + instanceClass + + // " with default constructor.", ie); + // } catch (IllegalAccessException iae) + // { + // throw new RuntimeException("Cannot access any default constructor of " + instanceClass, iae); + // } + // } + +} diff --git a/src/main/java/org/ow2/authzforce/core/pdp/impl/PdpModelHandler.java b/src/main/java/org/ow2/authzforce/core/pdp/impl/PdpModelHandler.java index 881f56e6..68e75ad4 100644 --- a/src/main/java/org/ow2/authzforce/core/pdp/impl/PdpModelHandler.java +++ b/src/main/java/org/ow2/authzforce/core/pdp/impl/PdpModelHandler.java @@ -1,199 +1,200 @@ -/** - * Copyright (C) 2011-2015 Thales Services SAS. - * - * This file is part of AuthZForce. - * - * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software - * Foundation, either version 3 of the License, or (at your option) any later version. - * - * AuthZForce 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . - */ -package org.ow2.authzforce.core.pdp.impl; - -import java.beans.ConstructorProperties; -import java.io.File; -import java.io.OutputStream; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import javax.xml.bind.JAXBContext; -import javax.xml.bind.JAXBElement; -import javax.xml.bind.JAXBException; -import javax.xml.bind.Marshaller; -import javax.xml.bind.Unmarshaller; -import javax.xml.transform.Source; -import javax.xml.validation.Schema; - -import org.ow2.authzforce.core.xmlns.pdp.Pdp; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * PDP Engine XML configuration handler - * - */ -public class PdpModelHandler -{ - /** - * Location of PDP configuration schema - */ - public final static String CORE_XSD_LOCATION = "classpath:pdp.xsd"; - - /** - * Default location of XML catalog to resolve imported XML schemas in {@value PdpModelHandler#CORE_XSD_LOCATION} - */ - public final static String DEFAULT_CATALOG_LOCATION = "classpath:catalog.xml"; - - private final static Logger LOGGER = LoggerFactory.getLogger(PdpModelHandler.class); - - /** - * Supported JAXB type for root elements of XML configuration documents (e.g. files) - */ - public final static Class SUPPORTED_ROOT_CONF_ELEMENT_JAXB_TYPE = Pdp.class; - - private final Schema confSchema; - private final JAXBContext confJaxbCtx; - - /** - * Load Configuration model handler. Parameters here are locations to XSD files. Locations can be any resource string supported by Spring ResourceLoader. More info: - * http://docs.spring.io/spring/docs/current/spring-framework-reference/html/resources.html - * - * For example: classpath:com/myapp/aaa.xsd, file:///data/bbb.xsd, http://myserver/ccc.xsd... - * - * - * @param extensionXsdLocation - * location of user-defined extension XSD (may be null if no extension to load), if exists; in such XSD, there must be a XSD import for each extension schema. Only import the namespace, - * do not define the actual schema location here. Use the catalog specified by the catalogLocation parameter to specify the schema location. For example: - * - *

-	 * {@literal
-	 * 		   
-	 * 		  
-	 *            
-	 *            
-	 * 			}
-	 * 
- * - * @param catalogLocation - * location of XML catalog for resolving XSDs imported by the pdp.xsd (PDP configuration schema) and the extensions XSD specified as 'extensionXsdLocation' argument (may be null) - * - */ - @ConstructorProperties({ "catalogLocation", "extensionXsdLocation" }) - public PdpModelHandler(String catalogLocation, String extensionXsdLocation) - { - final List schemaLocations; - if (extensionXsdLocation == null) - { - schemaLocations = Collections.singletonList(CORE_XSD_LOCATION); - } else - { - schemaLocations = Arrays.asList(extensionXsdLocation, CORE_XSD_LOCATION); - } - - /* - * JAXB classes of extensions are generated separately from the extension base type XSD. Therefore no @XmlSeeAlso to link to the base type. Therefore any JAXB provider cannot (un)marshall - * documents using the extension base type XSD, unless it is provided with the list of the extra JAXB classes based on the new extension XSD. For instance, this is the case for JAXB providers - * used by REST/SOAP frameworks: Apache CXF, Metro, etc. So we need to add to the JAXBContext all the extensions' model (JAXB-generated) classes. These have been collected by the - * PdpExtensionLoader. - */ - final Set> jaxbBoundClassList = new HashSet>(PdpExtensionLoader.getExtensionJaxbClasses()); - LOGGER.debug("Final list of loaded extension models (JAXB classes): {}", jaxbBoundClassList); - - // Classes to be bound when creating new instance of JAXB context - jaxbBoundClassList.add(SUPPORTED_ROOT_CONF_ELEMENT_JAXB_TYPE); - try - { - confJaxbCtx = JAXBContext.newInstance(jaxbBoundClassList.toArray(new Class[jaxbBoundClassList.size()])); - LOGGER.debug("JAXB context for PDP configuration (un)marshalling: {}", confJaxbCtx); - } catch (JAXBException e) - { - throw new RuntimeException("Failed to initialize configuration unmarshaller", e); - } - - // Load schema for validating XML configurations - final String schemaHandlerCatalogLocation; - if (catalogLocation == null) - { - LOGGER.debug("No XML catalog location specified for PDP schema handler, using default: {}", DEFAULT_CATALOG_LOCATION); - schemaHandlerCatalogLocation = DEFAULT_CATALOG_LOCATION; - } else - { - LOGGER.debug("XML catalog location specified for PDP schema handler: {}", catalogLocation); - schemaHandlerCatalogLocation = catalogLocation; - } - - confSchema = SchemaHandler.createSchema(schemaLocations, schemaHandlerCatalogLocation); - } - - /** - * Unmarshall object from XML source - * - * @param src - * XML source - * @param clazz - * Class of object to be unmarshalled, must be a subclass (or the class itself) of {@value #SUPPORTED_ROOT_CONF_ELEMENT_JAXB_TYPE} - * @return object of class clazz - * @throws JAXBException - * if an error was encountered while unmarshalling the XML document in {@code src} into an instance of {@code clazz} - */ - public T unmarshal(Source src, Class clazz) throws JAXBException - { - if (!SUPPORTED_ROOT_CONF_ELEMENT_JAXB_TYPE.isAssignableFrom(clazz)) - { - throw new UnsupportedOperationException("XML configuration unmarshalling is not supported for " + clazz + "; supported JAXB type for root configuration elements is: " - + SUPPORTED_ROOT_CONF_ELEMENT_JAXB_TYPE); - } - - final Unmarshaller unmarshaller = confJaxbCtx.createUnmarshaller(); - unmarshaller.setSchema(confSchema); - final JAXBElement confRootElt = unmarshaller.unmarshal(src, clazz); - return confRootElt.getValue(); - } - - /** - * Saves full configuration (XML) - * - * @param conf - * configuration - * @param os - * output stream where to save - * @throws JAXBException - * error when marshalling the XML configuration to the output stream - */ - public void marshal(Pdp conf, OutputStream os) throws JAXBException - { - final Marshaller marshaller = confJaxbCtx.createMarshaller(); - marshaller.setSchema(confSchema); - marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); - marshaller.marshal(conf, os); - } - - /** - * Saves full configuration (XML) - * - * @param conf - * configuration - * @param f - * output file where to save - * @throws JAXBException - * error when marshalling the XML configuration to file - */ - public void marshal(Pdp conf, File f) throws JAXBException - { - final Marshaller marshaller = confJaxbCtx.createMarshaller(); - marshaller.setSchema(confSchema); - marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); - marshaller.marshal(conf, f); - } - -} +/** + * Copyright (C) 2011-2015 Thales Services SAS. + * + * This file is part of AuthZForce. + * + * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * AuthZForce 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . + */ +package org.ow2.authzforce.core.pdp.impl; + +import java.beans.ConstructorProperties; +import java.io.File; +import java.io.OutputStream; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBElement; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; +import javax.xml.bind.Unmarshaller; +import javax.xml.transform.Source; +import javax.xml.validation.Schema; + +import org.ow2.authzforce.core.xmlns.pdp.Pdp; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * PDP Engine XML configuration handler + * + * @author cdangerv + * @version $Id: $ + */ +public class PdpModelHandler +{ + /** + * Location of PDP configuration schema + */ + public final static String CORE_XSD_LOCATION = "classpath:pdp.xsd"; + + /** + * Default location of XML catalog to resolve imported XML schemas in {@value #CORE_XSD_LOCATION} + */ + public final static String DEFAULT_CATALOG_LOCATION = "classpath:catalog.xml"; + + private final static Logger LOGGER = LoggerFactory.getLogger(PdpModelHandler.class); + + /** + * Supported JAXB type for root elements of XML configuration documents (e.g. files) + */ + public final static Class SUPPORTED_ROOT_CONF_ELEMENT_JAXB_TYPE = Pdp.class; + + private final Schema confSchema; + private final JAXBContext confJaxbCtx; + + /** + * Load Configuration model handler. Parameters here are locations to XSD files. Locations can be any resource string supported by Spring ResourceLoader. More info: + * http://docs.spring.io/spring/docs/current/spring-framework-reference/html/resources.html + * + * For example: classpath:com/myapp/aaa.xsd, file:///data/bbb.xsd, http://myserver/ccc.xsd... + * + * @param extensionXsdLocation + * location of user-defined extension XSD (may be null if no extension to load), if exists; in such XSD, there must be a XSD import for each extension schema. Only import the namespace, + * do not define the actual schema location here. Use the catalog specified by the catalogLocation parameter to specify the schema location. For example: + * + *
+	 * {@literal
+	 * 		  
+	 * 		  
+	 *            
+	 *            
+	 * 			}
+	 * 
+ * @param catalogLocation + * location of XML catalog for resolving XSDs imported by the pdp.xsd (PDP configuration schema) and the extensions XSD specified as 'extensionXsdLocation' argument (may be null) + */ + @ConstructorProperties({ "catalogLocation", "extensionXsdLocation" }) + public PdpModelHandler(String catalogLocation, String extensionXsdLocation) + { + final List schemaLocations; + if (extensionXsdLocation == null) + { + schemaLocations = Collections.singletonList(CORE_XSD_LOCATION); + } else + { + schemaLocations = Arrays.asList(extensionXsdLocation, CORE_XSD_LOCATION); + } + + /* + * JAXB classes of extensions are generated separately from the extension base type XSD. Therefore no @XmlSeeAlso to link to the base type. Therefore any JAXB provider cannot (un)marshall + * documents using the extension base type XSD, unless it is provided with the list of the extra JAXB classes based on the new extension XSD. For instance, this is the case for JAXB providers + * used by REST/SOAP frameworks: Apache CXF, Metro, etc. So we need to add to the JAXBContext all the extensions' model (JAXB-generated) classes. These have been collected by the + * PdpExtensionLoader. + */ + final Set> jaxbBoundClassList = new HashSet>(PdpExtensionLoader.getExtensionJaxbClasses()); + LOGGER.debug("Final list of loaded extension models (JAXB classes): {}", jaxbBoundClassList); + + // Classes to be bound when creating new instance of JAXB context + jaxbBoundClassList.add(SUPPORTED_ROOT_CONF_ELEMENT_JAXB_TYPE); + try + { + confJaxbCtx = JAXBContext.newInstance(jaxbBoundClassList.toArray(new Class[jaxbBoundClassList.size()])); + LOGGER.debug("JAXB context for PDP configuration (un)marshalling: {}", confJaxbCtx); + } catch (JAXBException e) + { + throw new RuntimeException("Failed to initialize configuration unmarshaller", e); + } + + // Load schema for validating XML configurations + final String schemaHandlerCatalogLocation; + if (catalogLocation == null) + { + LOGGER.debug("No XML catalog location specified for PDP schema handler, using default: {}", DEFAULT_CATALOG_LOCATION); + schemaHandlerCatalogLocation = DEFAULT_CATALOG_LOCATION; + } else + { + LOGGER.debug("XML catalog location specified for PDP schema handler: {}", catalogLocation); + schemaHandlerCatalogLocation = catalogLocation; + } + + confSchema = SchemaHandler.createSchema(schemaLocations, schemaHandlerCatalogLocation); + } + + /** + * Unmarshall object from XML source + * + * @param src + * XML source + * @param clazz + * Class of object to be unmarshalled, must be a subclass (or the class itself) of the one defined by {@link #SUPPORTED_ROOT_CONF_ELEMENT_JAXB_TYPE}, i.e. {@link Pdp} + * @return object of class clazz + * @throws javax.xml.bind.JAXBException + * if an error was encountered while unmarshalling the XML document in {@code src} into an instance of {@code clazz} + * @param + * a T object. + */ + public T unmarshal(Source src, Class clazz) throws JAXBException + { + if (!SUPPORTED_ROOT_CONF_ELEMENT_JAXB_TYPE.isAssignableFrom(clazz)) + { + throw new UnsupportedOperationException("XML configuration unmarshalling is not supported for " + clazz + "; supported JAXB type for root configuration elements is: " + + SUPPORTED_ROOT_CONF_ELEMENT_JAXB_TYPE); + } + + final Unmarshaller unmarshaller = confJaxbCtx.createUnmarshaller(); + unmarshaller.setSchema(confSchema); + final JAXBElement confRootElt = unmarshaller.unmarshal(src, clazz); + return confRootElt.getValue(); + } + + /** + * Saves full configuration (XML) + * + * @param conf + * configuration + * @param os + * output stream where to save + * @throws javax.xml.bind.JAXBException + * error when marshalling the XML configuration to the output stream + */ + public void marshal(Pdp conf, OutputStream os) throws JAXBException + { + final Marshaller marshaller = confJaxbCtx.createMarshaller(); + marshaller.setSchema(confSchema); + marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); + marshaller.marshal(conf, os); + } + + /** + * Saves full configuration (XML) + * + * @param conf + * configuration + * @param f + * output file where to save + * @throws javax.xml.bind.JAXBException + * error when marshalling the XML configuration to file + */ + public void marshal(Pdp conf, File f) throws JAXBException + { + final Marshaller marshaller = confJaxbCtx.createMarshaller(); + marshaller.setSchema(confSchema); + marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); + marshaller.marshal(conf, f); + } + +} diff --git a/src/main/java/org/ow2/authzforce/core/pdp/impl/PepActionExpression.java b/src/main/java/org/ow2/authzforce/core/pdp/impl/PepActionExpression.java index 74cfd610..638ed937 100644 --- a/src/main/java/org/ow2/authzforce/core/pdp/impl/PepActionExpression.java +++ b/src/main/java/org/ow2/authzforce/core/pdp/impl/PepActionExpression.java @@ -1,166 +1,164 @@ -/** - * Copyright (C) 2012-2015 Thales Services SAS. - * - * This file is part of AuthZForce CE. - * - * AuthZForce CE is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software - * Foundation, either version 3 of the License, or (at your option) any later version. - * - * AuthZForce CE 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with AuthZForce CE. If not, see . - */ -package org.ow2.authzforce.core.pdp.impl; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import net.sf.saxon.s9api.XPathCompiler; -import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeAssignment; -import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeAssignmentExpression; -import oasis.names.tc.xacml._3_0.core.schema.wd_17.EffectType; - -import org.ow2.authzforce.core.pdp.api.EvaluationContext; -import org.ow2.authzforce.core.pdp.api.ExpressionFactory; -import org.ow2.authzforce.core.pdp.api.IndeterminateEvaluationException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * PEP action (obligation/advice) expression evaluator - * - * @param - * PEP action type in XACML/JAXB model (Obligation/Advice) - */ -public final class PepActionExpression -{ - private static final Logger LOGGER = LoggerFactory.getLogger(PepActionExpression.class); - - private String actionId; - private final transient JAXB_PEP_ACTION emptyPepAction; - private final transient List evaluatableAttributeAssignmentExpressions; - - private final PepActionFactory pepActionFactory; - - private final String infoPrefix; - - private final EffectType appliesTo; - - /** - * Constructor that takes all the data associated with an PEP action (obligation/advice) expression. - * - * @param pepActionFactory - * PEP action factory - * - * @param pepActionId - * the obligation's id - * @param appliesTo - * the type of decision to which the PEP action applies (ObligationExpression's FulfillOn / AdviceExpression's AppliesTo) - * @param jaxbAssignmentExps - * a List of AttributeAssignmentExpressions - * @param xPathCompiler - * XPath compiler corresponding to enclosing policy(set) default XPath version - * @param expFactory - * Expression factory for parsing/instantiating AttributeAssignment expressions - * @throws IllegalArgumentException - * one of the AttributeAssignmentExpressions' Expression is invalid - */ - public PepActionExpression(PepActionFactory pepActionFactory, String pepActionId, EffectType appliesTo, - List jaxbAssignmentExps, XPathCompiler xPathCompiler, ExpressionFactory expFactory) throws IllegalArgumentException - { - this.actionId = pepActionId; - this.appliesTo = appliesTo; - - if (jaxbAssignmentExps == null || jaxbAssignmentExps.isEmpty()) - { - emptyPepAction = pepActionFactory.getInstance(null, pepActionId); - this.evaluatableAttributeAssignmentExpressions = Collections.emptyList(); - } else - { - emptyPepAction = null; - this.evaluatableAttributeAssignmentExpressions = new ArrayList<>(jaxbAssignmentExps.size()); - for (final AttributeAssignmentExpression jaxbAttrAssignExp : jaxbAssignmentExps) - { - final AttributeAssignmentExpressionEvaluator attrAssignExp; - try - { - attrAssignExp = new AttributeAssignmentExpressionEvaluator(jaxbAttrAssignExp, xPathCompiler, expFactory); - } catch (IllegalArgumentException e) - { - throw new IllegalArgumentException("Invalid AttributeAssignmentExpression[@AttributeId=" + jaxbAttrAssignExp.getAttributeId() - + "]/Expression", e); - } - - this.evaluatableAttributeAssignmentExpressions.add(attrAssignExp); - } - } - - this.pepActionFactory = pepActionFactory; - this.infoPrefix = pepActionFactory.getActionXmlElementName() + "Expression[@" + pepActionFactory.getActionXmlElementName() + "=" + actionId + "]"; - } - - /** - * The type of decision to which the PEP action applies (ObligationExpression's FulfillOn / AdviceExpression's AppliesTo) - * - * @return appliesTo/fulfillOn property - */ - public EffectType getAppliesTo() - { - return appliesTo; - } - - /** - * ID of the PEP action (ObligationId / AdviceId) - * - * @return action ID - */ - public String getActionId() - { - return this.actionId; - } - - /** - * Evaluates to a PEP action (obligation/advice). - * - * @param context - * evaluation context - * - * @return an instance of a PEP action in JAXB model (JAXB Obligation/Advice) - * - * @throws IndeterminateEvaluationException - * if any of the attribute assignment expressions evaluates to "Indeterminate" (see XACML 3.0 core spec, section 7.18) - */ - public JAXB_PEP_ACTION evaluate(EvaluationContext context) throws IndeterminateEvaluationException - { - // if no assignmentExpression - if (this.emptyPepAction != null) - { - return this.emptyPepAction; - } - - // else there are assignmentExpressions - final List assignments = new ArrayList<>(); - for (final AttributeAssignmentExpressionEvaluator attrAssignmentExpr : this.evaluatableAttributeAssignmentExpressions) - { - /* - * Section 5.39 of XACML 3.0 core spec says there may be multiple AttributeAssignments resulting from one AttributeAssignmentExpression - */ - final List attrAssignsFromExpr; - try - { - attrAssignsFromExpr = attrAssignmentExpr.evaluate(context); - LOGGER.debug("{}/{} -> {}", this.infoPrefix, attrAssignmentExpr, attrAssignsFromExpr); - } catch (IndeterminateEvaluationException e) - { - throw new IndeterminateEvaluationException(infoPrefix + ": Error evaluating AttributeAssignmentExpression[@AttributeId=" - + attrAssignmentExpr.getAttributeId() + "]/Expression", e.getStatusCode(), e); - } - - assignments.addAll(attrAssignsFromExpr); - } - - return pepActionFactory.getInstance(assignments, actionId); - } -} +/** + * Copyright (C) 2012-2015 Thales Services SAS. + * + * This file is part of AuthZForce CE. + * + * AuthZForce CE is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * AuthZForce CE 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with AuthZForce CE. If not, see . + */ +package org.ow2.authzforce.core.pdp.impl; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import net.sf.saxon.s9api.XPathCompiler; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeAssignment; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeAssignmentExpression; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.EffectType; + +import org.ow2.authzforce.core.pdp.api.EvaluationContext; +import org.ow2.authzforce.core.pdp.api.ExpressionFactory; +import org.ow2.authzforce.core.pdp.api.IndeterminateEvaluationException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * PEP action (obligation/advice) expression evaluator + * + * @param + * PEP action type in XACML/JAXB model (Obligation/Advice) + * @author cdangerv + * @version $Id: $ + */ +public final class PepActionExpression +{ + private static final Logger LOGGER = LoggerFactory.getLogger(PepActionExpression.class); + + private String actionId; + private final transient JAXB_PEP_ACTION emptyPepAction; + private final transient List evaluatableAttributeAssignmentExpressions; + + private final PepActionFactory pepActionFactory; + + private final String infoPrefix; + + private final EffectType appliesTo; + + /** + * Constructor that takes all the data associated with an PEP action (obligation/advice) expression. + * + * @param pepActionFactory + * PEP action factory + * @param pepActionId + * the obligation's id + * @param appliesTo + * the type of decision to which the PEP action applies (ObligationExpression's FulfillOn / AdviceExpression's AppliesTo) + * @param jaxbAssignmentExps + * a List of AttributeAssignmentExpressions + * @param xPathCompiler + * XPath compiler corresponding to enclosing policy(set) default XPath version + * @param expFactory + * Expression factory for parsing/instantiating AttributeAssignment expressions + * @throws java.lang.IllegalArgumentException + * one of the AttributeAssignmentExpressions' Expression is invalid + */ + public PepActionExpression(PepActionFactory pepActionFactory, String pepActionId, EffectType appliesTo, List jaxbAssignmentExps, + XPathCompiler xPathCompiler, ExpressionFactory expFactory) throws IllegalArgumentException + { + this.actionId = pepActionId; + this.appliesTo = appliesTo; + + if (jaxbAssignmentExps == null || jaxbAssignmentExps.isEmpty()) + { + emptyPepAction = pepActionFactory.getInstance(null, pepActionId); + this.evaluatableAttributeAssignmentExpressions = Collections.emptyList(); + } else + { + emptyPepAction = null; + this.evaluatableAttributeAssignmentExpressions = new ArrayList<>(jaxbAssignmentExps.size()); + for (final AttributeAssignmentExpression jaxbAttrAssignExp : jaxbAssignmentExps) + { + final AttributeAssignmentExpressionEvaluator attrAssignExp; + try + { + attrAssignExp = new AttributeAssignmentExpressionEvaluator(jaxbAttrAssignExp, xPathCompiler, expFactory); + } catch (IllegalArgumentException e) + { + throw new IllegalArgumentException("Invalid AttributeAssignmentExpression[@AttributeId=" + jaxbAttrAssignExp.getAttributeId() + "]/Expression", e); + } + + this.evaluatableAttributeAssignmentExpressions.add(attrAssignExp); + } + } + + this.pepActionFactory = pepActionFactory; + this.infoPrefix = pepActionFactory.getActionXmlElementName() + "Expression[@" + pepActionFactory.getActionXmlElementName() + "=" + actionId + "]"; + } + + /** + * The type of decision to which the PEP action applies (ObligationExpression's FulfillOn / AdviceExpression's AppliesTo) + * + * @return appliesTo/fulfillOn property + */ + public EffectType getAppliesTo() + { + return appliesTo; + } + + /** + * ID of the PEP action (ObligationId / AdviceId) + * + * @return action ID + */ + public String getActionId() + { + return this.actionId; + } + + /** + * Evaluates to a PEP action (obligation/advice). + * + * @param context + * evaluation context + * @return an instance of a PEP action in JAXB model (JAXB Obligation/Advice) + * @throws org.ow2.authzforce.core.pdp.api.IndeterminateEvaluationException + * if any of the attribute assignment expressions evaluates to "Indeterminate" (see XACML 3.0 core spec, section 7.18) + */ + public JAXB_PEP_ACTION evaluate(EvaluationContext context) throws IndeterminateEvaluationException + { + // if no assignmentExpression + if (this.emptyPepAction != null) + { + return this.emptyPepAction; + } + + // else there are assignmentExpressions + final List assignments = new ArrayList<>(); + for (final AttributeAssignmentExpressionEvaluator attrAssignmentExpr : this.evaluatableAttributeAssignmentExpressions) + { + /* + * Section 5.39 of XACML 3.0 core spec says there may be multiple AttributeAssignments resulting from one AttributeAssignmentExpression + */ + final List attrAssignsFromExpr; + try + { + attrAssignsFromExpr = attrAssignmentExpr.evaluate(context); + LOGGER.debug("{}/{} -> {}", this.infoPrefix, attrAssignmentExpr, attrAssignsFromExpr); + } catch (IndeterminateEvaluationException e) + { + throw new IndeterminateEvaluationException(infoPrefix + ": Error evaluating AttributeAssignmentExpression[@AttributeId=" + attrAssignmentExpr.getAttributeId() + "]/Expression", + e.getStatusCode(), e); + } + + assignments.addAll(attrAssignsFromExpr); + } + + return pepActionFactory.getInstance(assignments, actionId); + } +} diff --git a/src/main/java/org/ow2/authzforce/core/pdp/impl/PepActionExpressions.java b/src/main/java/org/ow2/authzforce/core/pdp/impl/PepActionExpressions.java index 544c9cb1..191697f6 100644 --- a/src/main/java/org/ow2/authzforce/core/pdp/impl/PepActionExpressions.java +++ b/src/main/java/org/ow2/authzforce/core/pdp/impl/PepActionExpressions.java @@ -1,291 +1,289 @@ -/** - * Copyright (C) 2011-2015 Thales Services SAS. - * - * This file is part of AuthZForce. - * - * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software - * Foundation, either version 3 of the License, or (at your option) any later version. - * - * AuthZForce 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . - */ -package org.ow2.authzforce.core.pdp.impl; - -import java.util.ArrayList; -import java.util.List; - -import net.sf.saxon.s9api.XPathCompiler; -import oasis.names.tc.xacml._3_0.core.schema.wd_17.Advice; -import oasis.names.tc.xacml._3_0.core.schema.wd_17.AdviceExpression; -import oasis.names.tc.xacml._3_0.core.schema.wd_17.AdviceExpressions; -import oasis.names.tc.xacml._3_0.core.schema.wd_17.EffectType; -import oasis.names.tc.xacml._3_0.core.schema.wd_17.Obligation; -import oasis.names.tc.xacml._3_0.core.schema.wd_17.ObligationExpression; -import oasis.names.tc.xacml._3_0.core.schema.wd_17.ObligationExpressions; - -import org.ow2.authzforce.core.pdp.api.EvaluationContext; -import org.ow2.authzforce.core.pdp.api.ExpressionFactory; -import org.ow2.authzforce.core.pdp.api.IndeterminateEvaluationException; -import org.ow2.authzforce.core.pdp.api.PepActions; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Low-level interface to a list of PEP action (obligation/advice) expressions - * - */ -public interface PepActionExpressions -{ - /** - * PepActionExpressions factory - * - * @param - * type of created instance - */ - interface Factory - { - T getInstance(XPathCompiler xPathCompiler, ExpressionFactory expressionFactory); - } - - /** - * Effect-specific obligation/advice expressions. Only expressions applying to such effect are allowed to be added to the list. - * - */ - final class EffectSpecific - { - // effect to which obligation and advice below apply - private final EffectType effect; - private final List> obligationExpList = new ArrayList<>(); - private final List> adviceExpList = new ArrayList<>(); - - /** - * @param effect - * Effect to which all obligation/advice expressions must apply - */ - public EffectSpecific(EffectType effect) - { - this.effect = effect; - } - - /** - * Adds an ObligationExpression to the list only if matching the the effect argument to {@link EffectSpecific#EffectSpecific(EffectType)} - * - * @param obligationExpressionEvaluator - * ObligationExpressionEvaluator - * @return true iff {@code obligationExpression} actually added to the expressions, i.e. fulfillOn matches the effect argument to - * {@link EffectSpecific#EffectSpecific(EffectType)} - */ - public boolean addObligationExpression(PepActionExpression obligationExpressionEvaluator) - { - if (obligationExpressionEvaluator.getAppliesTo() != effect) - { - return false; - } - - return obligationExpList.add(obligationExpressionEvaluator); - } - - /** - * Adds an AdviceExpression to the list only if matching the the effect argument to {@link EffectSpecific#EffectSpecific(EffectType)} - * - * @param adviceExpressionEvaluator - * AdviceExpressionEvaluator - * @return true iff {@code adviceExpression} actually added to the expressions, i.e. appliesTo matches the effect argument to - * {@link EffectSpecific#EffectSpecific(EffectType)} - */ - public boolean addAdviceExpression(PepActionExpression adviceExpressionEvaluator) - { - if (adviceExpressionEvaluator.getAppliesTo() != effect) - { - return false; - } - - return adviceExpList.add(adviceExpressionEvaluator); - } - - /** - * Effect-specific ObligationExpressions - * - * @return the effect-specific ObligationExpressions - */ - public List> getObligationExpressions() - { - return this.obligationExpList; - } - - /** - * Effect-specific AdviceExpressions - * - * @return the effect-specific AdviceExpressions - */ - public List> getAdviceExpressions() - { - return this.adviceExpList; - } - - /** - * Get Effect to be matched by all expressions - * - * @return effect - */ - public EffectType getEffect() - { - return effect; - } - } - - /** - * Helps parse/evaluate PEP action expressions (ObligationExpressions, AdviceExpressions) - * - */ - final class Helper - { - private static final Logger LOGGER = LoggerFactory.getLogger(Helper.class); - - private Helper() - { - } - - /** - * Parse XACML ObligationExpressions/AdviceExpressions - * - * @param jaxbObligationExpressions - * XACML ObligationExpressions - * @param jaxbAdviceExpressions - * XACML AdviceExpressions - * @param xPathCompiler - * XPath compiler corresponding to enclosing policy(set) default XPath version - * @param expFactory - * XACML Expression factory - * @param actionExpressionsFactory - * PepActionExpressions factory - * @return PEP action expressions - * @throws IllegalArgumentException - * if there is an invalid obligation/advice expression - */ - public static T parseActionExpressions(ObligationExpressions jaxbObligationExpressions, - AdviceExpressions jaxbAdviceExpressions, XPathCompiler xPathCompiler, ExpressionFactory expFactory, - PepActionExpressions.Factory actionExpressionsFactory) throws IllegalArgumentException - { - final T actionExpressions = actionExpressionsFactory.getInstance(xPathCompiler, expFactory); - if (jaxbObligationExpressions != null) - { - final List jaxbObligationExpList = jaxbObligationExpressions.getObligationExpressions(); - for (final ObligationExpression jaxbObligationExp : jaxbObligationExpList) - { - try - { - actionExpressions.add(jaxbObligationExp); - } catch (IllegalArgumentException e) - { - throw new IllegalArgumentException("One of the ObligationExpression[@ObligationId='" + jaxbObligationExp.getObligationId() - + "']/AttributeAssignmentExpression/Expression elements is invalid", e); - } - } - } - - if (jaxbAdviceExpressions != null) - { - final List jaxbAdviceExpList = jaxbAdviceExpressions.getAdviceExpressions(); - for (final AdviceExpression jaxbAdviceExp : jaxbAdviceExpList) - { - try - { - actionExpressions.add(jaxbAdviceExp); - } catch (IllegalArgumentException e) - { - throw new IllegalArgumentException("One of the AdviceExpression[@AdviceId='" + jaxbAdviceExp.getAdviceId() - + "']/AttributeAssignmentExpression/Expression elements is invalid", e); - } - } - } - - return actionExpressions; - } - - private static List evaluate(List> pepActionExpressions, EvaluationContext context, - String pepActionXmlTagName) throws IndeterminateEvaluationException - { - final List obligations; - if (pepActionExpressions.isEmpty()) - { - obligations = null; - } else - { - obligations = new ArrayList<>(pepActionExpressions.size()); - for (final PepActionExpression obligationExp : pepActionExpressions) - { - final PEP_ACTION obligation; - try - { - obligation = obligationExp.evaluate(context); - if (LOGGER.isDebugEnabled()) - { - LOGGER.debug("{}Expression[@{}Id={}] -> {}", pepActionXmlTagName, pepActionXmlTagName, obligationExp.getActionId(), obligation); - } - } catch (IndeterminateEvaluationException e) - { - throw new IndeterminateEvaluationException("Error evaluating one of the " + pepActionXmlTagName + "Expression[@" + pepActionXmlTagName - + "Id=" + obligationExp.getActionId() + "]/AttributeAssignmentExpression/Expression elements", e.getStatusCode(), e); - } - - obligations.add(obligation); - } - } - - return obligations; - } - - public static PepActions evaluate(PepActionExpressions.EffectSpecific pepActionExpressions, EvaluationContext context, PepActions pepActionsToUpdate) - throws IndeterminateEvaluationException - { - final List newObligations = evaluate(pepActionExpressions.getObligationExpressions(), context, - BasePepActions.OBLIGATION_FACTORY.getActionXmlElementName()); - final List newAdvices = evaluate(pepActionExpressions.getAdviceExpressions(), context, - BasePepActions.ADVICE_FACTORY.getActionXmlElementName()); - if (pepActionsToUpdate == null) - { - return new BasePepActions(newObligations, newAdvices); - } - - pepActionsToUpdate.merge(newObligations, newAdvices); - return pepActionsToUpdate; - } - } - - /** - * Adds a XACML ObligationExpression to the list - * - * @param jaxbObligationExp - * XACML ObligationExpression - * @throws IllegalArgumentException - * if invalid expression - */ - void add(ObligationExpression jaxbObligationExp) throws IllegalArgumentException; - - /** - * Adds a XACML AdviceExpression to the list - * - * @param jaxbAdviceExp - * XACML ObligationExpression - * @throws IllegalArgumentException - * if invalid expression - */ - void add(AdviceExpression jaxbAdviceExp) throws IllegalArgumentException; - - /** - * Gets all the expressions added with {@link #add(oasis.names.tc.xacml._3_0.core.schema.wd_17.ObligationExpression)} - * - * @return list of ObligationExpressions - */ - List> getObligationExpressionList(); - - /** - * Gets all the expressions added with {@link #add(oasis.names.tc.xacml._3_0.core.schema.wd_17.AdviceExpression)} - * - * @return list of AdviceExpressions - */ - List> getAdviceExpressionList(); -} +/** + * Copyright (C) 2011-2015 Thales Services SAS. + * + * This file is part of AuthZForce. + * + * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * AuthZForce 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . + */ +package org.ow2.authzforce.core.pdp.impl; + +import java.util.ArrayList; +import java.util.List; + +import net.sf.saxon.s9api.XPathCompiler; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.Advice; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.AdviceExpression; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.AdviceExpressions; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.EffectType; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.Obligation; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.ObligationExpression; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.ObligationExpressions; + +import org.ow2.authzforce.core.pdp.api.EvaluationContext; +import org.ow2.authzforce.core.pdp.api.ExpressionFactory; +import org.ow2.authzforce.core.pdp.api.IndeterminateEvaluationException; +import org.ow2.authzforce.core.pdp.api.PepActions; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Low-level interface to a list of PEP action (obligation/advice) expressions + * + * @author cdangerv + * @version $Id: $ + */ +public interface PepActionExpressions +{ + /** + * PepActionExpressions factory + * + * @param + * type of created instance + */ + interface Factory + { + T getInstance(XPathCompiler xPathCompiler, ExpressionFactory expressionFactory); + } + + /** + * Effect-specific obligation/advice expressions. Only expressions applying to such effect are allowed to be added to the list. + * + */ + final class EffectSpecific + { + // effect to which obligation and advice below apply + private final EffectType effect; + private final List> obligationExpList = new ArrayList<>(); + private final List> adviceExpList = new ArrayList<>(); + + /** + * @param effect + * Effect to which all obligation/advice expressions must apply + */ + public EffectSpecific(EffectType effect) + { + this.effect = effect; + } + + /** + * Adds an ObligationExpression to the list only if matching the {@link EffectType} to which this instance is specific, as defined by the constructor argument + * + * @param obligationExpressionEvaluator + * ObligationExpressionEvaluator + * @return true iff {@code obligationExpression} actually added to the expressions, i.e. fulfillOn matches the {@link EffectType} to which this instance is specific, as defined by the + * constructor argument + */ + public boolean addObligationExpression(PepActionExpression obligationExpressionEvaluator) + { + if (obligationExpressionEvaluator.getAppliesTo() != effect) + { + return false; + } + + return obligationExpList.add(obligationExpressionEvaluator); + } + + /** + * Adds an AdviceExpression to the list only if matching the {@link EffectType} to which this instance is specific, as defined by the constructor argument + * + * @param adviceExpressionEvaluator + * AdviceExpressionEvaluator + * @return true iff {@code adviceExpression} actually added to the PEP action expressions, i.e. appliesTo matches the {@link EffectType} to which this instance is specific, as defined by the + * constructor argument + */ + public boolean addAdviceExpression(PepActionExpression adviceExpressionEvaluator) + { + if (adviceExpressionEvaluator.getAppliesTo() != effect) + { + return false; + } + + return adviceExpList.add(adviceExpressionEvaluator); + } + + /** + * Effect-specific ObligationExpressions + * + * @return the effect-specific ObligationExpressions + */ + public List> getObligationExpressions() + { + return this.obligationExpList; + } + + /** + * Effect-specific AdviceExpressions + * + * @return the effect-specific AdviceExpressions + */ + public List> getAdviceExpressions() + { + return this.adviceExpList; + } + + /** + * Get Effect to be matched by all expressions + * + * @return effect + */ + public EffectType getEffect() + { + return effect; + } + } + + /** + * Helps parse/evaluate PEP action expressions (ObligationExpressions, AdviceExpressions) + * + */ + final class Helper + { + private static final Logger LOGGER = LoggerFactory.getLogger(Helper.class); + + private Helper() + { + } + + /** + * Parse XACML ObligationExpressions/AdviceExpressions + * + * @param jaxbObligationExpressions + * XACML ObligationExpressions + * @param jaxbAdviceExpressions + * XACML AdviceExpressions + * @param xPathCompiler + * XPath compiler corresponding to enclosing policy(set) default XPath version + * @param expFactory + * XACML Expression factory + * @param actionExpressionsFactory + * PepActionExpressions factory + * @return PEP action expressions + * @throws IllegalArgumentException + * if there is an invalid obligation/advice expression + */ + public static T parseActionExpressions(ObligationExpressions jaxbObligationExpressions, AdviceExpressions jaxbAdviceExpressions, XPathCompiler xPathCompiler, + ExpressionFactory expFactory, PepActionExpressions.Factory actionExpressionsFactory) throws IllegalArgumentException + { + final T actionExpressions = actionExpressionsFactory.getInstance(xPathCompiler, expFactory); + if (jaxbObligationExpressions != null) + { + final List jaxbObligationExpList = jaxbObligationExpressions.getObligationExpressions(); + for (final ObligationExpression jaxbObligationExp : jaxbObligationExpList) + { + try + { + actionExpressions.add(jaxbObligationExp); + } catch (IllegalArgumentException e) + { + throw new IllegalArgumentException("One of the ObligationExpression[@ObligationId='" + jaxbObligationExp.getObligationId() + + "']/AttributeAssignmentExpression/Expression elements is invalid", e); + } + } + } + + if (jaxbAdviceExpressions != null) + { + final List jaxbAdviceExpList = jaxbAdviceExpressions.getAdviceExpressions(); + for (final AdviceExpression jaxbAdviceExp : jaxbAdviceExpList) + { + try + { + actionExpressions.add(jaxbAdviceExp); + } catch (IllegalArgumentException e) + { + throw new IllegalArgumentException("One of the AdviceExpression[@AdviceId='" + jaxbAdviceExp.getAdviceId() + "']/AttributeAssignmentExpression/Expression elements is invalid", + e); + } + } + } + + return actionExpressions; + } + + private static List evaluate(List> pepActionExpressions, EvaluationContext context, String pepActionXmlTagName) + throws IndeterminateEvaluationException + { + final List obligations; + if (pepActionExpressions.isEmpty()) + { + obligations = null; + } else + { + obligations = new ArrayList<>(pepActionExpressions.size()); + for (final PepActionExpression obligationExp : pepActionExpressions) + { + final PEP_ACTION obligation; + try + { + obligation = obligationExp.evaluate(context); + if (LOGGER.isDebugEnabled()) + { + LOGGER.debug("{}Expression[@{}Id={}] -> {}", pepActionXmlTagName, pepActionXmlTagName, obligationExp.getActionId(), obligation); + } + } catch (IndeterminateEvaluationException e) + { + throw new IndeterminateEvaluationException("Error evaluating one of the " + pepActionXmlTagName + "Expression[@" + pepActionXmlTagName + "Id=" + obligationExp.getActionId() + + "]/AttributeAssignmentExpression/Expression elements", e.getStatusCode(), e); + } + + obligations.add(obligation); + } + } + + return obligations; + } + + public static PepActions evaluate(PepActionExpressions.EffectSpecific pepActionExpressions, EvaluationContext context, PepActions pepActionsToUpdate) throws IndeterminateEvaluationException + { + final List newObligations = evaluate(pepActionExpressions.getObligationExpressions(), context, BasePepActions.OBLIGATION_FACTORY.getActionXmlElementName()); + final List newAdvices = evaluate(pepActionExpressions.getAdviceExpressions(), context, BasePepActions.ADVICE_FACTORY.getActionXmlElementName()); + if (pepActionsToUpdate == null) + { + return new BasePepActions(newObligations, newAdvices); + } + + pepActionsToUpdate.merge(newObligations, newAdvices); + return pepActionsToUpdate; + } + } + + /** + * Adds a XACML ObligationExpression to the list + * + * @param jaxbObligationExp + * XACML ObligationExpression + * @throws java.lang.IllegalArgumentException + * if invalid expression + */ + void add(ObligationExpression jaxbObligationExp) throws IllegalArgumentException; + + /** + * Adds a XACML AdviceExpression to the list + * + * @param jaxbAdviceExp + * XACML ObligationExpression + * @throws java.lang.IllegalArgumentException + * if invalid expression + */ + void add(AdviceExpression jaxbAdviceExp) throws IllegalArgumentException; + + /** + * Gets all the expressions added with {@link #add(oasis.names.tc.xacml._3_0.core.schema.wd_17.ObligationExpression)} + * + * @return list of ObligationExpressions + */ + List> getObligationExpressionList(); + + /** + * Gets all the expressions added with {@link #add(oasis.names.tc.xacml._3_0.core.schema.wd_17.AdviceExpression)} + * + * @return list of AdviceExpressions + */ + List> getAdviceExpressionList(); +} diff --git a/src/main/java/org/ow2/authzforce/core/pdp/impl/PepActionFactory.java b/src/main/java/org/ow2/authzforce/core/pdp/impl/PepActionFactory.java index a9315f07..28d062f7 100644 --- a/src/main/java/org/ow2/authzforce/core/pdp/impl/PepActionFactory.java +++ b/src/main/java/org/ow2/authzforce/core/pdp/impl/PepActionFactory.java @@ -1,50 +1,50 @@ -/** - * Copyright (C) 2012-2015 Thales Services SAS. - * - * This file is part of AuthZForce CE. - * - * AuthZForce CE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AuthZForce CE 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AuthZForce CE. If not, see . - */ -package org.ow2.authzforce.core.pdp.impl; - -import java.util.List; - -import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeAssignment; - -/** - * PEP action (obligation/advice) factory - * - * @param - * JAXB-annotated PEP action type - */ -public interface PepActionFactory -{ - /** - * Creates instance of PEP action (obligation/advice) - * - * @param attributeAssignments - * XML/JAXB AttributeAssignments in the PEP action - * @param actionId - * action ID (ObligationId, AdviceId) - * @return PEP action - */ - JAXB_T getInstance(List attributeAssignments, String actionId); - - /** - * Get name of PEP Action element in XACML model, e.g. 'Obligation' - * - * @return action element name - */ - String getActionXmlElementName(); -} +/** + * Copyright (C) 2012-2015 Thales Services SAS. + * + * This file is part of AuthZForce CE. + * + * AuthZForce CE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AuthZForce CE 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AuthZForce CE. If not, see . + */ +package org.ow2.authzforce.core.pdp.impl; + +import java.util.List; + +import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeAssignment; + +/** + * PEP action (obligation/advice) factory + * + * @param + * JAXB-annotated PEP action type + */ +public interface PepActionFactory +{ + /** + * Creates instance of PEP action (obligation/advice) + * + * @param attributeAssignments + * XML/JAXB AttributeAssignments in the PEP action + * @param actionId + * action ID (ObligationId, AdviceId) + * @return PEP action + */ + JAXB_T getInstance(List attributeAssignments, String actionId); + + /** + * Get name of PEP Action element in XACML model, e.g. 'Obligation' + * + * @return action element name + */ + String getActionXmlElementName(); +} diff --git a/src/main/java/org/ow2/authzforce/core/pdp/impl/SchemaHandler.java b/src/main/java/org/ow2/authzforce/core/pdp/impl/SchemaHandler.java index e6db3f33..687ad7a2 100644 --- a/src/main/java/org/ow2/authzforce/core/pdp/impl/SchemaHandler.java +++ b/src/main/java/org/ow2/authzforce/core/pdp/impl/SchemaHandler.java @@ -1,559 +1,560 @@ -/** - * Copyright (C) 2011-2015 Thales Services SAS. - * - * This file is part of AuthZForce. - * - * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software - * Foundation, either version 3 of the License, or (at your option) any later version. - * - * AuthZForce 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . - */ -package org.ow2.authzforce.core.pdp.impl; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; -import java.net.MalformedURLException; -import java.net.URISyntaxException; -import java.net.URL; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; -import java.util.concurrent.CopyOnWriteArraySet; - -import javax.xml.XMLConstants; -import javax.xml.transform.Source; -import javax.xml.transform.stream.StreamSource; -import javax.xml.validation.Schema; -import javax.xml.validation.SchemaFactory; - -import org.apache.xml.resolver.Catalog; -import org.apache.xml.resolver.CatalogManager; -import org.apache.xml.resolver.tools.CatalogResolver; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.util.ResourceUtils; -import org.w3c.dom.ls.LSInput; -import org.w3c.dom.ls.LSResourceResolver; -import org.xml.sax.ErrorHandler; -import org.xml.sax.SAXException; -import org.xml.sax.SAXNotRecognizedException; -import org.xml.sax.SAXNotSupportedException; -import org.xml.sax.SAXParseException; - -/** - * - * XML schema handler that can load schema file(s) from location(s) supported by {@link ResourceUtils} using any OASIS catalog at any location supported by {@link ResourceUtils} as well. - * - */ -public class SchemaHandler -{ - private static final class XmlSchemaResourceResolver implements LSResourceResolver - { - private final static Logger _LOGGER = LoggerFactory.getLogger(XmlSchemaResourceResolver.class); - - private final String catalogLocation; - private final OASISCatalogManager catalogResolver; - - private XmlSchemaResourceResolver(String catalogLocation, OASISCatalogManager catalogResolver) - { - this.catalogLocation = catalogLocation; - this.catalogResolver = catalogResolver; - } - - @Override - public LSInput resolveResource(String type, String namespaceURI, String publicId, String systemId, String baseURI) - { - try - { - String resolvedLocation = catalogResolver.resolveSystem(systemId); - _LOGGER.debug("resolveSystem(systemId = {}) -> {}", systemId, resolvedLocation); - - if (resolvedLocation == null) - { - resolvedLocation = catalogResolver.resolveURI(namespaceURI); - _LOGGER.debug("resolveURI(namespaceURI = {}) -> {}", namespaceURI, resolvedLocation); - } - if (resolvedLocation == null) - { - resolvedLocation = catalogResolver.resolvePublic(publicId, systemId); - if (_LOGGER.isDebugEnabled()) - { - _LOGGER.debug("resolvePublic(publicId = {}, systemId = {}) -> {}", new Object[] { publicId, systemId, resolvedLocation }); - } - } - if (resolvedLocation != null) - { - final URL resourceURL = ResourceUtils.getURL(resolvedLocation); - if (resourceURL != null) - { - return new LSInputImpl(publicId, systemId, resourceURL.openStream()); - } - } - } catch (IOException ex) - { - final String errMsg = "Unable to resolve schema-required entity with XML catalog (location='" + catalogLocation + "'): type=" + type + ", namespaceURI=" + namespaceURI - + ", publicId='" + publicId + "', systemId='" + systemId + "', baseURI='" + baseURI + "'"; - throw new RuntimeException(errMsg, ex); - } - - return null; - } - } - - private static class SchemaParsingErrorHandler implements ErrorHandler - { - - @Override - public final void warning(SAXParseException exception) throws SAXException - { - throw exception; - } - - @Override - public final void error(SAXParseException exception) throws SAXException - { - throw exception; - } - - @Override - public final void fatalError(SAXParseException exception) throws SAXException - { - throw exception; - } - } - - /** - * This is quite similar to org.apache.cxf.catalog.OASISCatalogManager, except it is much simplified as we don't need as many features. We are not using CXF's OASISCatalogManager class directly - * because it is part of cxf-core which drags many classes and dependencies on CXF we don't need. It would make more sense if OASISCatalogManager was part of a cxf common utility package, but it - * is not the case as of writing (December 2014). - */ - private static class OASISCatalogManager - { - private final static Logger _LOGGER = LoggerFactory.getLogger(OASISCatalogManager.class); - - private final CatalogResolver resolver; - private final Catalog catalog; - private final Set loadedCatalogs = new CopyOnWriteArraySet<>(); - - private OASISCatalogManager() - { - resolver = getResolver(); - if (resolver == null) - { - throw new IllegalArgumentException("Error creating org.apache.xml.resolver.tools.CatalogResolver for OASIS CatalogManager"); - } - - catalog = getCatalog(resolver); - } - - private static Catalog getCatalog(CatalogResolver resolver) - { - assert resolver != null; - try - { - return resolver.getCatalog(); - } catch (Throwable t) - { - _LOGGER.error("Error getting OASIS org.apache.xml.resolver.Catalog from CatalogResolver", t); - } - - return null; - } - - private static CatalogResolver getResolver() - { - try - { - final CatalogManager catalogManager = new CatalogManager(); - if (_LOGGER.isDebugEnabled()) - { - // lowest debug level for logging all messages - catalogManager.debug.setDebug(0); - } - - catalogManager.setUseStaticCatalog(false); - catalogManager.setIgnoreMissingProperties(true); - final CatalogResolver catalogResolver = new CatalogResolver(catalogManager) - { - @Override - public String getResolvedEntity(String publicId, String systemId) - { - final String s = super.getResolvedEntity(publicId, systemId); - if (s != null && s.startsWith("classpath:")) - { - try - { - final URL resourceURL = ResourceUtils.getURL(s); - if (resourceURL != null) - { - return resourceURL.toExternalForm(); - } - } catch (IOException e) - { - _LOGGER.warn("Error resolving resource needed by org.apache.xml.resolver.CatalogResolver for OASIS CatalogManager with URL: {}", e); - } - } - return s; - } - }; - return catalogResolver; - } catch (Throwable t) - { - _LOGGER.error("Error getting org.apache.xml.resolver.CatalogResolver for OASIS CatalogManager", t); - } - return null; - } - - private final void loadCatalog(URL catalogURL) throws IOException - { - if (!loadedCatalogs.contains(catalogURL.toString())) - { - if ("file".equals(catalogURL.getProtocol())) - { - try - { - final File file = new File(catalogURL.toURI()); - if (!file.exists()) - { - throw new FileNotFoundException(file.getAbsolutePath()); - } - } catch (URISyntaxException e) - { - _LOGGER.warn("Error resolving XML catalog URL ({}) to a file", catalogURL, e); - } - } - - if (catalog == null) - { - _LOGGER.warn("Catalog found at {} but no org.apache.xml.resolver.CatalogManager was found. Check the classpatch for an xmlresolver jar.", catalogURL); - } else - { - catalog.parseCatalog(catalogURL); - - loadedCatalogs.add(catalogURL.toString()); - } - } - } - - /** - * Creates new {@link OASISCatalogManager} with default constructor - * - * @return new instance of {@link OASISCatalogManager} (so not null) - */ - private static OASISCatalogManager getContextCatalog() - { - return new OASISCatalogManager(); - } - - /** - * Creates new {@link OASISCatalogManager} - * - * @return new instance of {@link OASISCatalogManager} (so not null) - */ - private static OASISCatalogManager getCatalogManager() - { - return getContextCatalog(); - } - - private String resolveSystem(String sys) throws MalformedURLException, IOException - { - if (catalog == null) - { - return null; - } - return catalog.resolveSystem(sys); - } - - private String resolveURI(String uri) throws MalformedURLException, IOException - { - if (catalog == null) - { - return null; - } - return catalog.resolveURI(uri); - } - - private String resolvePublic(String uri, String parent) throws MalformedURLException, IOException - { - if (resolver == null) - { - return null; - } - return catalog.resolvePublic(uri, parent); - } - } - - private static class LSInputImpl implements LSInput - { - - private final String publicId; - private final String systemId; - private final InputStream byteStream; - - private LSInputImpl(String publicId, String systemId, InputStream byteStream) - { - this.publicId = publicId; - this.systemId = systemId; - this.byteStream = byteStream; - } - - @Override - public final InputStream getByteStream() - { - return byteStream; - } - - @Override - public final String getSystemId() - { - return systemId; - } - - @Override - public final String getPublicId() - { - return publicId; - } - - @Override - public final Reader getCharacterStream() - { - /* - * No character stream, only byte streams are allowed. Do not throw exception, otherwise the resolution of the resource fails, even if byte stream OK - */ - return null; - // throw new UnsupportedOperationException(); - } - - @Override - public final void setCharacterStream(Reader characterStream) - { - throw new UnsupportedOperationException(); - } - - @Override - public final void setByteStream(InputStream byteStream) - { - throw new UnsupportedOperationException(); - } - - @Override - public final String getStringData() - { - /* - * Not supported. Do not throw exception, otherwise the resolution of the resource fails. - */ - return null; - // throw new UnsupportedOperationException(); - } - - @Override - public final void setStringData(String stringData) - { - throw new UnsupportedOperationException(); - } - - @Override - public final void setSystemId(String systemId) - { - throw new UnsupportedOperationException(); - } - - @Override - public final void setPublicId(String publicId) - { - throw new UnsupportedOperationException(); - } - - @Override - public final String getBaseURI() - { - /* - * No base URI, only absolute URIs are allowed. Do not throw exception if no base URI, otherwise the resolution of the resource fails, even for absolute URIs - */ - return null; - // throw new UnsupportedOperationException(); - } - - @Override - public final void setBaseURI(String baseURI) - { - throw new UnsupportedOperationException(); - } - - @Override - public final String getEncoding() - { - /* - * No encoding override, only absolute URIs are allowed. Do not throw exception if no base URI, otherwise the resolution of the resource fails, even if encoding specified in other way - */ - return null; - // throw new UnsupportedOperationException(); - } - - @Override - public final void setEncoding(String encoding) - { - throw new UnsupportedOperationException(); - } - - @Override - public final boolean getCertifiedText() - { - throw new UnsupportedOperationException(); - } - - @Override - public final void setCertifiedText(boolean certifiedText) - { - throw new UnsupportedOperationException(); - } - - } - - private Schema schema; - private String catalogLocation; - - /** - * Default empty constructor, needed for instantiation by Spring framework - */ - public SchemaHandler() - { - } - - /** - * Sets (Spring-supported) locations to XML schema files - * - * @param locations - * XML schema locations - */ - public void setSchemaLocations(List locations) - { - schema = createSchema(locations, catalogLocation); - } - - /** - * Sets (Spring-supported) locations to XML catalog files - * - * @param location - * XML catalog location - */ - public void setCatalogLocation(String location) - { - this.catalogLocation = location; - } - - /** - * Get schema used by this handler - * - * @return XML schema - */ - public Schema getSchema() - { - return schema; - } - - /** - * Creates schema from locations to XML schema files and catalog file - * - * - * @param schemaLocations - * XML schema locations - * @param catalogLocation - * XML catalog location - * @return XML validation schema - */ - public static Schema createSchema(List schemaLocations, final String catalogLocation) - { - /* - * This is mostly similar to org.apache.cxf.jaxrs.utils.schemas.SchemaHandler#createSchema(), except we are using Spring ResourceUtils class to get Resource URLs and we don't use any Bus - * object. We are not using CXF's SchemaHandler class directly because it is part of cxf-rt-frontend-jaxrs which drags many dependencies on CXF we don't need, the full CXF JAX-RS framework - * actually. It would make more sense if SchemaHandler was part of some cxf common utility package, but it is not the case as of writing (December 2014). - */ - - final SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); - try - { - factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); - } catch (SAXNotRecognizedException e) - { - throw new RuntimeException("Error configuring the XML schema factory for secure processing", e); - } catch (SAXNotSupportedException e) - { - throw new RuntimeException("Error configuring the XML schema factory for secure processing", e); - } - - final ErrorHandler schemaErrorHandler = new SchemaParsingErrorHandler(); - factory.setErrorHandler(schemaErrorHandler); - final List sources = new ArrayList<>(schemaLocations.size()); - try - { - for (String schemaLocation : schemaLocations) - { - final URL schemaURL; - try - { - schemaURL = ResourceUtils.getURL(schemaLocation); - } catch (FileNotFoundException e) - { - throw new RuntimeException("No resource found for XML schema location: " + schemaLocation, e); - } - - final Reader r = new BufferedReader(new InputStreamReader(schemaURL.openStream(), "UTF-8")); - final StreamSource source = new StreamSource(r); - source.setSystemId(schemaURL.toString()); - sources.add(source); - } - } catch (IOException ex) - { - throw new RuntimeException("Failed to load XML schemas: " + schemaLocations, ex); - } - - if (sources.isEmpty()) - { - return null; - } - - if (catalogLocation != null) - { - final OASISCatalogManager catalogResolver = OASISCatalogManager.getCatalogManager(); - final URL catalogURL; - try - { - catalogURL = ResourceUtils.getURL(catalogLocation); - } catch (FileNotFoundException e) - { - throw new RuntimeException("No resource found for XML catalog file location: " + catalogLocation, e); - } - - try - { - catalogResolver.loadCatalog(catalogURL); - factory.setResourceResolver(new XmlSchemaResourceResolver(catalogLocation, catalogResolver)); - } catch (IOException ex) - { - throw new RuntimeException("Catalog located at '" + catalogLocation + "' can not be loaded", ex); - } - } - - final Schema s; - try - { - s = factory.newSchema(sources.toArray(new Source[sources.size()])); - } catch (SAXException e) - { - throw new RuntimeException("Failed to load XML schemas: " + schemaLocations, e); - } - - return s; - - } -} +/** + * Copyright (C) 2011-2015 Thales Services SAS. + * + * This file is part of AuthZForce. + * + * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * AuthZForce 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . + */ +package org.ow2.authzforce.core.pdp.impl; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.net.MalformedURLException; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.concurrent.CopyOnWriteArraySet; + +import javax.xml.XMLConstants; +import javax.xml.transform.Source; +import javax.xml.transform.stream.StreamSource; +import javax.xml.validation.Schema; +import javax.xml.validation.SchemaFactory; + +import org.apache.xml.resolver.Catalog; +import org.apache.xml.resolver.CatalogManager; +import org.apache.xml.resolver.tools.CatalogResolver; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.util.ResourceUtils; +import org.w3c.dom.ls.LSInput; +import org.w3c.dom.ls.LSResourceResolver; +import org.xml.sax.ErrorHandler; +import org.xml.sax.SAXException; +import org.xml.sax.SAXNotRecognizedException; +import org.xml.sax.SAXNotSupportedException; +import org.xml.sax.SAXParseException; + +/** + * + * XML schema handler that can load schema file(s) from location(s) supported by {@link ResourceUtils} using any OASIS catalog at any location supported by {@link ResourceUtils} as well. + * + * @author cdangerv + * @version $Id: $ + */ +public class SchemaHandler +{ + private static final class XmlSchemaResourceResolver implements LSResourceResolver + { + private final static Logger _LOGGER = LoggerFactory.getLogger(XmlSchemaResourceResolver.class); + + private final String catalogLocation; + private final OASISCatalogManager catalogResolver; + + private XmlSchemaResourceResolver(String catalogLocation, OASISCatalogManager catalogResolver) + { + this.catalogLocation = catalogLocation; + this.catalogResolver = catalogResolver; + } + + @Override + public LSInput resolveResource(String type, String namespaceURI, String publicId, String systemId, String baseURI) + { + try + { + String resolvedLocation = catalogResolver.resolveSystem(systemId); + _LOGGER.debug("resolveSystem(systemId = {}) -> {}", systemId, resolvedLocation); + + if (resolvedLocation == null) + { + resolvedLocation = catalogResolver.resolveURI(namespaceURI); + _LOGGER.debug("resolveURI(namespaceURI = {}) -> {}", namespaceURI, resolvedLocation); + } + if (resolvedLocation == null) + { + resolvedLocation = catalogResolver.resolvePublic(publicId, systemId); + if (_LOGGER.isDebugEnabled()) + { + _LOGGER.debug("resolvePublic(publicId = {}, systemId = {}) -> {}", new Object[] { publicId, systemId, resolvedLocation }); + } + } + if (resolvedLocation != null) + { + final URL resourceURL = ResourceUtils.getURL(resolvedLocation); + if (resourceURL != null) + { + return new LSInputImpl(publicId, systemId, resourceURL.openStream()); + } + } + } catch (IOException ex) + { + final String errMsg = "Unable to resolve schema-required entity with XML catalog (location='" + catalogLocation + "'): type=" + type + ", namespaceURI=" + namespaceURI + + ", publicId='" + publicId + "', systemId='" + systemId + "', baseURI='" + baseURI + "'"; + throw new RuntimeException(errMsg, ex); + } + + return null; + } + } + + private static class SchemaParsingErrorHandler implements ErrorHandler + { + + @Override + public final void warning(SAXParseException exception) throws SAXException + { + throw exception; + } + + @Override + public final void error(SAXParseException exception) throws SAXException + { + throw exception; + } + + @Override + public final void fatalError(SAXParseException exception) throws SAXException + { + throw exception; + } + } + + /** + * This is quite similar to org.apache.cxf.catalog.OASISCatalogManager, except it is much simplified as we don't need as many features. We are not using CXF's OASISCatalogManager class directly + * because it is part of cxf-core which drags many classes and dependencies on CXF we don't need. It would make more sense if OASISCatalogManager was part of a cxf common utility package, but it + * is not the case as of writing (December 2014). + */ + private static class OASISCatalogManager + { + private final static Logger _LOGGER = LoggerFactory.getLogger(OASISCatalogManager.class); + + private final CatalogResolver resolver; + private final Catalog catalog; + private final Set loadedCatalogs = new CopyOnWriteArraySet<>(); + + private OASISCatalogManager() + { + resolver = getResolver(); + if (resolver == null) + { + throw new IllegalArgumentException("Error creating org.apache.xml.resolver.tools.CatalogResolver for OASIS CatalogManager"); + } + + catalog = getCatalog(resolver); + } + + private static Catalog getCatalog(CatalogResolver resolver) + { + assert resolver != null; + try + { + return resolver.getCatalog(); + } catch (Throwable t) + { + _LOGGER.error("Error getting OASIS org.apache.xml.resolver.Catalog from CatalogResolver", t); + } + + return null; + } + + private static CatalogResolver getResolver() + { + try + { + final CatalogManager catalogManager = new CatalogManager(); + if (_LOGGER.isDebugEnabled()) + { + // lowest debug level for logging all messages + catalogManager.debug.setDebug(0); + } + + catalogManager.setUseStaticCatalog(false); + catalogManager.setIgnoreMissingProperties(true); + final CatalogResolver catalogResolver = new CatalogResolver(catalogManager) + { + @Override + public String getResolvedEntity(String publicId, String systemId) + { + final String s = super.getResolvedEntity(publicId, systemId); + if (s != null && s.startsWith("classpath:")) + { + try + { + final URL resourceURL = ResourceUtils.getURL(s); + if (resourceURL != null) + { + return resourceURL.toExternalForm(); + } + } catch (IOException e) + { + _LOGGER.warn("Error resolving resource needed by org.apache.xml.resolver.CatalogResolver for OASIS CatalogManager with URL: {}", e); + } + } + return s; + } + }; + return catalogResolver; + } catch (Throwable t) + { + _LOGGER.error("Error getting org.apache.xml.resolver.CatalogResolver for OASIS CatalogManager", t); + } + return null; + } + + private final void loadCatalog(URL catalogURL) throws IOException + { + if (!loadedCatalogs.contains(catalogURL.toString())) + { + if ("file".equals(catalogURL.getProtocol())) + { + try + { + final File file = new File(catalogURL.toURI()); + if (!file.exists()) + { + throw new FileNotFoundException(file.getAbsolutePath()); + } + } catch (URISyntaxException e) + { + _LOGGER.warn("Error resolving XML catalog URL ({}) to a file", catalogURL, e); + } + } + + if (catalog == null) + { + _LOGGER.warn("Catalog found at {} but no org.apache.xml.resolver.CatalogManager was found. Check the classpatch for an xmlresolver jar.", catalogURL); + } else + { + catalog.parseCatalog(catalogURL); + + loadedCatalogs.add(catalogURL.toString()); + } + } + } + + /** + * Creates new {@link OASISCatalogManager} with default constructor + * + * @return new instance of {@link OASISCatalogManager} (so not null) + */ + private static OASISCatalogManager getContextCatalog() + { + return new OASISCatalogManager(); + } + + /** + * Creates new {@link OASISCatalogManager} + * + * @return new instance of {@link OASISCatalogManager} (so not null) + */ + private static OASISCatalogManager getCatalogManager() + { + return getContextCatalog(); + } + + private String resolveSystem(String sys) throws MalformedURLException, IOException + { + if (catalog == null) + { + return null; + } + return catalog.resolveSystem(sys); + } + + private String resolveURI(String uri) throws MalformedURLException, IOException + { + if (catalog == null) + { + return null; + } + return catalog.resolveURI(uri); + } + + private String resolvePublic(String uri, String parent) throws MalformedURLException, IOException + { + if (resolver == null) + { + return null; + } + return catalog.resolvePublic(uri, parent); + } + } + + private static class LSInputImpl implements LSInput + { + + private final String publicId; + private final String systemId; + private final InputStream byteStream; + + private LSInputImpl(String publicId, String systemId, InputStream byteStream) + { + this.publicId = publicId; + this.systemId = systemId; + this.byteStream = byteStream; + } + + @Override + public final InputStream getByteStream() + { + return byteStream; + } + + @Override + public final String getSystemId() + { + return systemId; + } + + @Override + public final String getPublicId() + { + return publicId; + } + + @Override + public final Reader getCharacterStream() + { + /* + * No character stream, only byte streams are allowed. Do not throw exception, otherwise the resolution of the resource fails, even if byte stream OK + */ + return null; + // throw new UnsupportedOperationException(); + } + + @Override + public final void setCharacterStream(Reader characterStream) + { + throw new UnsupportedOperationException(); + } + + @Override + public final void setByteStream(InputStream byteStream) + { + throw new UnsupportedOperationException(); + } + + @Override + public final String getStringData() + { + /* + * Not supported. Do not throw exception, otherwise the resolution of the resource fails. + */ + return null; + // throw new UnsupportedOperationException(); + } + + @Override + public final void setStringData(String stringData) + { + throw new UnsupportedOperationException(); + } + + @Override + public final void setSystemId(String systemId) + { + throw new UnsupportedOperationException(); + } + + @Override + public final void setPublicId(String publicId) + { + throw new UnsupportedOperationException(); + } + + @Override + public final String getBaseURI() + { + /* + * No base URI, only absolute URIs are allowed. Do not throw exception if no base URI, otherwise the resolution of the resource fails, even for absolute URIs + */ + return null; + // throw new UnsupportedOperationException(); + } + + @Override + public final void setBaseURI(String baseURI) + { + throw new UnsupportedOperationException(); + } + + @Override + public final String getEncoding() + { + /* + * No encoding override, only absolute URIs are allowed. Do not throw exception if no base URI, otherwise the resolution of the resource fails, even if encoding specified in other way + */ + return null; + // throw new UnsupportedOperationException(); + } + + @Override + public final void setEncoding(String encoding) + { + throw new UnsupportedOperationException(); + } + + @Override + public final boolean getCertifiedText() + { + throw new UnsupportedOperationException(); + } + + @Override + public final void setCertifiedText(boolean certifiedText) + { + throw new UnsupportedOperationException(); + } + + } + + private Schema schema; + private String catalogLocation; + + /** + * Default empty constructor, needed for instantiation by Spring framework + */ + public SchemaHandler() + { + } + + /** + * Sets (Spring-supported) locations to XML schema files + * + * @param locations + * XML schema locations + */ + public void setSchemaLocations(List locations) + { + schema = createSchema(locations, catalogLocation); + } + + /** + * Sets (Spring-supported) locations to XML catalog files + * + * @param location + * XML catalog location + */ + public void setCatalogLocation(String location) + { + this.catalogLocation = location; + } + + /** + * Get schema used by this handler + * + * @return XML schema + */ + public Schema getSchema() + { + return schema; + } + + /** + * Creates schema from locations to XML schema files and catalog file + * + * @param schemaLocations + * XML schema locations + * @param catalogLocation + * XML catalog location + * @return XML validation schema + */ + public static Schema createSchema(List schemaLocations, final String catalogLocation) + { + /* + * This is mostly similar to org.apache.cxf.jaxrs.utils.schemas.SchemaHandler#createSchema(), except we are using Spring ResourceUtils class to get Resource URLs and we don't use any Bus + * object. We are not using CXF's SchemaHandler class directly because it is part of cxf-rt-frontend-jaxrs which drags many dependencies on CXF we don't need, the full CXF JAX-RS framework + * actually. It would make more sense if SchemaHandler was part of some cxf common utility package, but it is not the case as of writing (December 2014). + */ + + final SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); + try + { + factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); + } catch (SAXNotRecognizedException e) + { + throw new RuntimeException("Error configuring the XML schema factory for secure processing", e); + } catch (SAXNotSupportedException e) + { + throw new RuntimeException("Error configuring the XML schema factory for secure processing", e); + } + + final ErrorHandler schemaErrorHandler = new SchemaParsingErrorHandler(); + factory.setErrorHandler(schemaErrorHandler); + final List sources = new ArrayList<>(schemaLocations.size()); + try + { + for (String schemaLocation : schemaLocations) + { + final URL schemaURL; + try + { + schemaURL = ResourceUtils.getURL(schemaLocation); + } catch (FileNotFoundException e) + { + throw new RuntimeException("No resource found for XML schema location: " + schemaLocation, e); + } + + final Reader r = new BufferedReader(new InputStreamReader(schemaURL.openStream(), "UTF-8")); + final StreamSource source = new StreamSource(r); + source.setSystemId(schemaURL.toString()); + sources.add(source); + } + } catch (IOException ex) + { + throw new RuntimeException("Failed to load XML schemas: " + schemaLocations, ex); + } + + if (sources.isEmpty()) + { + return null; + } + + if (catalogLocation != null) + { + final OASISCatalogManager catalogResolver = OASISCatalogManager.getCatalogManager(); + final URL catalogURL; + try + { + catalogURL = ResourceUtils.getURL(catalogLocation); + } catch (FileNotFoundException e) + { + throw new RuntimeException("No resource found for XML catalog file location: " + catalogLocation, e); + } + + try + { + catalogResolver.loadCatalog(catalogURL); + factory.setResourceResolver(new XmlSchemaResourceResolver(catalogLocation, catalogResolver)); + } catch (IOException ex) + { + throw new RuntimeException("Catalog located at '" + catalogLocation + "' can not be loaded", ex); + } + } + + final Schema s; + try + { + s = factory.newSchema(sources.toArray(new Source[sources.size()])); + } catch (SAXException e) + { + throw new RuntimeException("Failed to load XML schemas: " + schemaLocations, e); + } + + return s; + + } +} diff --git a/src/main/java/org/ow2/authzforce/core/pdp/impl/TargetEvaluator.java b/src/main/java/org/ow2/authzforce/core/pdp/impl/TargetEvaluator.java index 9de4eef9..84d28750 100644 --- a/src/main/java/org/ow2/authzforce/core/pdp/impl/TargetEvaluator.java +++ b/src/main/java/org/ow2/authzforce/core/pdp/impl/TargetEvaluator.java @@ -28,7 +28,9 @@ import org.slf4j.LoggerFactory; /** * Represents the TargetType XML type in XACML. - * + * + * @author cdangerv + * @version $Id: $ */ public class TargetEvaluator { @@ -43,16 +45,15 @@ public class TargetEvaluator /** * Instantiates Target (evaluator) from XACML-Schema-derived Target. - * + * * @param jaxbTarget * XACML-schema-derived JAXB Target * @param xPathCompiler * XPath compiler corresponding to enclosing policy(set) default XPath version * @param expFactory * Expression factory - * @throws IllegalArgumentException + * @throws java.lang.IllegalArgumentException * if one of the child AnyOf elements is invalid - * */ public TargetEvaluator(Target jaxbTarget, XPathCompiler xPathCompiler, ExpressionFactory expFactory) throws IllegalArgumentException { @@ -83,18 +84,17 @@ public class TargetEvaluator /** * Determines whether this Target matches the input request (whether it is applicable). If any of the AnyOf doesn't match the request context - * so it's a NO_MATCH result. Here is the table shown in the specification: + * so it's a NO_MATCH result. Here is the table shown in the specification: * values value * All Match? Match? * At Least one "No Match" No Match? * Otherwise Indeterminate? * Also if Target empty (no AnyOf), return "Match" - * + * * @param context * the representation of the request - * * @return true if and only if Match (else No-match) - * @throws IndeterminateEvaluationException + * @throws org.ow2.authzforce.core.pdp.api.IndeterminateEvaluationException * if Indetermiante (error evaluating target) */ public boolean match(EvaluationContext context) throws IndeterminateEvaluationException diff --git a/src/main/java/org/ow2/authzforce/core/pdp/impl/combining/BaseCombiningAlgRegistry.java b/src/main/java/org/ow2/authzforce/core/pdp/impl/combining/BaseCombiningAlgRegistry.java index a99c1cb0..a626a433 100644 --- a/src/main/java/org/ow2/authzforce/core/pdp/impl/combining/BaseCombiningAlgRegistry.java +++ b/src/main/java/org/ow2/authzforce/core/pdp/impl/combining/BaseCombiningAlgRegistry.java @@ -1,65 +1,76 @@ -/** - * Copyright (C) 2011-2015 Thales Services SAS. - * - * This file is part of AuthZForce. - * - * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software - * Foundation, either version 3 of the License, or (at your option) any later version. - * - * AuthZForce 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . - */ -package org.ow2.authzforce.core.pdp.impl.combining; - -import java.util.Set; - -import org.ow2.authzforce.core.pdp.api.CombiningAlg; -import org.ow2.authzforce.core.pdp.api.CombiningAlgRegistry; -import org.ow2.authzforce.core.pdp.api.Decidable; -import org.ow2.authzforce.core.pdp.impl.BasePdpExtensionRegistry; - -/** - * This is a com.thalesgroup.authzforce.core.test.basic implementation of CombiningAlgRegistry. - */ -public class BaseCombiningAlgRegistry extends BasePdpExtensionRegistry> implements CombiningAlgRegistry -{ - protected BaseCombiningAlgRegistry(Set> algorithms) - { - super(CombiningAlg.class, algorithms); - } - - /** - * @param baseRegistry - * parent registry from which this inherits all entries - * @see BasePdpExtensionRegistry#BasePdpExtensionRegistry(Class, BasePdpExtensionRegistry) - */ - public BaseCombiningAlgRegistry(BasePdpExtensionRegistry> baseRegistry) - { - super(CombiningAlg.class, baseRegistry); - } - - /** - * Default constructor. - */ - public BaseCombiningAlgRegistry() - { - super(CombiningAlg.class); - } - - @Override - public CombiningAlg getAlgorithm(String algId, Class combinedEltType) throws IllegalArgumentException - { - final CombiningAlg alg = this.getExtension(algId); - if (alg.getCombinedElementType().isAssignableFrom(combinedEltType)) - { - return (CombiningAlg) alg; - } - - // wrong type of alg - throw new IllegalArgumentException("Registered combining algorithm for ID=" + algId + " combines instances of type '" + alg.getCombinedElementType() - + "' which is not compatible (not same or supertype) with requested type of combined elements : " + combinedEltType); - } - -} +/** + * Copyright (C) 2011-2015 Thales Services SAS. + * + * This file is part of AuthZForce. + * + * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * AuthZForce 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . + */ +package org.ow2.authzforce.core.pdp.impl.combining; + +import java.util.Set; + +import org.ow2.authzforce.core.pdp.api.CombiningAlg; +import org.ow2.authzforce.core.pdp.api.CombiningAlgRegistry; +import org.ow2.authzforce.core.pdp.api.Decidable; +import org.ow2.authzforce.core.pdp.impl.BasePdpExtensionRegistry; + +/** + * This is a com.thalesgroup.authzforce.core.test.basic implementation of CombiningAlgRegistry. + * + * @author cdangerv + * @version $Id: $ + */ +public class BaseCombiningAlgRegistry extends BasePdpExtensionRegistry> implements CombiningAlgRegistry +{ + /** + *

Constructor for BaseCombiningAlgRegistry.

+ * + * @param algorithms a {@link java.util.Set} object. + */ + protected BaseCombiningAlgRegistry(Set> algorithms) + { + super(CombiningAlg.class, algorithms); + } + + /** + *

Constructor for BaseCombiningAlgRegistry.

+ * + * @param baseRegistry + * parent registry from which this inherits all entries + * @see BasePdpExtensionRegistry#BasePdpExtensionRegistry(Class, BasePdpExtensionRegistry) + */ + public BaseCombiningAlgRegistry(BasePdpExtensionRegistry> baseRegistry) + { + super(CombiningAlg.class, baseRegistry); + } + + /** + * Default constructor. + */ + public BaseCombiningAlgRegistry() + { + super(CombiningAlg.class); + } + + /** {@inheritDoc} */ + @Override + public CombiningAlg getAlgorithm(String algId, Class combinedEltType) throws IllegalArgumentException + { + final CombiningAlg alg = this.getExtension(algId); + if (alg.getCombinedElementType().isAssignableFrom(combinedEltType)) + { + return (CombiningAlg) alg; + } + + // wrong type of alg + throw new IllegalArgumentException("Registered combining algorithm for ID=" + algId + " combines instances of type '" + alg.getCombinedElementType() + + "' which is not compatible (not same or supertype) with requested type of combined elements : " + combinedEltType); + } + +} diff --git a/src/main/java/org/ow2/authzforce/core/pdp/impl/combining/CombiningAlgSet.java b/src/main/java/org/ow2/authzforce/core/pdp/impl/combining/CombiningAlgSet.java index ee1e381d..b1d65f20 100644 --- a/src/main/java/org/ow2/authzforce/core/pdp/impl/combining/CombiningAlgSet.java +++ b/src/main/java/org/ow2/authzforce/core/pdp/impl/combining/CombiningAlgSet.java @@ -1,72 +1,75 @@ -/** - * Copyright (C) 2011-2015 Thales Services SAS. - * - * This file is part of AuthZForce. - * - * AuthZForce is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AuthZForce 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AuthZForce. If not, see . - */ -package org.ow2.authzforce.core.pdp.impl.combining; - -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; - -import org.ow2.authzforce.core.pdp.api.CombiningAlg; -import org.ow2.authzforce.core.pdp.api.Decidable; - -/** - * Combining algorithm set. Allows to group combining algorithms, especially when it is actually the same generic algorithm but with different IDs, such as most standard algorithms which are the same - * for policy combining and rule combining algorithm IDs. - * - * TODO: consider making it a PdpExtension like FunctionSet, or generic PdpExtensionSet - */ -public class CombiningAlgSet -{ - private final Set> algs; - - /** - * Creates set from multiple combining algorithms - * - * @param algorithms - * XACML policy/rule combining algorithms added to the set - */ - public CombiningAlgSet(CombiningAlg... algorithms) - { - this(new HashSet<>(Arrays.asList(algorithms))); - } - - /** - * Creates a set as a copy of an existing set - * - * @param algorithms - * XACML policy/rule combining algorithms added to the set - */ - public CombiningAlgSet(Set> algorithms) - { - this.algs = Collections.unmodifiableSet(algorithms); - } - - /** - * Returns a single instance of each of the functions supported by some class. The Set must contain instances of Function, and it must be both non-null and non-empty. It - * may contain only a single Function. - * - * @return the functions members of this group - */ - public Set> getSupportedAlgorithms() - { - return algs; - } - -} +/** + * Copyright (C) 2011-2015 Thales Services SAS. + * + * This file is part of AuthZForce. + * + * AuthZForce is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AuthZForce 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AuthZForce. If not, see . + */ +package org.ow2.authzforce.core.pdp.impl.combining; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import org.ow2.authzforce.core.pdp.api.CombiningAlg; +import org.ow2.authzforce.core.pdp.api.Decidable; + +/** + * Combining algorithm set. Allows to group combining algorithms, especially when it is actually the same generic algorithm but with different IDs, such as most standard algorithms which are the same + * for policy combining and rule combining algorithm IDs. + * + * TODO: consider making it a PdpExtension like FunctionSet, or generic PdpExtensionSet + * + * @author cdangerv + * @version $Id: $ + */ +public class CombiningAlgSet +{ + private final Set> algs; + + /** + * Creates set from multiple combining algorithms + * + * @param algorithms + * XACML policy/rule combining algorithms added to the set + */ + public CombiningAlgSet(CombiningAlg... algorithms) + { + this(new HashSet<>(Arrays.asList(algorithms))); + } + + /** + * Creates a set as a copy of an existing set + * + * @param algorithms + * XACML policy/rule combining algorithms added to the set + */ + public CombiningAlgSet(Set> algorithms) + { + this.algs = Collections.unmodifiableSet(algorithms); + } + + /** + * Returns a single instance of each of the functions supported by some class. The Set must contain instances of Function, and it must be both non-null and non-empty. It + * may contain only a single Function. + * + * @return the functions members of this group + */ + public Set> getSupportedAlgorithms() + { + return algs; + } + +} diff --git a/src/main/java/org/ow2/authzforce/core/pdp/impl/combining/DenyOverridesAlg.java b/src/main/java/org/ow2/authzforce/core/pdp/impl/combining/DenyOverridesAlg.java index dc2656d9..69eedca8 100644 --- a/src/main/java/org/ow2/authzforce/core/pdp/impl/combining/DenyOverridesAlg.java +++ b/src/main/java/org/ow2/authzforce/core/pdp/impl/combining/DenyOverridesAlg.java @@ -31,6 +31,9 @@ import org.ow2.authzforce.core.pdp.impl.BaseDecisionResult; * This is the standard XACML 3.0 Deny-Overrides policy/rule combining algorithm. It allows a single evaluation of Deny * to take precedence over any number of permit, not applicable or indeterminate results. Note that since this * implementation does an ordered evaluation, this class also supports the Ordered-Deny-Overrides-algorithm. + * + * @author cdangerv + * @version $Id: $ */ public final class DenyOverridesAlg extends BaseCombiningAlg { @@ -181,6 +184,7 @@ public final class DenyOverridesAlg extends BaseCombiningAlg super(algId, Decidable.class); } + /** {@inheritDoc} */ @Override public CombiningAlg.Evaluator getInstance(List> params, List combinedElements) throws UnsupportedOperationException, IllegalArgumentException diff --git a/src/main/java/org/ow2/authzforce/core/pdp/impl/combining/DenyUnlessPermitAlg.java b/src/main/java/org/ow2/authzforce/core/pdp/impl/combining/DenyUnlessPermitAlg.java index 925e6741..564432e3 100644 --- a/src/main/java/org/ow2/authzforce/core/pdp/impl/combining/DenyUnlessPermitAlg.java +++ b/src/main/java/org/ow2/authzforce/core/pdp/impl/combining/DenyUnlessPermitAlg.java @@ -1,99 +1,102 @@ -/** - * Copyright (C) 2011-2015 Thales Services SAS. - * - * This file is part of AuthZForce. - * - * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software - * Foundation, either version 3 of the License, or (at your option) any later version. - * - * AuthZForce 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . - */ -package org.ow2.authzforce.core.pdp.impl.combining; - -import java.util.List; - -import org.ow2.authzforce.core.pdp.api.BaseCombiningAlg; -import org.ow2.authzforce.core.pdp.api.CombiningAlg; -import org.ow2.authzforce.core.pdp.api.CombiningAlgParameter; -import org.ow2.authzforce.core.pdp.api.Decidable; -import org.ow2.authzforce.core.pdp.api.DecisionResult; -import org.ow2.authzforce.core.pdp.api.EvaluationContext; -import org.ow2.authzforce.core.pdp.impl.BaseDecisionResult; - -import oasis.names.tc.xacml._3_0.core.schema.wd_17.DecisionType; - -/** - * Deny-unless-permit combining algorithm - * - */ -public final class DenyUnlessPermitAlg extends BaseCombiningAlg -{ - - private static class Evaluator implements CombiningAlg.Evaluator - { - - private final List combinedElements; - - private Evaluator(List combinedElements) - { - this.combinedElements = combinedElements; - } - - @Override - public DecisionResult eval(EvaluationContext context) - { - DecisionResult combinedDenyResult = null; - for (Decidable combinedElement : combinedElements) - { - // make sure that the policy matches the context - final DecisionResult policyResult = combinedElement.evaluate(context); - final DecisionType decision = policyResult.getDecision(); - switch (decision) - { - case PERMIT: - return policyResult; - case DENY: - // merge result (obligations/advice/mached policy IDs) - if (combinedDenyResult == null) - { - combinedDenyResult = policyResult; - } else - { - combinedDenyResult.merge(policyResult.getPepActions(), policyResult.getApplicablePolicyIdList()); - } - break; - default: - continue; - } - } - - return combinedDenyResult == null ? BaseDecisionResult.DENY : combinedDenyResult; - } - - } - - @Override - public CombiningAlg.Evaluator getInstance(List> params, List combinedElements) throws UnsupportedOperationException, IllegalArgumentException - { - return new Evaluator(combinedElements); - } - - /** - * The standard URIs used to identify this algorithm; first one is for policy combinging, second one for rule combining. - */ - static final String[] SUPPORTED_IDENTIFIERS = { "urn:oasis:names:tc:xacml:3.0:policy-combining-algorithm:deny-unless-permit", "urn:oasis:names:tc:xacml:3.0:rule-combining-algorithm:deny-unless-permit" }; - - /** - * Supported algorithms - */ - public static final CombiningAlgSet SET = new CombiningAlgSet(new DenyUnlessPermitAlg(SUPPORTED_IDENTIFIERS[0]), new DenyUnlessPermitAlg(SUPPORTED_IDENTIFIERS[1])); - - private DenyUnlessPermitAlg(String algId) - { - super(algId, Decidable.class); - } - -} +/** + * Copyright (C) 2011-2015 Thales Services SAS. + * + * This file is part of AuthZForce. + * + * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * AuthZForce 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . + */ +package org.ow2.authzforce.core.pdp.impl.combining; + +import java.util.List; + +import org.ow2.authzforce.core.pdp.api.BaseCombiningAlg; +import org.ow2.authzforce.core.pdp.api.CombiningAlg; +import org.ow2.authzforce.core.pdp.api.CombiningAlgParameter; +import org.ow2.authzforce.core.pdp.api.Decidable; +import org.ow2.authzforce.core.pdp.api.DecisionResult; +import org.ow2.authzforce.core.pdp.api.EvaluationContext; +import org.ow2.authzforce.core.pdp.impl.BaseDecisionResult; + +import oasis.names.tc.xacml._3_0.core.schema.wd_17.DecisionType; + +/** + * Deny-unless-permit combining algorithm + * + * @author cdangerv + * @version $Id: $ + */ +public final class DenyUnlessPermitAlg extends BaseCombiningAlg +{ + + private static class Evaluator implements CombiningAlg.Evaluator + { + + private final List combinedElements; + + private Evaluator(List combinedElements) + { + this.combinedElements = combinedElements; + } + + @Override + public DecisionResult eval(EvaluationContext context) + { + DecisionResult combinedDenyResult = null; + for (Decidable combinedElement : combinedElements) + { + // make sure that the policy matches the context + final DecisionResult policyResult = combinedElement.evaluate(context); + final DecisionType decision = policyResult.getDecision(); + switch (decision) + { + case PERMIT: + return policyResult; + case DENY: + // merge result (obligations/advice/mached policy IDs) + if (combinedDenyResult == null) + { + combinedDenyResult = policyResult; + } else + { + combinedDenyResult.merge(policyResult.getPepActions(), policyResult.getApplicablePolicyIdList()); + } + break; + default: + continue; + } + } + + return combinedDenyResult == null ? BaseDecisionResult.DENY : combinedDenyResult; + } + + } + + /** {@inheritDoc} */ + @Override + public CombiningAlg.Evaluator getInstance(List> params, List combinedElements) throws UnsupportedOperationException, IllegalArgumentException + { + return new Evaluator(combinedElements); + } + + /** + * The standard URIs used to identify this algorithm; first one is for policy combinging, second one for rule combining. + */ + static final String[] SUPPORTED_IDENTIFIERS = { "urn:oasis:names:tc:xacml:3.0:policy-combining-algorithm:deny-unless-permit", "urn:oasis:names:tc:xacml:3.0:rule-combining-algorithm:deny-unless-permit" }; + + /** + * Supported algorithms + */ + public static final CombiningAlgSet SET = new CombiningAlgSet(new DenyUnlessPermitAlg(SUPPORTED_IDENTIFIERS[0]), new DenyUnlessPermitAlg(SUPPORTED_IDENTIFIERS[1])); + + private DenyUnlessPermitAlg(String algId) + { + super(algId, Decidable.class); + } + +} diff --git a/src/main/java/org/ow2/authzforce/core/pdp/impl/combining/FirstApplicableAlg.java b/src/main/java/org/ow2/authzforce/core/pdp/impl/combining/FirstApplicableAlg.java index ca10d98d..23c5f587 100644 --- a/src/main/java/org/ow2/authzforce/core/pdp/impl/combining/FirstApplicableAlg.java +++ b/src/main/java/org/ow2/authzforce/core/pdp/impl/combining/FirstApplicableAlg.java @@ -1,93 +1,96 @@ -/** - * Copyright (C) 2011-2015 Thales Services SAS. - * - * This file is part of AuthZForce. - * - * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software - * Foundation, either version 3 of the License, or (at your option) any later version. - * - * AuthZForce 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . - */ -package org.ow2.authzforce.core.pdp.impl.combining; - -import java.util.List; - -import oasis.names.tc.xacml._3_0.core.schema.wd_17.DecisionType; - -import org.ow2.authzforce.core.pdp.api.BaseCombiningAlg; -import org.ow2.authzforce.core.pdp.api.CombiningAlg; -import org.ow2.authzforce.core.pdp.api.CombiningAlgParameter; -import org.ow2.authzforce.core.pdp.api.Decidable; -import org.ow2.authzforce.core.pdp.api.DecisionResult; -import org.ow2.authzforce.core.pdp.api.EvaluationContext; -import org.ow2.authzforce.core.pdp.impl.BaseDecisionResult; - -/** - * This is the standard First-Applicable policy/rule combining algorithm. It looks through the set of policies/rules, finds the first one that applies, and - * returns that evaluation result. - * - */ -public final class FirstApplicableAlg extends BaseCombiningAlg -{ - private static class Evaluator implements CombiningAlg.Evaluator - { - - private final List combinedElements; - - private Evaluator(List combinedElements) - { - this.combinedElements = combinedElements; - } - - @Override - public DecisionResult eval(EvaluationContext context) - { - for (final Decidable combinedElement : combinedElements) - { - // evaluate the policy - final DecisionResult result = combinedElement.evaluate(context); - final DecisionType decision = result.getDecision(); - - // in the case of PERMIT, DENY, or INDETERMINATE, we always - // just return that result, so only on a rule that doesn't - // apply do we keep going... - if (decision != DecisionType.NOT_APPLICABLE) - { - return result; - } - } - - // if we got here, then none of the rules applied - return BaseDecisionResult.NOT_APPLICABLE; - } - - } - - @Override - public CombiningAlg.Evaluator getInstance(List> params, List combinedElements) - throws UnsupportedOperationException, IllegalArgumentException - { - return new Evaluator(combinedElements); - } - - /** - * The standard URIs used to identify this algorithm - */ - static final String[] SUPPORTED_IDENTIFIERS = { "urn:oasis:names:tc:xacml:1.0:policy-combining-algorithm:first-applicable", - "urn:oasis:names:tc:xacml:1.0:rule-combining-algorithm:first-applicable" }; - - /** - * Supported algorithms - */ - public static final CombiningAlgSet SET = new CombiningAlgSet(new FirstApplicableAlg(SUPPORTED_IDENTIFIERS[0]), new FirstApplicableAlg( - SUPPORTED_IDENTIFIERS[1])); - - private FirstApplicableAlg(String algId) - { - super(algId, Decidable.class); - } - -} +/** + * Copyright (C) 2011-2015 Thales Services SAS. + * + * This file is part of AuthZForce. + * + * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * AuthZForce 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . + */ +package org.ow2.authzforce.core.pdp.impl.combining; + +import java.util.List; + +import oasis.names.tc.xacml._3_0.core.schema.wd_17.DecisionType; + +import org.ow2.authzforce.core.pdp.api.BaseCombiningAlg; +import org.ow2.authzforce.core.pdp.api.CombiningAlg; +import org.ow2.authzforce.core.pdp.api.CombiningAlgParameter; +import org.ow2.authzforce.core.pdp.api.Decidable; +import org.ow2.authzforce.core.pdp.api.DecisionResult; +import org.ow2.authzforce.core.pdp.api.EvaluationContext; +import org.ow2.authzforce.core.pdp.impl.BaseDecisionResult; + +/** + * This is the standard First-Applicable policy/rule combining algorithm. It looks through the set of policies/rules, finds the first one that applies, and + * returns that evaluation result. + * + * @author cdangerv + * @version $Id: $ + */ +public final class FirstApplicableAlg extends BaseCombiningAlg +{ + private static class Evaluator implements CombiningAlg.Evaluator + { + + private final List combinedElements; + + private Evaluator(List combinedElements) + { + this.combinedElements = combinedElements; + } + + @Override + public DecisionResult eval(EvaluationContext context) + { + for (final Decidable combinedElement : combinedElements) + { + // evaluate the policy + final DecisionResult result = combinedElement.evaluate(context); + final DecisionType decision = result.getDecision(); + + // in the case of PERMIT, DENY, or INDETERMINATE, we always + // just return that result, so only on a rule that doesn't + // apply do we keep going... + if (decision != DecisionType.NOT_APPLICABLE) + { + return result; + } + } + + // if we got here, then none of the rules applied + return BaseDecisionResult.NOT_APPLICABLE; + } + + } + + /** {@inheritDoc} */ + @Override + public CombiningAlg.Evaluator getInstance(List> params, List combinedElements) + throws UnsupportedOperationException, IllegalArgumentException + { + return new Evaluator(combinedElements); + } + + /** + * The standard URIs used to identify this algorithm + */ + static final String[] SUPPORTED_IDENTIFIERS = { "urn:oasis:names:tc:xacml:1.0:policy-combining-algorithm:first-applicable", + "urn:oasis:names:tc:xacml:1.0:rule-combining-algorithm:first-applicable" }; + + /** + * Supported algorithms + */ + public static final CombiningAlgSet SET = new CombiningAlgSet(new FirstApplicableAlg(SUPPORTED_IDENTIFIERS[0]), new FirstApplicableAlg( + SUPPORTED_IDENTIFIERS[1])); + + private FirstApplicableAlg(String algId) + { + super(algId, Decidable.class); + } + +} diff --git a/src/main/java/org/ow2/authzforce/core/pdp/impl/combining/LegacyDenyOverridesAlg.java b/src/main/java/org/ow2/authzforce/core/pdp/impl/combining/LegacyDenyOverridesAlg.java index 3b2f3bb9..84cd85de 100644 --- a/src/main/java/org/ow2/authzforce/core/pdp/impl/combining/LegacyDenyOverridesAlg.java +++ b/src/main/java/org/ow2/authzforce/core/pdp/impl/combining/LegacyDenyOverridesAlg.java @@ -1,90 +1,93 @@ -/** - * Copyright (C) 2011-2015 Thales Services SAS. - * - * This file is part of AuthZForce. - * - * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software - * Foundation, either version 3 of the License, or (at your option) any later version. - * - * AuthZForce 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . - */ -package org.ow2.authzforce.core.pdp.impl.combining; - -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import org.ow2.authzforce.core.pdp.api.BaseCombiningAlg; -import org.ow2.authzforce.core.pdp.api.CombiningAlg; -import org.ow2.authzforce.core.pdp.api.CombiningAlgParameter; -import org.ow2.authzforce.core.pdp.api.Decidable; - -/** - * This is the standard Deny-Overrides and Ordered-Deny-Overrides combining algorithm. It allows a single evaluation of Deny to take precedence over any number - * of permit, not applicable or indeterminate results. Note that since this implementation does an ordered evaluation, this class also supports the - * Ordered-Deny-Overrides algorithm. - * - */ -public final class LegacyDenyOverridesAlg extends BaseCombiningAlg -{ - private static final String LEGACY_ALG_WARNING = "%s is a legacy combining algorithm defined in XACML versions earlier than 3.0. This implementation does not support such legacy algorithms. Use the new XACML 3.0 versions of these combining algorithms instead."; - - /** - * The standard URIs used to identify this algorithm - */ - static final String[] SUPPORTED_IDENTIFIERS = { "urn:oasis:names:tc:xacml:1.0:policy-combining-algorithm:deny-overrides", - "urn:oasis:names:tc:xacml:1.0:rule-combining-algorithm:deny-overrides", - "urn:oasis:names:tc:xacml:1.1:policy-combining-algorithm:ordered-deny-overrides", - "urn:oasis:names:tc:xacml:1.1:rule-combining-algorithm:ordered-deny-overrides" }; - - /** - * Supported algorithms - */ - public static final CombiningAlgSet SET; - static - { - final Set> algSet = new HashSet<>(); - for (final String algId : SUPPORTED_IDENTIFIERS) - { - algSet.add(new LegacyDenyOverridesAlg(algId)); - } - - SET = new CombiningAlgSet(algSet); - } - - private final UnsupportedOperationException unsupportedLegacyAlgorithmException; - - private LegacyDenyOverridesAlg(String algId) - { - super(algId, Decidable.class); - this.unsupportedLegacyAlgorithmException = new UnsupportedOperationException(String.format(LEGACY_ALG_WARNING, this)); - } - - @Override - public CombiningAlg.Evaluator getInstance(List> params, List combinedElements) - { - throw this.unsupportedLegacyAlgorithmException; - /* - * boolean atLeastOnePermit = false; Obligations combinedPermitObligations = null; AssociatedAdvice combinedPermitAdvice = null; for (final IPolicy - * policy : policyElements) { // evaluate the policy final DecisionResult result = policy.evaluate(context); final DecisionType effect = - * result.getDecision(); - * - * // unlike in the RuleCombining version of this alg, we always // return DENY if any Policy returns DENY or INDETERMINATE if ((effect == - * DecisionType.DENY) || (effect == DecisionType.INDETERMINATE)) { return new DecisionResult(DecisionType.DENY, result.getPepActions()); } - * - * // remember if at least one Policy said PERMIT if (effect == DecisionType.PERMIT) { atLeastOnePermit = true; - * - * // copy the obligations/advice in case the final result is Permit combinedPermitObligations = CombiningAlgorithm.addResultObligations(result, - * combinedPermitObligations); combinedPermitAdvice = CombiningAlgorithm.addResultAdvice(result, combinedPermitAdvice); } } - * - * // if we got a PERMIT, return it, otherwise it's NOT_APPLICABLE if (atLeastOnePermit) { return new Result(DecisionType.PERMIT, null, - * combinedPermitObligations, combinedPermitAdvice, null, null); } - * - * return new Result(DecisionType.NOT_APPLICABLE, null, null, null, null, null); - */ - } - -} +/** + * Copyright (C) 2011-2015 Thales Services SAS. + * + * This file is part of AuthZForce. + * + * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * AuthZForce 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . + */ +package org.ow2.authzforce.core.pdp.impl.combining; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.ow2.authzforce.core.pdp.api.BaseCombiningAlg; +import org.ow2.authzforce.core.pdp.api.CombiningAlg; +import org.ow2.authzforce.core.pdp.api.CombiningAlgParameter; +import org.ow2.authzforce.core.pdp.api.Decidable; + +/** + * This is the standard Deny-Overrides and Ordered-Deny-Overrides combining algorithm. It allows a single evaluation of Deny to take precedence over any number + * of permit, not applicable or indeterminate results. Note that since this implementation does an ordered evaluation, this class also supports the + * Ordered-Deny-Overrides algorithm. + * + * @author cdangerv + * @version $Id: $ + */ +public final class LegacyDenyOverridesAlg extends BaseCombiningAlg +{ + private static final String LEGACY_ALG_WARNING = "%s is a legacy combining algorithm defined in XACML versions earlier than 3.0. This implementation does not support such legacy algorithms. Use the new XACML 3.0 versions of these combining algorithms instead."; + + /** + * The standard URIs used to identify this algorithm + */ + static final String[] SUPPORTED_IDENTIFIERS = { "urn:oasis:names:tc:xacml:1.0:policy-combining-algorithm:deny-overrides", + "urn:oasis:names:tc:xacml:1.0:rule-combining-algorithm:deny-overrides", + "urn:oasis:names:tc:xacml:1.1:policy-combining-algorithm:ordered-deny-overrides", + "urn:oasis:names:tc:xacml:1.1:rule-combining-algorithm:ordered-deny-overrides" }; + + /** + * Supported algorithms + */ + public static final CombiningAlgSet SET; + static + { + final Set> algSet = new HashSet<>(); + for (final String algId : SUPPORTED_IDENTIFIERS) + { + algSet.add(new LegacyDenyOverridesAlg(algId)); + } + + SET = new CombiningAlgSet(algSet); + } + + private final UnsupportedOperationException unsupportedLegacyAlgorithmException; + + private LegacyDenyOverridesAlg(String algId) + { + super(algId, Decidable.class); + this.unsupportedLegacyAlgorithmException = new UnsupportedOperationException(String.format(LEGACY_ALG_WARNING, this)); + } + + /** {@inheritDoc} */ + @Override + public CombiningAlg.Evaluator getInstance(List> params, List combinedElements) + { + throw this.unsupportedLegacyAlgorithmException; + /* + * boolean atLeastOnePermit = false; Obligations combinedPermitObligations = null; AssociatedAdvice combinedPermitAdvice = null; for (final IPolicy + * policy : policyElements) { // evaluate the policy final DecisionResult result = policy.evaluate(context); final DecisionType effect = + * result.getDecision(); + * + * // unlike in the RuleCombining version of this alg, we always // return DENY if any Policy returns DENY or INDETERMINATE if ((effect == + * DecisionType.DENY) || (effect == DecisionType.INDETERMINATE)) { return new DecisionResult(DecisionType.DENY, result.getPepActions()); } + * + * // remember if at least one Policy said PERMIT if (effect == DecisionType.PERMIT) { atLeastOnePermit = true; + * + * // copy the obligations/advice in case the final result is Permit combinedPermitObligations = CombiningAlgorithm.addResultObligations(result, + * combinedPermitObligations); combinedPermitAdvice = CombiningAlgorithm.addResultAdvice(result, combinedPermitAdvice); } } + * + * // if we got a PERMIT, return it, otherwise it's NOT_APPLICABLE if (atLeastOnePermit) { return new Result(DecisionType.PERMIT, null, + * combinedPermitObligations, combinedPermitAdvice, null, null); } + * + * return new Result(DecisionType.NOT_APPLICABLE, null, null, null, null, null); + */ + } + +} diff --git a/src/main/java/org/ow2/authzforce/core/pdp/impl/combining/LegacyPermitOverridesAlg.java b/src/main/java/org/ow2/authzforce/core/pdp/impl/combining/LegacyPermitOverridesAlg.java index 2492f782..eab614b7 100644 --- a/src/main/java/org/ow2/authzforce/core/pdp/impl/combining/LegacyPermitOverridesAlg.java +++ b/src/main/java/org/ow2/authzforce/core/pdp/impl/combining/LegacyPermitOverridesAlg.java @@ -1,98 +1,101 @@ -/** - * Copyright (C) 2011-2015 Thales Services SAS. - * - * This file is part of AuthZForce. - * - * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software - * Foundation, either version 3 of the License, or (at your option) any later version. - * - * AuthZForce 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . - */ -package org.ow2.authzforce.core.pdp.impl.combining; - -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import org.ow2.authzforce.core.pdp.api.BaseCombiningAlg; -import org.ow2.authzforce.core.pdp.api.CombiningAlg; -import org.ow2.authzforce.core.pdp.api.CombiningAlgParameter; -import org.ow2.authzforce.core.pdp.api.Decidable; - -/** - * This implements the standard Permit-Overrides and Ordered-Permit-Overrides policy/rule combining algorithm. It allows a single evaluation of Permit to take - * precedence over any number of deny, not applicable or indeterminate results. Note that since this implementation does an ordered evaluation, this class also - * supports the Ordered Permit Overrides algorithm. - * - */ -public final class LegacyPermitOverridesAlg extends BaseCombiningAlg -{ - private static final String LEGACY_ALG_WARNING = "%s is a legacy combining algorithm defined in XACML versions earlier than 3.0. This implementation does not support such legacy algorithms. Use the new XACML 3.0 versions of these combining algorithms instead."; - - /** - * The standard URIs used to identify this algorithm - */ - static final String[] SUPPORTED_IDENTIFIERS = { "urn:oasis:names:tc:xacml:1.0:policy-combining-algorithm:permit-overrides", - "urn:oasis:names:tc:xacml:1.0:rule-combining-algorithm:permit-overrides", - "urn:oasis:names:tc:xacml:1.1:policy-combining-algorithm:ordered-permit-overrides", - "urn:oasis:names:tc:xacml:1.1:rule-combining-algorithm:ordered-permit-overrides" }; - - /** - * Supported algorithms - */ - public static final CombiningAlgSet SET; - static - { - final Set> algSet = new HashSet<>(); - for (final String algId : SUPPORTED_IDENTIFIERS) - { - algSet.add(new LegacyPermitOverridesAlg(algId)); - } - - SET = new CombiningAlgSet(algSet); - } - - private final UnsupportedOperationException unsupportedLegacyAlgorithmException; - - private LegacyPermitOverridesAlg(String algId) - { - super(algId, Decidable.class); - this.unsupportedLegacyAlgorithmException = new UnsupportedOperationException(String.format(LEGACY_ALG_WARNING, this)); - } - - @Override - public Evaluator getInstance(List> parameters, List combinedElements) - { - throw this.unsupportedLegacyAlgorithmException; - /* - * boolean atLeastOneError = false; boolean atLeastOneDeny = false; Obligations combinedDenyObligations = null; AssociatedAdvice combinedDenyAdvice = - * null; StatusHelper firstIndeterminateStatus = null; - * - * // List policiesList = new ArrayList(); for (final IPolicy policyElement : policyElements) { // make sure that the - * policy matches the context final MatchResult match = policyElement.match(context); LOGGER.debug("{} - {}", policyElement, match); if (match == null) - * { atLeastOneError = true; } else if (match.getResult() == MatchResult.INDETERMINATE) { atLeastOneError = true; - * - * // keep track of the first error, regardless of cause if (firstIndeterminateStatus == null) { firstIndeterminateStatus = match.getStatus(); } } else - * if (match.getResult() == MatchResult.MATCH) { // now we evaluate the policy final Result result = policyElement.evaluate(context); final DecisionType - * effect = result.getDecision(); - * - * if (effect == DecisionType.PERMIT) { return result; } if (effect == DecisionType.DENY) { atLeastOneDeny = true; - * - * // copy the obligations/advice in case the final result is Deny combinedDenyObligations = CombiningAlgorithm.addResultObligations(result, - * combinedDenyObligations); combinedDenyAdvice = CombiningAlgorithm.addResultAdvice(result, combinedDenyAdvice); } else if (effect == - * DecisionType.INDETERMINATE) { atLeastOneError = true; // keep track of the first error, regardless of cause if (firstIndeterminateStatus == null) { - * firstIndeterminateStatus = result.getStatus(); } } } } - * - * // if we got a DENY, return it if (atLeastOneDeny) { return new Result(DecisionType.DENY, null, combinedDenyObligations, combinedDenyAdvice, null, - * null); } - * - * // if we got an INDETERMINATE, return it if (atLeastOneError) { return new Result(DecisionType.INDETERMINATE, firstIndeterminateStatus, null, null, - * null, null); } - * - * // if we got here, then nothing applied to us return new Result(DecisionType.NOT_APPLICABLE, null, null, null, null, null); - */ - } -} +/** + * Copyright (C) 2011-2015 Thales Services SAS. + * + * This file is part of AuthZForce. + * + * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * AuthZForce 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . + */ +package org.ow2.authzforce.core.pdp.impl.combining; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.ow2.authzforce.core.pdp.api.BaseCombiningAlg; +import org.ow2.authzforce.core.pdp.api.CombiningAlg; +import org.ow2.authzforce.core.pdp.api.CombiningAlgParameter; +import org.ow2.authzforce.core.pdp.api.Decidable; + +/** + * This implements the standard Permit-Overrides and Ordered-Permit-Overrides policy/rule combining algorithm. It allows a single evaluation of Permit to take + * precedence over any number of deny, not applicable or indeterminate results. Note that since this implementation does an ordered evaluation, this class also + * supports the Ordered Permit Overrides algorithm. + * + * @author cdangerv + * @version $Id: $ + */ +public final class LegacyPermitOverridesAlg extends BaseCombiningAlg +{ + private static final String LEGACY_ALG_WARNING = "%s is a legacy combining algorithm defined in XACML versions earlier than 3.0. This implementation does not support such legacy algorithms. Use the new XACML 3.0 versions of these combining algorithms instead."; + + /** + * The standard URIs used to identify this algorithm + */ + static final String[] SUPPORTED_IDENTIFIERS = { "urn:oasis:names:tc:xacml:1.0:policy-combining-algorithm:permit-overrides", + "urn:oasis:names:tc:xacml:1.0:rule-combining-algorithm:permit-overrides", + "urn:oasis:names:tc:xacml:1.1:policy-combining-algorithm:ordered-permit-overrides", + "urn:oasis:names:tc:xacml:1.1:rule-combining-algorithm:ordered-permit-overrides" }; + + /** + * Supported algorithms + */ + public static final CombiningAlgSet SET; + static + { + final Set> algSet = new HashSet<>(); + for (final String algId : SUPPORTED_IDENTIFIERS) + { + algSet.add(new LegacyPermitOverridesAlg(algId)); + } + + SET = new CombiningAlgSet(algSet); + } + + private final UnsupportedOperationException unsupportedLegacyAlgorithmException; + + private LegacyPermitOverridesAlg(String algId) + { + super(algId, Decidable.class); + this.unsupportedLegacyAlgorithmException = new UnsupportedOperationException(String.format(LEGACY_ALG_WARNING, this)); + } + + /** {@inheritDoc} */ + @Override + public Evaluator getInstance(List> parameters, List combinedElements) + { + throw this.unsupportedLegacyAlgorithmException; + /* + * boolean atLeastOneError = false; boolean atLeastOneDeny = false; Obligations combinedDenyObligations = null; AssociatedAdvice combinedDenyAdvice = + * null; StatusHelper firstIndeterminateStatus = null; + * + * // List policiesList = new ArrayList(); for (final IPolicy policyElement : policyElements) { // make sure that the + * policy matches the context final MatchResult match = policyElement.match(context); LOGGER.debug("{} - {}", policyElement, match); if (match == null) + * { atLeastOneError = true; } else if (match.getResult() == MatchResult.INDETERMINATE) { atLeastOneError = true; + * + * // keep track of the first error, regardless of cause if (firstIndeterminateStatus == null) { firstIndeterminateStatus = match.getStatus(); } } else + * if (match.getResult() == MatchResult.MATCH) { // now we evaluate the policy final Result result = policyElement.evaluate(context); final DecisionType + * effect = result.getDecision(); + * + * if (effect == DecisionType.PERMIT) { return result; } if (effect == DecisionType.DENY) { atLeastOneDeny = true; + * + * // copy the obligations/advice in case the final result is Deny combinedDenyObligations = CombiningAlgorithm.addResultObligations(result, + * combinedDenyObligations); combinedDenyAdvice = CombiningAlgorithm.addResultAdvice(result, combinedDenyAdvice); } else if (effect == + * DecisionType.INDETERMINATE) { atLeastOneError = true; // keep track of the first error, regardless of cause if (firstIndeterminateStatus == null) { + * firstIndeterminateStatus = result.getStatus(); } } } } + * + * // if we got a DENY, return it if (atLeastOneDeny) { return new Result(DecisionType.DENY, null, combinedDenyObligations, combinedDenyAdvice, null, + * null); } + * + * // if we got an INDETERMINATE, return it if (atLeastOneError) { return new Result(DecisionType.INDETERMINATE, firstIndeterminateStatus, null, null, + * null, null); } + * + * // if we got here, then nothing applied to us return new Result(DecisionType.NOT_APPLICABLE, null, null, null, null, null); + */ + } +} diff --git a/src/main/java/org/ow2/authzforce/core/pdp/impl/combining/OnlyOneApplicableAlg.java b/src/main/java/org/ow2/authzforce/core/pdp/impl/combining/OnlyOneApplicableAlg.java index 9fc5cfd5..308ac921 100644 --- a/src/main/java/org/ow2/authzforce/core/pdp/impl/combining/OnlyOneApplicableAlg.java +++ b/src/main/java/org/ow2/authzforce/core/pdp/impl/combining/OnlyOneApplicableAlg.java @@ -1,114 +1,117 @@ -/** - * Copyright (C) 2012-2015 Thales Services SAS. - * - * This file is part of AuthZForce CE. - * - * AuthZForce CE is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software - * Foundation, either version 3 of the License, or (at your option) any later version. - * - * AuthZForce CE 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with AuthZForce CE. If not, see . - */ -package org.ow2.authzforce.core.pdp.impl.combining; - -import java.util.List; - -import org.ow2.authzforce.core.pdp.api.BaseCombiningAlg; -import org.ow2.authzforce.core.pdp.api.CombiningAlg; -import org.ow2.authzforce.core.pdp.api.CombiningAlgParameter; -import org.ow2.authzforce.core.pdp.api.DecisionResult; -import org.ow2.authzforce.core.pdp.api.EvaluationContext; -import org.ow2.authzforce.core.pdp.api.PolicyEvaluator; -import org.ow2.authzforce.core.pdp.api.IndeterminateEvaluationException; -import org.ow2.authzforce.core.pdp.api.StatusHelper; -import org.ow2.authzforce.core.pdp.impl.BaseDecisionResult; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * This is the standard only-one-applicable policy combining algorithm. - * - */ -public class OnlyOneApplicableAlg extends BaseCombiningAlg -{ - private static class Evaluator implements CombiningAlg.Evaluator - { - private static final Logger LOGGER = LoggerFactory.getLogger(Evaluator.class); - - private static final BaseDecisionResult TOO_MANY_APPLICABLE_POLICIES_INDETERMINATE_RESULT = new BaseDecisionResult(new StatusHelper( - StatusHelper.STATUS_PROCESSING_ERROR, "Too many (more than one) applicable policies for algorithm: " + ID)); - - private final List policyElements; - - private Evaluator(List policyElements) - { - this.policyElements = policyElements; - } - - @Override - public DecisionResult eval(EvaluationContext context) - { - // atLeastOne == true iff selectedPolicy != null - PolicyEvaluator selectedPolicy = null; - - for (final PolicyEvaluator policy : policyElements) - { - // see if the policy applies to the context - final boolean isApplicable; - try - { - isApplicable = policy.isApplicable(context); - } catch (IndeterminateEvaluationException e) - { - LOGGER.info("Error checking whether {} is applicable", policy, e); - return new BaseDecisionResult(e.getStatus()); - } - - if (isApplicable) - { - // if one selected (found applicable) already - if (selectedPolicy != null) - { - return TOO_MANY_APPLICABLE_POLICIES_INDETERMINATE_RESULT; - } - - // if this was the first applicable policy in the set, then - // remember it for later - selectedPolicy = policy; - } - } - - // if we got through the loop, it means we found at most one match, then - // we return the evaluation result of that policy if there is a match - if (selectedPolicy != null) - { - return selectedPolicy.evaluate(context, true); - } - - return BaseDecisionResult.NOT_APPLICABLE; - } - - } - - @Override - public Evaluator getInstance(List> params, List combinedElements) - { - return new Evaluator(combinedElements); - } - - /** - * The standard URI used to identify this algorithm - */ - public static final String ID = "urn:oasis:names:tc:xacml:1.0:policy-combining-algorithm:only-one-applicable"; - - /** - * Standard constructor. - */ - public OnlyOneApplicableAlg() - { - super(ID, PolicyEvaluator.class); - } - -} +/** + * Copyright (C) 2012-2015 Thales Services SAS. + * + * This file is part of AuthZForce CE. + * + * AuthZForce CE is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * AuthZForce CE 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with AuthZForce CE. If not, see . + */ +package org.ow2.authzforce.core.pdp.impl.combining; + +import java.util.List; + +import org.ow2.authzforce.core.pdp.api.BaseCombiningAlg; +import org.ow2.authzforce.core.pdp.api.CombiningAlg; +import org.ow2.authzforce.core.pdp.api.CombiningAlgParameter; +import org.ow2.authzforce.core.pdp.api.DecisionResult; +import org.ow2.authzforce.core.pdp.api.EvaluationContext; +import org.ow2.authzforce.core.pdp.api.PolicyEvaluator; +import org.ow2.authzforce.core.pdp.api.IndeterminateEvaluationException; +import org.ow2.authzforce.core.pdp.api.StatusHelper; +import org.ow2.authzforce.core.pdp.impl.BaseDecisionResult; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This is the standard only-one-applicable policy combining algorithm. + * + * @author cdangerv + * @version $Id: $ + */ +public class OnlyOneApplicableAlg extends BaseCombiningAlg +{ + private static class Evaluator implements CombiningAlg.Evaluator + { + private static final Logger LOGGER = LoggerFactory.getLogger(Evaluator.class); + + private static final BaseDecisionResult TOO_MANY_APPLICABLE_POLICIES_INDETERMINATE_RESULT = new BaseDecisionResult(new StatusHelper( + StatusHelper.STATUS_PROCESSING_ERROR, "Too many (more than one) applicable policies for algorithm: " + ID)); + + private final List policyElements; + + private Evaluator(List policyElements) + { + this.policyElements = policyElements; + } + + @Override + public DecisionResult eval(EvaluationContext context) + { + // atLeastOne == true iff selectedPolicy != null + PolicyEvaluator selectedPolicy = null; + + for (final PolicyEvaluator policy : policyElements) + { + // see if the policy applies to the context + final boolean isApplicable; + try + { + isApplicable = policy.isApplicable(context); + } catch (IndeterminateEvaluationException e) + { + LOGGER.info("Error checking whether {} is applicable", policy, e); + return new BaseDecisionResult(e.getStatus()); + } + + if (isApplicable) + { + // if one selected (found applicable) already + if (selectedPolicy != null) + { + return TOO_MANY_APPLICABLE_POLICIES_INDETERMINATE_RESULT; + } + + // if this was the first applicable policy in the set, then + // remember it for later + selectedPolicy = policy; + } + } + + // if we got through the loop, it means we found at most one match, then + // we return the evaluation result of that policy if there is a match + if (selectedPolicy != null) + { + return selectedPolicy.evaluate(context, true); + } + + return BaseDecisionResult.NOT_APPLICABLE; + } + + } + + /** {@inheritDoc} */ + @Override + public Evaluator getInstance(List> params, List combinedElements) + { + return new Evaluator(combinedElements); + } + + /** + * The standard URI used to identify this algorithm + */ + public static final String ID = "urn:oasis:names:tc:xacml:1.0:policy-combining-algorithm:only-one-applicable"; + + /** + * Standard constructor. + */ + public OnlyOneApplicableAlg() + { + super(ID, PolicyEvaluator.class); + } + +} diff --git a/src/main/java/org/ow2/authzforce/core/pdp/impl/combining/PermitOverridesAlg.java b/src/main/java/org/ow2/authzforce/core/pdp/impl/combining/PermitOverridesAlg.java index d7f38941..32e526d6 100644 --- a/src/main/java/org/ow2/authzforce/core/pdp/impl/combining/PermitOverridesAlg.java +++ b/src/main/java/org/ow2/authzforce/core/pdp/impl/combining/PermitOverridesAlg.java @@ -31,6 +31,9 @@ import org.ow2.authzforce.core.pdp.impl.BaseDecisionResult; * This is the standard Permit-Overrides policy/rule combining algorithm. It allows a single evaluation of Permit to * take precedence over any number of deny, not applicable or indeterminate results. Note that since this implementation * does an ordered evaluation, this class also supports the Ordered-Permit-Overrides algorithm. + * + * @author cdangerv + * @version $Id: $ */ public final class PermitOverridesAlg extends BaseCombiningAlg { @@ -156,6 +159,7 @@ public final class PermitOverridesAlg extends BaseCombiningAlg } + /** {@inheritDoc} */ @Override public Evaluator getInstance(List> params, List combinedElements) diff --git a/src/main/java/org/ow2/authzforce/core/pdp/impl/combining/PermitUnlessDenyAlg.java b/src/main/java/org/ow2/authzforce/core/pdp/impl/combining/PermitUnlessDenyAlg.java index 259a379d..d3f2adb1 100644 --- a/src/main/java/org/ow2/authzforce/core/pdp/impl/combining/PermitUnlessDenyAlg.java +++ b/src/main/java/org/ow2/authzforce/core/pdp/impl/combining/PermitUnlessDenyAlg.java @@ -1,111 +1,114 @@ -/** - * Copyright (C) 2011-2015 Thales Services SAS. - * - * This file is part of AuthZForce. - * - * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software - * Foundation, either version 3 of the License, or (at your option) any later version. - * - * AuthZForce 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . - */ -package org.ow2.authzforce.core.pdp.impl.combining; - -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import oasis.names.tc.xacml._3_0.core.schema.wd_17.DecisionType; - -import org.ow2.authzforce.core.pdp.api.BaseCombiningAlg; -import org.ow2.authzforce.core.pdp.api.CombiningAlg; -import org.ow2.authzforce.core.pdp.api.CombiningAlgParameter; -import org.ow2.authzforce.core.pdp.api.Decidable; -import org.ow2.authzforce.core.pdp.api.DecisionResult; -import org.ow2.authzforce.core.pdp.api.EvaluationContext; -import org.ow2.authzforce.core.pdp.impl.BaseDecisionResult; - -/** - * permit-unless-deny policy algorithm - * - */ -public final class PermitUnlessDenyAlg extends BaseCombiningAlg -{ - private static class Evaluator implements CombiningAlg.Evaluator - { - - private final List combinedElements; - - private Evaluator(List combinedElements) - { - this.combinedElements = combinedElements; - } - - @Override - public DecisionResult eval(EvaluationContext context) - { - DecisionResult combinedPermitResult = null; - - for (Decidable combinedElement : combinedElements) - { - final DecisionResult result = combinedElement.evaluate(context); - final DecisionType decision = result.getDecision(); - switch (decision) - { - case DENY: - return result; - case PERMIT: - // merge the obligations, etc. in case the final result is Permit - if (combinedPermitResult == null) - { - combinedPermitResult = result; - } else - { - combinedPermitResult.merge(result.getPepActions(), result.getApplicablePolicyIdList()); - } - break; - default: - continue; - } - } - - return combinedPermitResult == null ? BaseDecisionResult.PERMIT : combinedPermitResult; - } - - } - - @Override - public Evaluator getInstance(List> params, List combinedElements) - { - return new Evaluator(combinedElements); - } - - /** - * The standard URN used to identify this algorithm - */ - static final String[] SUPPORTED_IDENTIFIERS = { "urn:oasis:names:tc:xacml:3.0:policy-combining-algorithm:permit-unless-deny", - "urn:oasis:names:tc:xacml:3.0:rule-combining-algorithm:permit-unless-deny" }; - - /** - * Supported algorithms - */ - public static final CombiningAlgSet SET; - static - { - final Set> algSet = new HashSet<>(); - for (final String algId : SUPPORTED_IDENTIFIERS) - { - algSet.add(new PermitUnlessDenyAlg(algId)); - } - - SET = new CombiningAlgSet(algSet); - } - - private PermitUnlessDenyAlg(String algId) - { - super(algId, Decidable.class); - } - -} +/** + * Copyright (C) 2011-2015 Thales Services SAS. + * + * This file is part of AuthZForce. + * + * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * AuthZForce 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . + */ +package org.ow2.authzforce.core.pdp.impl.combining; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import oasis.names.tc.xacml._3_0.core.schema.wd_17.DecisionType; + +import org.ow2.authzforce.core.pdp.api.BaseCombiningAlg; +import org.ow2.authzforce.core.pdp.api.CombiningAlg; +import org.ow2.authzforce.core.pdp.api.CombiningAlgParameter; +import org.ow2.authzforce.core.pdp.api.Decidable; +import org.ow2.authzforce.core.pdp.api.DecisionResult; +import org.ow2.authzforce.core.pdp.api.EvaluationContext; +import org.ow2.authzforce.core.pdp.impl.BaseDecisionResult; + +/** + * permit-unless-deny policy algorithm + * + * @author cdangerv + * @version $Id: $ + */ +public final class PermitUnlessDenyAlg extends BaseCombiningAlg +{ + private static class Evaluator implements CombiningAlg.Evaluator + { + + private final List combinedElements; + + private Evaluator(List combinedElements) + { + this.combinedElements = combinedElements; + } + + @Override + public DecisionResult eval(EvaluationContext context) + { + DecisionResult combinedPermitResult = null; + + for (Decidable combinedElement : combinedElements) + { + final DecisionResult result = combinedElement.evaluate(context); + final DecisionType decision = result.getDecision(); + switch (decision) + { + case DENY: + return result; + case PERMIT: + // merge the obligations, etc. in case the final result is Permit + if (combinedPermitResult == null) + { + combinedPermitResult = result; + } else + { + combinedPermitResult.merge(result.getPepActions(), result.getApplicablePolicyIdList()); + } + break; + default: + continue; + } + } + + return combinedPermitResult == null ? BaseDecisionResult.PERMIT : combinedPermitResult; + } + + } + + /** {@inheritDoc} */ + @Override + public Evaluator getInstance(List> params, List combinedElements) + { + return new Evaluator(combinedElements); + } + + /** + * The standard URN used to identify this algorithm + */ + static final String[] SUPPORTED_IDENTIFIERS = { "urn:oasis:names:tc:xacml:3.0:policy-combining-algorithm:permit-unless-deny", + "urn:oasis:names:tc:xacml:3.0:rule-combining-algorithm:permit-unless-deny" }; + + /** + * Supported algorithms + */ + public static final CombiningAlgSet SET; + static + { + final Set> algSet = new HashSet<>(); + for (final String algId : SUPPORTED_IDENTIFIERS) + { + algSet.add(new PermitUnlessDenyAlg(algId)); + } + + SET = new CombiningAlgSet(algSet); + } + + private PermitUnlessDenyAlg(String algId) + { + super(algId, Decidable.class); + } + +} diff --git a/src/main/java/org/ow2/authzforce/core/pdp/impl/combining/StandardCombiningAlgRegistry.java b/src/main/java/org/ow2/authzforce/core/pdp/impl/combining/StandardCombiningAlgRegistry.java index 3a2be2be..29d41708 100644 --- a/src/main/java/org/ow2/authzforce/core/pdp/impl/combining/StandardCombiningAlgRegistry.java +++ b/src/main/java/org/ow2/authzforce/core/pdp/impl/combining/StandardCombiningAlgRegistry.java @@ -1,86 +1,89 @@ -/** - * Copyright (C) 2011-2015 Thales Services SAS. - * - * This file is part of AuthZForce. - * - * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software - * Foundation, either version 3 of the License, or (at your option) any later version. - * - * AuthZForce 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . - */ -package org.ow2.authzforce.core.pdp.impl.combining; - -import java.util.HashSet; -import java.util.Set; -import java.util.TreeSet; - -import org.ow2.authzforce.core.pdp.api.CombiningAlg; -import org.ow2.authzforce.core.pdp.api.Decidable; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * This registry supports the standard set of algorithms specified in XACML. - *

- * Note that because this supports only the standard algorithms, this factory does not allow the addition of any other algorithms. If you need a standard - * factory that is modifiable, you should create a new BaseCombiningAlgRegistry by passing this to - * {@link BaseCombiningAlgRegistry#BaseCombiningAlgRegistry(org.ow2.authzforce.core.pdp.impl.BasePdpExtensionRegistry)}. - * - */ -public final class StandardCombiningAlgRegistry extends BaseCombiningAlgRegistry -{ - private static final Logger LOGGER = LoggerFactory.getLogger(StandardCombiningAlgRegistry.class); - - private static final PdpExtensionComparator> COMPARATOR = new PdpExtensionComparator<>(); - - /** - * Singleton function registry instance for standard functions - */ - public static final StandardCombiningAlgRegistry INSTANCE; - static - { - final Set> standardAlgorithms = new HashSet<>(); - // XACML 3.0 algorithms - // deny-overrides and ordered-deny-overrides - standardAlgorithms.addAll(DenyOverridesAlg.SET.getSupportedAlgorithms()); - // permit-overrides and ordered-permit-overrides - standardAlgorithms.addAll(PermitOverridesAlg.SET.getSupportedAlgorithms()); - // deny-unless-permit - standardAlgorithms.addAll(DenyUnlessPermitAlg.SET.getSupportedAlgorithms()); - // permit-unless-deny - standardAlgorithms.addAll(PermitUnlessDenyAlg.SET.getSupportedAlgorithms()); - // first-applicable - standardAlgorithms.addAll(FirstApplicableAlg.SET.getSupportedAlgorithms()); - // only-one-applicable - standardAlgorithms.add(new OnlyOneApplicableAlg()); - // - // Legacy - // (ordered-)deny-overrides - standardAlgorithms.addAll(LegacyDenyOverridesAlg.SET.getSupportedAlgorithms()); - // (orderered-)permit-overrides - standardAlgorithms.addAll(LegacyPermitOverridesAlg.SET.getSupportedAlgorithms()); - - INSTANCE = new StandardCombiningAlgRegistry(standardAlgorithms); - if (LOGGER.isDebugEnabled()) - { - final TreeSet> sortedAlgorithms = new TreeSet<>(COMPARATOR); - sortedAlgorithms.addAll(standardAlgorithms); - LOGGER.debug("Loaded XACML standard combining algorithms: {}", sortedAlgorithms); - } - } - - private StandardCombiningAlgRegistry(Set> standardExtensions) - { - super(standardExtensions); - } - - @Override - public void addExtension(CombiningAlg alg) - { - throw new UnsupportedOperationException("a standard factory cannot be modified"); - } - -} +/** + * Copyright (C) 2011-2015 Thales Services SAS. + * + * This file is part of AuthZForce. + * + * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * AuthZForce 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . + */ +package org.ow2.authzforce.core.pdp.impl.combining; + +import java.util.HashSet; +import java.util.Set; +import java.util.TreeSet; + +import org.ow2.authzforce.core.pdp.api.CombiningAlg; +import org.ow2.authzforce.core.pdp.api.Decidable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This registry supports the standard set of algorithms specified in XACML. + *

+ * Note that because this supports only the standard algorithms, this factory does not allow the addition of any other algorithms. If you need a standard + * factory that is modifiable, you should create a new BaseCombiningAlgRegistry by passing this to + * {@link BaseCombiningAlgRegistry#BaseCombiningAlgRegistry(org.ow2.authzforce.core.pdp.impl.BasePdpExtensionRegistry)}. + * + * @author cdangerv + * @version $Id: $ + */ +public final class StandardCombiningAlgRegistry extends BaseCombiningAlgRegistry +{ + private static final Logger LOGGER = LoggerFactory.getLogger(StandardCombiningAlgRegistry.class); + + private static final PdpExtensionComparator> COMPARATOR = new PdpExtensionComparator<>(); + + /** + * Singleton function registry instance for standard functions + */ + public static final StandardCombiningAlgRegistry INSTANCE; + static + { + final Set> standardAlgorithms = new HashSet<>(); + // XACML 3.0 algorithms + // deny-overrides and ordered-deny-overrides + standardAlgorithms.addAll(DenyOverridesAlg.SET.getSupportedAlgorithms()); + // permit-overrides and ordered-permit-overrides + standardAlgorithms.addAll(PermitOverridesAlg.SET.getSupportedAlgorithms()); + // deny-unless-permit + standardAlgorithms.addAll(DenyUnlessPermitAlg.SET.getSupportedAlgorithms()); + // permit-unless-deny + standardAlgorithms.addAll(PermitUnlessDenyAlg.SET.getSupportedAlgorithms()); + // first-applicable + standardAlgorithms.addAll(FirstApplicableAlg.SET.getSupportedAlgorithms()); + // only-one-applicable + standardAlgorithms.add(new OnlyOneApplicableAlg()); + // + // Legacy + // (ordered-)deny-overrides + standardAlgorithms.addAll(LegacyDenyOverridesAlg.SET.getSupportedAlgorithms()); + // (orderered-)permit-overrides + standardAlgorithms.addAll(LegacyPermitOverridesAlg.SET.getSupportedAlgorithms()); + + INSTANCE = new StandardCombiningAlgRegistry(standardAlgorithms); + if (LOGGER.isDebugEnabled()) + { + final TreeSet> sortedAlgorithms = new TreeSet<>(COMPARATOR); + sortedAlgorithms.addAll(standardAlgorithms); + LOGGER.debug("Loaded XACML standard combining algorithms: {}", sortedAlgorithms); + } + } + + private StandardCombiningAlgRegistry(Set> standardExtensions) + { + super(standardExtensions); + } + + /** {@inheritDoc} */ + @Override + public void addExtension(CombiningAlg alg) + { + throw new UnsupportedOperationException("a standard factory cannot be modified"); + } + +} diff --git a/src/main/java/org/ow2/authzforce/core/pdp/impl/expression/Apply.java b/src/main/java/org/ow2/authzforce/core/pdp/impl/expression/Apply.java index 7024dc22..4a6a9125 100644 --- a/src/main/java/org/ow2/authzforce/core/pdp/impl/expression/Apply.java +++ b/src/main/java/org/ow2/authzforce/core/pdp/impl/expression/Apply.java @@ -37,9 +37,11 @@ import org.ow2.authzforce.core.pdp.api.Value; /** * Evaluates XACML Apply - * + * * @param * evaluation's return type + * @author cdangerv + * @version $Id: $ */ public final class Apply extends ApplyType implements Expression { @@ -58,6 +60,7 @@ public final class Apply extends ApplyType implements Expressio * * @see oasis.names.tc.xacml._3_0.core.schema.wd_17.ApplyType#setFunctionId(java.lang.String) */ + /** {@inheritDoc} */ @Override public void setFunctionId(String value) { @@ -67,7 +70,7 @@ public final class Apply extends ApplyType implements Expressio /** * Creates instance from XACML Apply element - * + * * @param xacmlApply * XACML Apply element * @param xPathCompiler @@ -79,8 +82,7 @@ public final class Apply extends ApplyType implements Expressio * this, where "V1 -> V2" means: the expression in VariableDefinition of V1 contains a VariableReference to V2. This is used to detect exceeding depth of VariableReference * reference when a new VariableReference occurs in a VariableDefinition's expression. May be null, if this expression does not belong to any VariableDefinition. * @return Apply instance - * - * @throws IllegalArgumentException + * @throws java.lang.IllegalArgumentException * if {@code xacmlApply} is invalid or {@code expFactory} is null; or function ID not supported/unknown; if {@code xprs} are invalid expressions, or invalid arguments for this * function; or if all {@code xprs} are static but calling the function statically (with these static arguments) failed */ @@ -240,6 +242,7 @@ public final class Apply extends ApplyType implements Expressio isStatic = allStatic; } + /** {@inheritDoc} */ @Override public boolean isStatic() { @@ -247,14 +250,9 @@ public final class Apply extends ApplyType implements Expressio } /** + * {@inheritDoc} + * * Evaluates the apply object using the given function. This will in turn call evaluate on all the given parameters, some of which may be other Apply objects. - * - * @param context - * the representation of the request - * - * @return the result of trying to evaluate this apply object - * @throws IndeterminateEvaluationException - * if any evaluation error occured when evaluating the Apply expression in the given {@context} */ @Override public V evaluate(EvaluationContext context) throws IndeterminateEvaluationException @@ -263,10 +261,10 @@ public final class Apply extends ApplyType implements Expressio } /** + * {@inheritDoc} + * * Returns the type of attribute that this object will return on a call to evaluate . In practice, this will always be the same as the result of calling getReturnType on * the function used by this object. - * - * @return the type returned by evaluate */ @Override public Datatype getReturnType() @@ -274,6 +272,7 @@ public final class Apply extends ApplyType implements Expressio return functionCall.getReturnType(); } + /** {@inheritDoc} */ @Override public JAXBElement getJAXBElement() { diff --git a/src/main/java/org/ow2/authzforce/core/pdp/impl/expression/AttributeDesignator.java b/src/main/java/org/ow2/authzforce/core/pdp/impl/expression/AttributeDesignator.java index 80db4bd8..c77efc83 100644 --- a/src/main/java/org/ow2/authzforce/core/pdp/impl/expression/AttributeDesignator.java +++ b/src/main/java/org/ow2/authzforce/core/pdp/impl/expression/AttributeDesignator.java @@ -31,7 +31,7 @@ import org.ow2.authzforce.core.pdp.api.StatusHelper; /** * AttributeDesignator - * + * *

* WARNING: java.net.URI cannot be used here for XACML datatype/category/ID, because not equivalent to XML schema anyURI type. Spaces are allowed in XSD anyURI * [1], not in java.net.URI. @@ -45,10 +45,11 @@ import org.ow2.authzforce.core.pdp.api.StatusHelper; *

* https://java.net/projects/jaxb/lists/users/archive/2011-07/message/16 *

- * + * * @param * AttributeDesignator evaluation result value's primitive datatype - * + * @author cdangerv + * @version $Id: $ */ public class AttributeDesignator extends AttributeDesignatorType implements Expression> { @@ -81,6 +82,7 @@ public class AttributeDesignator extends AttributeDes * * @see oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeDesignatorType#setCategory(java.lang .String) */ + /** {@inheritDoc} */ @Override public final void setCategory(String value) { @@ -93,6 +95,7 @@ public class AttributeDesignator extends AttributeDes * * @see oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeDesignatorType#setAttributeId(java.lang .String) */ + /** {@inheritDoc} */ @Override public final void setAttributeId(String value) { @@ -105,6 +108,7 @@ public class AttributeDesignator extends AttributeDes * * @see oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeDesignatorType#setIssuer(java.lang.String ) */ + /** {@inheritDoc} */ @Override public final void setIssuer(String value) { @@ -117,6 +121,7 @@ public class AttributeDesignator extends AttributeDes * * @see oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeDesignatorType#setDataType(java.lang .String) */ + /** {@inheritDoc} */ @Override public final void setDataType(String value) { @@ -129,6 +134,7 @@ public class AttributeDesignator extends AttributeDes * * @see com.thalesgroup.authzforce.core.eval.Expression#isStatic() */ + /** {@inheritDoc} */ @Override public boolean isStatic() { @@ -138,7 +144,7 @@ public class AttributeDesignator extends AttributeDes /** * Return an instance of an AttributeDesignator based on an AttributeDesignatorType - * + * * @param attrDesignator * the AttributeDesignatorType we want to convert * @param resultDatatype @@ -194,12 +200,9 @@ public class AttributeDesignator extends AttributeDes } /** + * {@inheritDoc} + * * Evaluates the pre-assigned meta-data against the given context, trying to find some matching values. - * - * @param context - * the representation of the request - * - * @return a result containing a bag either empty because no values were found or containing at least one value */ @Override public Bag evaluate(EvaluationContext context) throws IndeterminateEvaluationException @@ -225,12 +228,14 @@ public class AttributeDesignator extends AttributeDes return bag; } + /** {@inheritDoc} */ @Override public Datatype> getReturnType() { return this.returnType; } + /** {@inheritDoc} */ @Override public JAXBElement getJAXBElement() { @@ -246,6 +251,7 @@ public class AttributeDesignator extends AttributeDes * * @see java.lang.Object#toString() */ + /** {@inheritDoc} */ @Override public String toString() { @@ -258,6 +264,7 @@ public class AttributeDesignator extends AttributeDes return toString; } + /** {@inheritDoc} */ @Override public int hashCode() { @@ -269,6 +276,7 @@ public class AttributeDesignator extends AttributeDes return hashCode; } + /** {@inheritDoc} */ @Override public boolean equals(Object obj) { diff --git a/src/main/java/org/ow2/authzforce/core/pdp/impl/expression/AttributeSelectorExpression.java b/src/main/java/org/ow2/authzforce/core/pdp/impl/expression/AttributeSelectorExpression.java index 15a3c546..c68730c8 100644 --- a/src/main/java/org/ow2/authzforce/core/pdp/impl/expression/AttributeSelectorExpression.java +++ b/src/main/java/org/ow2/authzforce/core/pdp/impl/expression/AttributeSelectorExpression.java @@ -58,7 +58,7 @@ import org.ow2.authzforce.core.pdp.impl.value.XPathValue; * specification, and this implementation is experimental (not to be used in production). *

* Reasons for using SAXON's native API (s9api) in XPath evaluation instead of standard Java APIs (e.g. JAXP): - * + * *

    *
  1. Performance: See http://www.saxonica.com/documentation9.5/javadoc/net /sf/saxon/s9api/package-summary.html: *

    @@ -67,14 +67,16 @@ import org.ow2.authzforce.core.pdp.impl.value.XPathValue; * performance of the application. *

    *
  2. - * + * *
  3. Functional: s9api provides XPATH 3.0 support, whereas standard Java APIs designed for XPATH 1.0 support only. See http://www.saxonica.com/html/documentation/conformance/jaxp.html. However, for * the moment, only XPath 1.0 and 2.0 are supported by this class. But we prepare for the future.
  4. *
*

- * + * * @param * AttributeSelector evaluation results' primitive returnType + * @author cdangerv + * @version $Id: $ */ public class AttributeSelectorExpression extends AttributeSelectorType implements Expression> { @@ -188,6 +190,7 @@ public class AttributeSelectorExpression extends Attr * * @see oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeDesignatorType#setDataType(java.lang .String) */ + /** {@inheritDoc} */ @Override public final void setDataType(String value) { @@ -200,6 +203,7 @@ public class AttributeSelectorExpression extends Attr * * @see oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeDesignatorType#setCategory(java.lang .String) */ + /** {@inheritDoc} */ @Override public final void setCategory(String value) { @@ -212,6 +216,7 @@ public class AttributeSelectorExpression extends Attr * * @see oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeDesignatorType#setAttributeId(java.lang .String) */ + /** {@inheritDoc} */ @Override public final void setContextSelectorId(String value) { @@ -224,6 +229,7 @@ public class AttributeSelectorExpression extends Attr * * @see oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeDesignatorType#setDataType(java.lang .String) */ + /** {@inheritDoc} */ @Override public final void setPath(String value) { @@ -236,6 +242,7 @@ public class AttributeSelectorExpression extends Attr * * @see com.thalesgroup.authzforce.core.eval.Expression#isStatic() */ + /** {@inheritDoc} */ @Override public boolean isStatic() { @@ -245,7 +252,7 @@ public class AttributeSelectorExpression extends Attr /** * Creates instance from XACML model - * + * * @param attrSelectorElement * XACML AttributeSelector * @param xPathCompiler @@ -254,9 +261,9 @@ public class AttributeSelectorExpression extends Attr * AttributeProvider for finding value of the attribute identified by ContextSelectorId in {@code attrSelectorElement}; may be null if ContextSelectorId not specified * @param attrFactory * attribute factory to create the AttributeValue(s) from the XML node(s) resolved by XPath - * @throws XPathExpressionException + * @throws javax.xml.xpath.XPathExpressionException * if the Path could not be compiled to an XPath expression (using namespaceContextNode if non-null) - * @throws IllegalArgumentException + * @throws java.lang.IllegalArgumentException * if {@code attrSelectorElement}, {@code xpathCompiler} or {@code attrFactory} is null; or ContextSelectorId is not null but {@code attrProvider} is null */ public AttributeSelectorExpression(AttributeSelectorType attrSelectorElement, XPathCompiler xPathCompiler, AttributeProvider attrProvider, DatatypeFactory attrFactory) @@ -322,9 +329,9 @@ public class AttributeSelectorExpression extends Attr } /** + * {@inheritDoc} + * * Returns the data type of the attribute values that the evaluation of this selector will return - * - * @return the data type of the values found by this selector */ @Override public Datatype> getReturnType() @@ -341,14 +348,11 @@ public class AttributeSelectorExpression extends Attr } /** + * {@inheritDoc} + * * Invokes the AttributeProvider used by the given EvaluationContext to try to resolve an attribute value. If the selector is defined with MustBePresent as true, then * failure to find a matching value will result in Indeterminate, otherwise it will result in an empty bag. To support the com.thalesgroup.authzforce.core.test.basic selector functionality defined * in the XACML specification, use a Provider that has only the SelectorModule as a module that supports selector finding. - * - * @param context - * representation of the request to search - * - * @return a result containing a bag either empty because no values were found or containing at least one value, or status associated with an Indeterminate result */ @Override public Bag evaluate(EvaluationContext context) throws IndeterminateEvaluationException @@ -513,6 +517,7 @@ public class AttributeSelectorExpression extends Attr * * @see java.lang.Object#toString() */ + /** {@inheritDoc} */ @Override public String toString() { @@ -528,12 +533,14 @@ public class AttributeSelectorExpression extends Attr return toString; } + /** {@inheritDoc} */ @Override public JAXBElement getJAXBElement() { return JaxbXACMLUtils.XACML_3_0_OBJECT_FACTORY.createAttributeSelector(this); } + /** {@inheritDoc} */ @Override public int hashCode() { @@ -545,6 +552,7 @@ public class AttributeSelectorExpression extends Attr return hashCode; } + /** {@inheritDoc} */ @Override public boolean equals(Object obj) { diff --git a/src/main/java/org/ow2/authzforce/core/pdp/impl/expression/BaseVariableReference.java b/src/main/java/org/ow2/authzforce/core/pdp/impl/expression/BaseVariableReference.java index ff8b1fa3..3418b856 100644 --- a/src/main/java/org/ow2/authzforce/core/pdp/impl/expression/BaseVariableReference.java +++ b/src/main/java/org/ow2/authzforce/core/pdp/impl/expression/BaseVariableReference.java @@ -1,150 +1,154 @@ -/** - * Copyright (C) 2011-2015 Thales Services SAS. - * - * This file is part of AuthZForce. - * - * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software - * Foundation, either version 3 of the License, or (at your option) any later version. - * - * AuthZForce 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . - */ -package org.ow2.authzforce.core.pdp.impl.expression; - -import java.util.Deque; - -import javax.xml.bind.JAXBElement; - -import oasis.names.tc.xacml._3_0.core.schema.wd_17.VariableReferenceType; - -import org.ow2.authzforce.core.pdp.api.Datatype; -import org.ow2.authzforce.core.pdp.api.EvaluationContext; -import org.ow2.authzforce.core.pdp.api.Expression; -import org.ow2.authzforce.core.pdp.api.IndeterminateEvaluationException; -import org.ow2.authzforce.core.pdp.api.JaxbXACMLUtils; -import org.ow2.authzforce.core.pdp.api.Value; -import org.ow2.authzforce.core.pdp.api.VariableReference; - -/** - * This class defines a VariableReference built from VariableReference after the referenced VariableDefinition has been resolved and therefore its expression. - * As a result, Variables are simply Expressions identified by an ID (VariableId) and replace original XACML VariableReferences for actual evaluation. - * - * @param - * evaluation's return type - */ -public class BaseVariableReference implements VariableReference -{ - private final transient Expression expression; - - /* - * (non-Javadoc) - * - * @see com.thalesgroup.authzforce.core.eval.Expression#isStatic() - */ - @Override - public boolean isStatic() - { - return expression.isStatic(); - } - - /* - * (non-Javadoc) - * - * @see org.ow2.authzforce.core.pdp.impl.expression.VariableReference#getReferencedExpression() - */ - @Override - public Expression getReferencedExpression() - { - return expression; - } - - private final String variableId; - - private final transient Deque longestVariableReferenceChain; - - /** - * Constructor that takes a variable identifier - * - * @param varId - * input VariableReference from XACML model - * @param varExpr - * Expression of referenced VariableDefinition - * @param longestVarRefChain - * longest chain of VariableReference Reference in expr (V1 -> V2 -> ... -> Vn, where "V1 -> V2" means VariableReference V1's - * expression contains one or more VariableReferences to V2) - */ - public BaseVariableReference(String varId, Expression varExpr, Deque longestVarRefChain) - { - this.variableId = varId; - this.expression = varExpr; - this.longestVariableReferenceChain = longestVarRefChain; - } - - /** - * Evaluates the referenced expression using the given context, and either returns an error or a resulting value. If this doesn't reference an evaluatable - * expression (eg, a single Function) then this will throw an exception. - * - * @param context - * the representation of the request - * - * @return the result of evaluation - */ - @Override - public V evaluate(EvaluationContext context) throws IndeterminateEvaluationException - { - /* - * Even if context == null, evaluation may work because expression may be static/constant (e.g. AttributeValue or Apply on AttributeValues). This is - * called static evaluation and is used for pre-evaluating/optimizing certain function calls. - */ - if (context == null) - { - return expression.evaluate(null); - } - - final V ctxVal = context.getVariableValue(this.variableId, expression.getReturnType()); - if (ctxVal != null) - { - return ctxVal; - } - - // ctxVal == null: not evaluated yet in this context -> evaluate now - final V result = expression.evaluate(context); - context.putVariableIfAbsent(this.variableId, result); - return result; - } - - /** - * Returns the type of the referenced expression. - * - * @return the attribute return type of the referenced expression - * - */ - @Override - public Datatype getReturnType() - { - return expression.getReturnType(); - } - - @Override - public String getVariableId() - { - return this.variableId; - } - - /** - * @return the longestVariableReferenceChain - */ - public Deque getLongestVariableReferenceChain() - { - return longestVariableReferenceChain; - } - - @Override - public JAXBElement getJAXBElement() - { - return JaxbXACMLUtils.XACML_3_0_OBJECT_FACTORY.createVariableReference(new VariableReferenceType(variableId)); - } - -} +/** + * Copyright (C) 2011-2015 Thales Services SAS. + * + * This file is part of AuthZForce. + * + * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * AuthZForce 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . + */ +package org.ow2.authzforce.core.pdp.impl.expression; + +import java.util.Deque; + +import javax.xml.bind.JAXBElement; + +import oasis.names.tc.xacml._3_0.core.schema.wd_17.VariableReferenceType; + +import org.ow2.authzforce.core.pdp.api.Datatype; +import org.ow2.authzforce.core.pdp.api.EvaluationContext; +import org.ow2.authzforce.core.pdp.api.Expression; +import org.ow2.authzforce.core.pdp.api.IndeterminateEvaluationException; +import org.ow2.authzforce.core.pdp.api.JaxbXACMLUtils; +import org.ow2.authzforce.core.pdp.api.Value; +import org.ow2.authzforce.core.pdp.api.VariableReference; + +/** + * This class defines a VariableReference built from VariableReference after the referenced VariableDefinition has been resolved and therefore its expression. + * As a result, Variables are simply Expressions identified by an ID (VariableId) and replace original XACML VariableReferences for actual evaluation. + * + * @param + * evaluation's return type + * @author cdangerv + * @version $Id: $ + */ +public class BaseVariableReference implements VariableReference +{ + private final transient Expression expression; + + /* + * (non-Javadoc) + * + * @see com.thalesgroup.authzforce.core.eval.Expression#isStatic() + */ + /** {@inheritDoc} */ + @Override + public boolean isStatic() + { + return expression.isStatic(); + } + + /* + * (non-Javadoc) + * + * @see org.ow2.authzforce.core.pdp.impl.expression.VariableReference#getReferencedExpression() + */ + /** {@inheritDoc} */ + @Override + public Expression getReferencedExpression() + { + return expression; + } + + private final String variableId; + + private final transient Deque longestVariableReferenceChain; + + /** + * Constructor that takes a variable identifier + * + * @param varId + * input VariableReference from XACML model + * @param varExpr + * Expression of referenced VariableDefinition + * @param longestVarRefChain + * longest chain of VariableReference Reference in expr (V1 -> V2 -> ... -> Vn, where "V1 -> V2" means VariableReference V1's + * expression contains one or more VariableReferences to V2) + */ + public BaseVariableReference(String varId, Expression varExpr, Deque longestVarRefChain) + { + this.variableId = varId; + this.expression = varExpr; + this.longestVariableReferenceChain = longestVarRefChain; + } + + /** + * {@inheritDoc} + * + * Evaluates the referenced expression using the given context, and either returns an error or a resulting value. If this doesn't reference an evaluatable + * expression (eg, a single Function) then this will throw an exception. + */ + @Override + public V evaluate(EvaluationContext context) throws IndeterminateEvaluationException + { + /* + * Even if context == null, evaluation may work because expression may be static/constant (e.g. AttributeValue or Apply on AttributeValues). This is + * called static evaluation and is used for pre-evaluating/optimizing certain function calls. + */ + if (context == null) + { + return expression.evaluate(null); + } + + final V ctxVal = context.getVariableValue(this.variableId, expression.getReturnType()); + if (ctxVal != null) + { + return ctxVal; + } + + // ctxVal == null: not evaluated yet in this context -> evaluate now + final V result = expression.evaluate(context); + context.putVariableIfAbsent(this.variableId, result); + return result; + } + + /** + * {@inheritDoc} + * + * Returns the type of the referenced expression. + */ + @Override + public Datatype getReturnType() + { + return expression.getReturnType(); + } + + /** {@inheritDoc} */ + @Override + public String getVariableId() + { + return this.variableId; + } + + /** + *

Getter for the field longestVariableReferenceChain.

+ * + * @return the longestVariableReferenceChain + */ + public Deque getLongestVariableReferenceChain() + { + return longestVariableReferenceChain; + } + + /** {@inheritDoc} */ + @Override + public JAXBElement getJAXBElement() + { + return JaxbXACMLUtils.XACML_3_0_OBJECT_FACTORY.createVariableReference(new VariableReferenceType(variableId)); + } + +} diff --git a/src/main/java/org/ow2/authzforce/core/pdp/impl/expression/ExpressionFactoryImpl.java b/src/main/java/org/ow2/authzforce/core/pdp/impl/expression/ExpressionFactoryImpl.java index 96c5260e..9a3dc364 100644 --- a/src/main/java/org/ow2/authzforce/core/pdp/impl/expression/ExpressionFactoryImpl.java +++ b/src/main/java/org/ow2/authzforce/core/pdp/impl/expression/ExpressionFactoryImpl.java @@ -1,373 +1,369 @@ -/** - * Copyright (C) 2011-2015 Thales Services SAS. - * - * This file is part of AuthZForce. - * - * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software - * Foundation, either version 3 of the License, or (at your option) any later version. - * - * AuthZForce 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . - */ -package org.ow2.authzforce.core.pdp.impl.expression; - -import java.io.IOException; -import java.util.ArrayDeque; -import java.util.Deque; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import javax.xml.xpath.XPathExpressionException; - -import net.sf.saxon.s9api.XPathCompiler; -import oasis.names.tc.xacml._3_0.core.schema.wd_17.ApplyType; -import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeDesignatorType; -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.ExpressionType; -import oasis.names.tc.xacml._3_0.core.schema.wd_17.FunctionType; -import oasis.names.tc.xacml._3_0.core.schema.wd_17.VariableDefinition; -import oasis.names.tc.xacml._3_0.core.schema.wd_17.VariableReferenceType; - -import org.ow2.authzforce.core.pdp.api.AttributeValue; -import org.ow2.authzforce.core.pdp.api.Datatype; -import org.ow2.authzforce.core.pdp.api.DatatypeFactory; -import org.ow2.authzforce.core.pdp.api.DatatypeFactoryRegistry; -import org.ow2.authzforce.core.pdp.api.Expression; -import org.ow2.authzforce.core.pdp.api.ExpressionFactory; -import org.ow2.authzforce.core.pdp.api.Function; -import org.ow2.authzforce.core.pdp.api.ValueExpression; -import org.ow2.authzforce.core.pdp.api.VariableReference; -import org.ow2.authzforce.core.pdp.impl.CloseableAttributeProvider; -import org.ow2.authzforce.core.pdp.impl.func.FunctionRegistry; -import org.ow2.authzforce.xmlns.pdp.ext.AbstractAttributeProvider; - -import com.sun.xacml.UnknownIdentifierException; - -/** - * Implementation of ExpressionFactory that supports the Expressions defined in VariableDefinitions in order to resolve VariableReferences. In particular, it makes sure the depth of recursivity of - * VariableDefinition does not exceed a value (to avoid inconveniences such as stackoverflow or very negative performance impact) defined by parameter to - * {@link #ExpressionFactoryImpl(DatatypeFactoryRegistry, FunctionRegistry, List, int, boolean, boolean)} parameter. Note that reference loops are avoided by the fact that a VariableReference can - * reference only a VariableDefinition defined previously to the VariableReference in this implementation. - * - */ -public class ExpressionFactoryImpl implements ExpressionFactory -{ - private static final IllegalArgumentException MISSING_ATTRIBUTE_DESIGNATOR_ISSUER_EXCEPTION = new IllegalArgumentException( - "Missing Issuer that is required on AttributeDesignators by PDP configuration"); - - private static final IllegalArgumentException UNSUPPORTED_ATTRIBUTE_SELECTOR_EXCEPTION = new IllegalArgumentException("Unsupported Expression type (optional XACML feature): AttributeSelector"); - - private static final IllegalArgumentException NULL_FUNCTION_REGISTRY_EXCEPTION = new IllegalArgumentException("Undefined function registry"); - - private static final IllegalArgumentException NULL_ATTRIBUTE_DATATYPE_REGISTRY_EXCEPTION = new IllegalArgumentException("Undefined attribute datatype registry"); - - private static final IllegalArgumentException UNSUPPORTED_ATTRIBUTE_DESIGNATOR_OR_SELECTOR_BECAUSE_OF_NULL_ATTRIBUTE_PROVIDER_EXCEPTION = new IllegalArgumentException( - "Unsupported Expression type 'AttributeDesignator' and 'AttributeSelector' because no attribute Provider defined"); - - private static final int UNLIMITED_MAX_VARIABLE_REF_DEPTH = -1; - - private final DatatypeFactoryRegistry datatypeFactoryRegistry; - private final FunctionRegistry functionRegistry; - private final CloseableAttributeProvider attributeProvider; - private final int maxVariableReferenceDepth; - // the map from identifiers to internal data - private final Map> idToVariableMap = new HashMap<>(); - private final boolean allowAttributeSelectors; - - private final boolean issuerRequiredOnAttributeDesignators; - - /** - * Maximum VariableReference depth allowed for VariableDefinitions to be managed. Examples: - *
    - *
  • A VariableDefinition V1 that does not use any VariableReference has a reference depth of 0.
  • - *
  • A VariableDefinition V1 that uses a VariableReference to VariableDefinition V2 with no further VariableReference, has a reference depth of 1
  • - *
  • etc.
  • - *
- * - * @param attributeFactory - * attribute value factory (not null) - * @param functionRegistry - * function registry (not null) - * @param jaxbAttributeProviderConfs - * XML/JAXB configurations of Attribute Providers for AttributeDesignator/AttributeSelector evaluation; may be null for static expression evaluation (out of context), in which case - * AttributeSelectors/AttributeDesignators are not supported - * @param maxVarRefDepth - * max depth of VariableReference chaining: VariableDefinition -> VariableDefinition ->... ('->' represents a VariableReference); strictly negative value means unlimited - * @param allowAttributeSelectors - * allow use of AttributeSelectors (experimental, not for production, use with caution) - * @param issuerRequiredInAttributeDesignators - * true iff it is required that all AttributeDesignator set the Issuer field, as a best practice. If the issuer is not set, remember what XACML 3.0 AttributeDesignator Evaluation says: - * "If the Issuer is not present in the attribute designator, then the matching of the attribute to the named attribute SHALL be governed by AttributeId and DataType attributes alone." - * As a result, be aware that if you use AttributeDesignators without Issuer ( {@code issuerRequiredInAttributeDesignators == false}) and the requests are using matching Attributes but - * with one or more different Issuers, this PDP engine has to gather all the values from all the attributes with matching Category/AttributeId but with any Issuer or no Issuer, - * resulting in lower performance. - * @throws IllegalArgumentException - * If any of attribute Provider modules created from {@code jaxbAttributeProviderConfs} does not provide any attribute; or it is in conflict with another one already registered to - * provide the same or part of the same attributes. - * @throws IOException - * error closing the attribute Provider modules created from {@code jaxbAttributeProviderConfs}, when and before an {@link IllegalArgumentException} is raised - */ - public ExpressionFactoryImpl(DatatypeFactoryRegistry attributeFactory, FunctionRegistry functionRegistry, List jaxbAttributeProviderConfs, int maxVarRefDepth, - boolean allowAttributeSelectors, boolean issuerRequiredInAttributeDesignators) throws IllegalArgumentException, IOException - { - if (attributeFactory == null) - { - throw NULL_ATTRIBUTE_DATATYPE_REGISTRY_EXCEPTION; - } - - if (functionRegistry == null) - { - throw NULL_FUNCTION_REGISTRY_EXCEPTION; - } - - this.datatypeFactoryRegistry = attributeFactory; - this.functionRegistry = functionRegistry; - this.maxVariableReferenceDepth = maxVarRefDepth < 0 ? UNLIMITED_MAX_VARIABLE_REF_DEPTH : maxVarRefDepth; - // finally create the global attribute Provider used to resolve - // AttributeDesignators - this.attributeProvider = CloseableAttributeProvider.getInstance(jaxbAttributeProviderConfs, attributeFactory); - this.allowAttributeSelectors = allowAttributeSelectors; - this.issuerRequiredOnAttributeDesignators = issuerRequiredInAttributeDesignators; - } - - @Override - public VariableReference addVariable(VariableDefinition varDef, XPathCompiler xPathCompiler, Deque inoutLongestVarRefChain) throws IllegalArgumentException - { - final String varId = varDef.getVariableId(); - /* - * Initialize the longest variable reference chain from this VariableDefinition (varDef -> VarDef2 -> ..., where "v1 -> v2" means: v1's expression contains a VariableReference to v2) as empty - * for later update by this#getDefinition() when resolving a VariableReference within this varDef's expression (being parsed just after). The goal is to detect chains longer than - * this.maxVariableReferenceDepth to limit abuse of VariableReferences. There may be multiple VariableReferences in a VariableDefinition's expression, such as an Apply, and each may be - * referencing a different VariableDefinition; but we are interested only in the one with the longest chain of references. - */ - /* - * we need to check the longest variableReference chain does not have circular reference and does not exceed a specific value (need to call contains() method repeatedly and preserve the - * order). - */ - final Deque longestVarRefChain = inoutLongestVarRefChain == null ? new ArrayDeque() : inoutLongestVarRefChain; - final Expression varExpr = getInstance(varDef.getExpression().getValue(), xPathCompiler, longestVarRefChain); - final BaseVariableReference var = new BaseVariableReference<>(varId, varExpr, longestVarRefChain); - return idToVariableMap.put(varId, var); - } - - @Override - public VariableReference removeVariable(String varId) - { - return idToVariableMap.remove(varId); - } - - /** - * Resolves a VariableReference to the corresponding VariableReference(Definition) and validates the depth of VariableReference, i.e. the length of VariableReference chain. A chain of variable - * references is a list of VariableIds, such that V1 -> V2 ->... -> Vn, where 'V1 -> V2' means: V1's Expression contains a VariableReference to V2. - * - * @param jaxbVarRef - * the JAXB/XACML VariableReference with merely identifying a VariableDefinition by its VariableId - * - * @return VariableReference containing the resolved VariableDefinition's expression referenced by jaxbVarRef as known by this factory, or null if unknown - * @param longestVarRefChain - * If we are resolving a VariableReference in a VariableDefinition's expression (may be null if not), this is the longest chain of VariableReferences starting from a one in this - * VariableDefinition. If we are not resolving a VariableReference in a VariableDefinition's expression, this may be null.This is used to detect exceeding reference depth (see - * {@link #ExpressionFactoryImpl(int)} for the limit. In a Expression such as an Apply, we can have multiple VariableReferences referencing different VariableDefinitions. So we can have - * different depths of VariableReference references. We compare the length of the current longest chain with the one we would get by adding the longest one in the referenced - * VariableDefinition and jaxbVarRef's VariableId. If the latter is longer, its content becomes the content longestVarRefChain. - * @throws UnknownIdentifierException - * if VariableReference's VariableId is unknown by this factory - */ - private BaseVariableReference getVariable(VariableReferenceType jaxbVarRef, Deque longestVarRefChain) throws IllegalArgumentException - { - final String varId = jaxbVarRef.getVariableId(); - final BaseVariableReference var = idToVariableMap.get(varId); - if (var == null) - { - throw new IllegalArgumentException("VariableReference's VariableId=" + varId + " unknown in the current context, i.e. does not match any prior VariableDefinition's VariableId"); - } - - if (longestVarRefChain != null) - { - final Deque referencedVarLongestRefChain = var.getLongestVariableReferenceChain(); - if (referencedVarLongestRefChain.size() + 1 > longestVarRefChain.size()) - { - // current longest is no longer the longest, so replace with new - // longest's content - longestVarRefChain.clear(); - longestVarRefChain.add(varId); - longestVarRefChain.addAll(referencedVarLongestRefChain); - } - - if (maxVariableReferenceDepth != UNLIMITED_MAX_VARIABLE_REF_DEPTH && longestVarRefChain.size() > this.maxVariableReferenceDepth) - { - throw new IllegalArgumentException("Max allowed VariableReference depth (" + this.maxVariableReferenceDepth + ") exceeded by length (" + longestVarRefChain.size() - + ") of VariableReference Reference chain: " + longestVarRefChain); - } - - } - - return var; - } - - /** - * Create a function instance using the function registry passed as parameter to {@link #ExpressionFactoryImpl(DatatypeFactoryRegistry, FunctionRegistry, List, int, boolean, boolean)} . - * - * @param functionId - * function ID (XACML URI) - * @return function instance; or null if no such function with ID {@code functionId} - */ - @Override - public Function getFunction(String functionId) - { - return this.functionRegistry.getFunction(functionId); - } - - /** - * Create a function instance using the function registry passed as parameter to {@link #ExpressionFactoryImpl(DatatypeFactoryRegistry, FunctionRegistry, List, int, boolean, boolean)} . - * - * @param functionId - * function ID (XACML URI) - * @param subFunctionReturnType - * optional sub-function's return type required only if a generic higher-order function is expected as the result, of which the sub-function is expected to be the first parameter; - * otherwise null (for first-order function). A generic higher-order function is a function whose return type depends on the sub-function ('s return type). - * @return function instance; or null if no such function with ID {@code functionId}, or if non-null {@code subFunctionReturnTypeId} specified and no higher-order function compatible with - * sub-function's return type {@code subFunctionReturnTypeId} - * @throws IllegalArgumentException - * if datatype {@code subFunctionReturnType} is not supported/known - * - */ - @Override - public Function getFunction(String functionId, Datatype subFunctionReturnType) throws IllegalArgumentException - { - if (subFunctionReturnType == null) - { - return getFunction(functionId); - } - - final DatatypeFactory subFuncReturnTypeFactory = this.datatypeFactoryRegistry.getExtension(subFunctionReturnType.getId()); - if (subFuncReturnTypeFactory == null) - { - throw new IllegalArgumentException("Invalid sub-function's return type specified: unknown/unsupported ID: " + subFunctionReturnType.getId()); - } - - return this.functionRegistry.getFunction(functionId, subFuncReturnTypeFactory); - } - - /* - * (non-Javadoc) - * - * @see com.thalesgroup.authzforce.core.eval.ExpressionFactory#getInstance(oasis. names.tc.xacml._3_0 .core.schema.wd_17.ExpressionType, oasis.names.tc.xacml._3_0.core.schema.wd_17.DefaultsType, - * java.util.List) - */ - @Override - public Expression getInstance(ExpressionType expr, XPathCompiler xPathCompiler, Deque longestVarRefChain) throws IllegalArgumentException - { - final Expression expression; - /* - * We check all types of Expression: , , , , and - */ - if (expr instanceof ApplyType) - { - expression = Apply.getInstance((ApplyType) expr, xPathCompiler, this, longestVarRefChain); - } else if (expr instanceof AttributeDesignatorType) - { - if (this.attributeProvider == null) - { - throw UNSUPPORTED_ATTRIBUTE_DESIGNATOR_OR_SELECTOR_BECAUSE_OF_NULL_ATTRIBUTE_PROVIDER_EXCEPTION; - } - - final AttributeDesignatorType jaxbAttrDes = (AttributeDesignatorType) expr; - if (this.issuerRequiredOnAttributeDesignators && jaxbAttrDes.getIssuer() == null) - { - throw MISSING_ATTRIBUTE_DESIGNATOR_ISSUER_EXCEPTION; - } - - final DatatypeFactory attrFactory = datatypeFactoryRegistry.getExtension(jaxbAttrDes.getDataType()); - if (attrFactory == null) - { - throw new IllegalArgumentException("Unsupported Datatype used in AttributeDesignator: " + jaxbAttrDes.getDataType()); - } - - expression = new AttributeDesignator<>(jaxbAttrDes, attrFactory.getBagDatatype(), attributeProvider); - } else if (expr instanceof AttributeSelectorType) - { - if (!allowAttributeSelectors) - { - throw UNSUPPORTED_ATTRIBUTE_SELECTOR_EXCEPTION; - } - - if (this.attributeProvider == null) - { - throw UNSUPPORTED_ATTRIBUTE_DESIGNATOR_OR_SELECTOR_BECAUSE_OF_NULL_ATTRIBUTE_PROVIDER_EXCEPTION; - } - - final AttributeSelectorType jaxbAttrSelector = (AttributeSelectorType) expr; - final DatatypeFactory attrFactory = datatypeFactoryRegistry.getExtension(jaxbAttrSelector.getDataType()); - if (attrFactory == null) - { - throw new IllegalArgumentException("Unsupported Datatype used in AttributeSelector: " + jaxbAttrSelector.getDataType()); - } - - // Check whether default XPath compiler/version specified for the - // XPath evaluator - if (xPathCompiler == null) - { - throw new IllegalArgumentException("AttributeSelector found but missing Policy(Set)Defaults/XPathVersion required for XPath evaluation in AttributeSelector"); - } - - try - { - expression = new AttributeSelectorExpression<>(jaxbAttrSelector, xPathCompiler, attributeProvider, attrFactory); - } catch (XPathExpressionException e) - { - throw new IllegalArgumentException("Invalid AttributeSelector's Path='" + jaxbAttrSelector.getPath() + "' into a XPath expression", e); - } - } else if (expr instanceof AttributeValueType) - { - expression = getInstance((AttributeValueType) expr, xPathCompiler); - } else if (expr instanceof FunctionType) - { - final FunctionType jaxbFunc = (FunctionType) expr; - final Function func = getFunction(jaxbFunc.getFunctionId()); - if (func != null) - { - expression = func; - } else - { - throw new IllegalArgumentException("Function " + jaxbFunc.getFunctionId() - + " is not supported (at least) as standalone Expression: either a generic higher-order function supported only as Apply FunctionId, or function completely unknown."); - } - } else if (expr instanceof VariableReferenceType) - { - final VariableReferenceType varRefElt = (VariableReferenceType) expr; - expression = getVariable(varRefElt, longestVarRefChain); - } else - { - throw new IllegalArgumentException("Expressions of type " + expr.getClass().getSimpleName() - + " are not supported. Expected: one of Apply, AttributeDesignator, AttributeSelector, AttributeValue, Function or VariableReference."); - } - - return expression; - } - - /* - * (non-Javadoc) - * - * @see com.thalesgroup.authzforce.core.eval.ExpressionFactory# createAttributeValueExpression(oasis .names.tc.xacml._3_0.core.schema.wd_17.AttributeValueType) - */ - @Override - public ValueExpression getInstance(AttributeValueType jaxbAttrVal, XPathCompiler xPathCompiler) throws IllegalArgumentException - { - return this.datatypeFactoryRegistry.createValueExpression(jaxbAttrVal, xPathCompiler); - } - - @Override - public void close() throws IOException - { - if (attributeProvider != null) - { - attributeProvider.close(); - } - } - -} +/** + * Copyright (C) 2011-2015 Thales Services SAS. + * + * This file is part of AuthZForce. + * + * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * AuthZForce 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . + */ +package org.ow2.authzforce.core.pdp.impl.expression; + +import java.io.IOException; +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.xml.xpath.XPathExpressionException; + +import net.sf.saxon.s9api.XPathCompiler; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.ApplyType; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeDesignatorType; +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.ExpressionType; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.FunctionType; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.VariableDefinition; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.VariableReferenceType; + +import org.ow2.authzforce.core.pdp.api.AttributeValue; +import org.ow2.authzforce.core.pdp.api.Datatype; +import org.ow2.authzforce.core.pdp.api.DatatypeFactory; +import org.ow2.authzforce.core.pdp.api.DatatypeFactoryRegistry; +import org.ow2.authzforce.core.pdp.api.Expression; +import org.ow2.authzforce.core.pdp.api.ExpressionFactory; +import org.ow2.authzforce.core.pdp.api.Function; +import org.ow2.authzforce.core.pdp.api.ValueExpression; +import org.ow2.authzforce.core.pdp.api.VariableReference; +import org.ow2.authzforce.core.pdp.impl.CloseableAttributeProvider; +import org.ow2.authzforce.core.pdp.impl.func.FunctionRegistry; +import org.ow2.authzforce.xmlns.pdp.ext.AbstractAttributeProvider; + +import com.sun.xacml.UnknownIdentifierException; + +/** + * Implementation of ExpressionFactory that supports the Expressions defined in VariableDefinitions in order to resolve VariableReferences. In particular, it makes sure the depth of recursivity of + * VariableDefinition does not exceed a value (to avoid inconveniences such as stackoverflow or very negative performance impact) defined by parameter to + * {@link #ExpressionFactoryImpl(DatatypeFactoryRegistry, FunctionRegistry, List, int, boolean, boolean)} parameter. Note that reference loops are avoided by the fact that a VariableReference can + * reference only a VariableDefinition defined previously to the VariableReference in this implementation. + * + * @author cdangerv + * @version $Id: $ + */ +public class ExpressionFactoryImpl implements ExpressionFactory +{ + private static final IllegalArgumentException MISSING_ATTRIBUTE_DESIGNATOR_ISSUER_EXCEPTION = new IllegalArgumentException( + "Missing Issuer that is required on AttributeDesignators by PDP configuration"); + + private static final IllegalArgumentException UNSUPPORTED_ATTRIBUTE_SELECTOR_EXCEPTION = new IllegalArgumentException("Unsupported Expression type (optional XACML feature): AttributeSelector"); + + private static final IllegalArgumentException NULL_FUNCTION_REGISTRY_EXCEPTION = new IllegalArgumentException("Undefined function registry"); + + private static final IllegalArgumentException NULL_ATTRIBUTE_DATATYPE_REGISTRY_EXCEPTION = new IllegalArgumentException("Undefined attribute datatype registry"); + + private static final IllegalArgumentException UNSUPPORTED_ATTRIBUTE_DESIGNATOR_OR_SELECTOR_BECAUSE_OF_NULL_ATTRIBUTE_PROVIDER_EXCEPTION = new IllegalArgumentException( + "Unsupported Expression type 'AttributeDesignator' and 'AttributeSelector' because no attribute Provider defined"); + + private static final int UNLIMITED_MAX_VARIABLE_REF_DEPTH = -1; + + private final DatatypeFactoryRegistry datatypeFactoryRegistry; + private final FunctionRegistry functionRegistry; + private final CloseableAttributeProvider attributeProvider; + private final int maxVariableReferenceDepth; + // the map from identifiers to internal data + private final Map> idToVariableMap = new HashMap<>(); + private final boolean allowAttributeSelectors; + + private final boolean issuerRequiredOnAttributeDesignators; + + /** + * Maximum VariableReference depth allowed for VariableDefinitions to be managed. Examples: + *
    + *
  • A VariableDefinition V1 that does not use any VariableReference has a reference depth of 0.
  • + *
  • A VariableDefinition V1 that uses a VariableReference to VariableDefinition V2 with no further VariableReference, has a reference depth of 1
  • + *
  • etc.
  • + *
+ * + * @param attributeFactory + * attribute value factory (not null) + * @param functionRegistry + * function registry (not null) + * @param jaxbAttributeProviderConfs + * XML/JAXB configurations of Attribute Providers for AttributeDesignator/AttributeSelector evaluation; may be null for static expression evaluation (out of context), in which case + * AttributeSelectors/AttributeDesignators are not supported + * @param maxVarRefDepth + * max depth of VariableReference chaining: VariableDefinition -> VariableDefinition ->... ('->' represents a VariableReference); strictly negative value means unlimited + * @param allowAttributeSelectors + * allow use of AttributeSelectors (experimental, not for production, use with caution) + * @param issuerRequiredInAttributeDesignators + * true iff it is required that all AttributeDesignator set the Issuer field, as a best practice. If the issuer is not set, remember what XACML 3.0 AttributeDesignator Evaluation says: + * "If the Issuer is not present in the attribute designator, then the matching of the attribute to the named attribute SHALL be governed by AttributeId and DataType attributes alone." + * As a result, be aware that if you use AttributeDesignators without Issuer ( {@code issuerRequiredInAttributeDesignators == false}) and the requests are using matching Attributes but + * with one or more different Issuers, this PDP engine has to gather all the values from all the attributes with matching Category/AttributeId but with any Issuer or no Issuer, + * resulting in lower performance. + * @throws java.lang.IllegalArgumentException + * If any of attribute Provider modules created from {@code jaxbAttributeProviderConfs} does not provide any attribute; or it is in conflict with another one already registered to + * provide the same or part of the same attributes. + * @throws java.io.IOException + * error closing the attribute Provider modules created from {@code jaxbAttributeProviderConfs}, when and before an {@link IllegalArgumentException} is raised + */ + public ExpressionFactoryImpl(DatatypeFactoryRegistry attributeFactory, FunctionRegistry functionRegistry, List jaxbAttributeProviderConfs, int maxVarRefDepth, + boolean allowAttributeSelectors, boolean issuerRequiredInAttributeDesignators) throws IllegalArgumentException, IOException + { + if (attributeFactory == null) + { + throw NULL_ATTRIBUTE_DATATYPE_REGISTRY_EXCEPTION; + } + + if (functionRegistry == null) + { + throw NULL_FUNCTION_REGISTRY_EXCEPTION; + } + + this.datatypeFactoryRegistry = attributeFactory; + this.functionRegistry = functionRegistry; + this.maxVariableReferenceDepth = maxVarRefDepth < 0 ? UNLIMITED_MAX_VARIABLE_REF_DEPTH : maxVarRefDepth; + // finally create the global attribute Provider used to resolve + // AttributeDesignators + this.attributeProvider = CloseableAttributeProvider.getInstance(jaxbAttributeProviderConfs, attributeFactory); + this.allowAttributeSelectors = allowAttributeSelectors; + this.issuerRequiredOnAttributeDesignators = issuerRequiredInAttributeDesignators; + } + + /** {@inheritDoc} */ + @Override + public VariableReference addVariable(VariableDefinition varDef, XPathCompiler xPathCompiler, Deque inoutLongestVarRefChain) throws IllegalArgumentException + { + final String varId = varDef.getVariableId(); + /* + * Initialize the longest variable reference chain from this VariableDefinition (varDef -> VarDef2 -> ..., where "v1 -> v2" means: v1's expression contains a VariableReference to v2) as empty + * for later update by this#getDefinition() when resolving a VariableReference within this varDef's expression (being parsed just after). The goal is to detect chains longer than + * this.maxVariableReferenceDepth to limit abuse of VariableReferences. There may be multiple VariableReferences in a VariableDefinition's expression, such as an Apply, and each may be + * referencing a different VariableDefinition; but we are interested only in the one with the longest chain of references. + */ + /* + * we need to check the longest variableReference chain does not have circular reference and does not exceed a specific value (need to call contains() method repeatedly and preserve the + * order). + */ + final Deque longestVarRefChain = inoutLongestVarRefChain == null ? new ArrayDeque() : inoutLongestVarRefChain; + final Expression varExpr = getInstance(varDef.getExpression().getValue(), xPathCompiler, longestVarRefChain); + final BaseVariableReference var = new BaseVariableReference<>(varId, varExpr, longestVarRefChain); + return idToVariableMap.put(varId, var); + } + + /** {@inheritDoc} */ + @Override + public VariableReference removeVariable(String varId) + { + return idToVariableMap.remove(varId); + } + + /** + * Resolves a VariableReference to the corresponding VariableReference(Definition) and validates the depth of VariableReference, i.e. the length of VariableReference chain. A chain of variable + * references is a list of VariableIds, such that V1 -> V2 ->... -> Vn, where 'V1 -> V2' means: V1's Expression contains a VariableReference to V2. + * + * @param jaxbVarRef + * the JAXB/XACML VariableReference with merely identifying a VariableDefinition by its VariableId + * + * @return VariableReference containing the resolved VariableDefinition's expression referenced by jaxbVarRef as known by this factory, or null if unknown + * @param longestVarRefChain + * If we are resolving a VariableReference in a VariableDefinition's expression (may be null if not), this is the longest chain of VariableReferences starting from a one in this + * VariableDefinition. If we are not resolving a VariableReference in a VariableDefinition's expression, this may be null.This is used to detect exceeding reference depth (see + * {@link #ExpressionFactoryImpl(int)} for the limit. In a Expression such as an Apply, we can have multiple VariableReferences referencing different VariableDefinitions. So we can have + * different depths of VariableReference references. We compare the length of the current longest chain with the one we would get by adding the longest one in the referenced + * VariableDefinition and jaxbVarRef's VariableId. If the latter is longer, its content becomes the content longestVarRefChain. + * @throws UnknownIdentifierException + * if VariableReference's VariableId is unknown by this factory + */ + private BaseVariableReference getVariable(VariableReferenceType jaxbVarRef, Deque longestVarRefChain) throws IllegalArgumentException + { + final String varId = jaxbVarRef.getVariableId(); + final BaseVariableReference var = idToVariableMap.get(varId); + if (var == null) + { + throw new IllegalArgumentException("VariableReference's VariableId=" + varId + " unknown in the current context, i.e. does not match any prior VariableDefinition's VariableId"); + } + + if (longestVarRefChain != null) + { + final Deque referencedVarLongestRefChain = var.getLongestVariableReferenceChain(); + if (referencedVarLongestRefChain.size() + 1 > longestVarRefChain.size()) + { + // current longest is no longer the longest, so replace with new + // longest's content + longestVarRefChain.clear(); + longestVarRefChain.add(varId); + longestVarRefChain.addAll(referencedVarLongestRefChain); + } + + if (maxVariableReferenceDepth != UNLIMITED_MAX_VARIABLE_REF_DEPTH && longestVarRefChain.size() > this.maxVariableReferenceDepth) + { + throw new IllegalArgumentException("Max allowed VariableReference depth (" + this.maxVariableReferenceDepth + ") exceeded by length (" + longestVarRefChain.size() + + ") of VariableReference Reference chain: " + longestVarRefChain); + } + + } + + return var; + } + + /** + * {@inheritDoc} + * + * Create a function instance using the function registry passed as parameter to {@link #ExpressionFactoryImpl(DatatypeFactoryRegistry, FunctionRegistry, List, int, boolean, boolean)} . + */ + @Override + public Function getFunction(String functionId) + { + return this.functionRegistry.getFunction(functionId); + } + + /** + * {@inheritDoc} + * + * Create a function instance using the function registry passed as parameter to {@link #ExpressionFactoryImpl(DatatypeFactoryRegistry, FunctionRegistry, List, int, boolean, boolean)} . + */ + @Override + public Function getFunction(String functionId, Datatype subFunctionReturnType) throws IllegalArgumentException + { + if (subFunctionReturnType == null) + { + return getFunction(functionId); + } + + final DatatypeFactory subFuncReturnTypeFactory = this.datatypeFactoryRegistry.getExtension(subFunctionReturnType.getId()); + if (subFuncReturnTypeFactory == null) + { + throw new IllegalArgumentException("Invalid sub-function's return type specified: unknown/unsupported ID: " + subFunctionReturnType.getId()); + } + + return this.functionRegistry.getFunction(functionId, subFuncReturnTypeFactory); + } + + /* + * (non-Javadoc) + * + * @see com.thalesgroup.authzforce.core.eval.ExpressionFactory#getInstance(oasis. names.tc.xacml._3_0 .core.schema.wd_17.ExpressionType, oasis.names.tc.xacml._3_0.core.schema.wd_17.DefaultsType, + * java.util.List) + */ + /** {@inheritDoc} */ + @Override + public Expression getInstance(ExpressionType expr, XPathCompiler xPathCompiler, Deque longestVarRefChain) throws IllegalArgumentException + { + final Expression expression; + /* + * We check all types of Expression: , , , , and + */ + if (expr instanceof ApplyType) + { + expression = Apply.getInstance((ApplyType) expr, xPathCompiler, this, longestVarRefChain); + } else if (expr instanceof AttributeDesignatorType) + { + if (this.attributeProvider == null) + { + throw UNSUPPORTED_ATTRIBUTE_DESIGNATOR_OR_SELECTOR_BECAUSE_OF_NULL_ATTRIBUTE_PROVIDER_EXCEPTION; + } + + final AttributeDesignatorType jaxbAttrDes = (AttributeDesignatorType) expr; + if (this.issuerRequiredOnAttributeDesignators && jaxbAttrDes.getIssuer() == null) + { + throw MISSING_ATTRIBUTE_DESIGNATOR_ISSUER_EXCEPTION; + } + + final DatatypeFactory attrFactory = datatypeFactoryRegistry.getExtension(jaxbAttrDes.getDataType()); + if (attrFactory == null) + { + throw new IllegalArgumentException("Unsupported Datatype used in AttributeDesignator: " + jaxbAttrDes.getDataType()); + } + + expression = new AttributeDesignator<>(jaxbAttrDes, attrFactory.getBagDatatype(), attributeProvider); + } else if (expr instanceof AttributeSelectorType) + { + if (!allowAttributeSelectors) + { + throw UNSUPPORTED_ATTRIBUTE_SELECTOR_EXCEPTION; + } + + if (this.attributeProvider == null) + { + throw UNSUPPORTED_ATTRIBUTE_DESIGNATOR_OR_SELECTOR_BECAUSE_OF_NULL_ATTRIBUTE_PROVIDER_EXCEPTION; + } + + final AttributeSelectorType jaxbAttrSelector = (AttributeSelectorType) expr; + final DatatypeFactory attrFactory = datatypeFactoryRegistry.getExtension(jaxbAttrSelector.getDataType()); + if (attrFactory == null) + { + throw new IllegalArgumentException("Unsupported Datatype used in AttributeSelector: " + jaxbAttrSelector.getDataType()); + } + + // Check whether default XPath compiler/version specified for the + // XPath evaluator + if (xPathCompiler == null) + { + throw new IllegalArgumentException("AttributeSelector found but missing Policy(Set)Defaults/XPathVersion required for XPath evaluation in AttributeSelector"); + } + + try + { + expression = new AttributeSelectorExpression<>(jaxbAttrSelector, xPathCompiler, attributeProvider, attrFactory); + } catch (XPathExpressionException e) + { + throw new IllegalArgumentException("Invalid AttributeSelector's Path='" + jaxbAttrSelector.getPath() + "' into a XPath expression", e); + } + } else if (expr instanceof AttributeValueType) + { + expression = getInstance((AttributeValueType) expr, xPathCompiler); + } else if (expr instanceof FunctionType) + { + final FunctionType jaxbFunc = (FunctionType) expr; + final Function func = getFunction(jaxbFunc.getFunctionId()); + if (func != null) + { + expression = func; + } else + { + throw new IllegalArgumentException("Function " + jaxbFunc.getFunctionId() + + " is not supported (at least) as standalone Expression: either a generic higher-order function supported only as Apply FunctionId, or function completely unknown."); + } + } else if (expr instanceof VariableReferenceType) + { + final VariableReferenceType varRefElt = (VariableReferenceType) expr; + expression = getVariable(varRefElt, longestVarRefChain); + } else + { + throw new IllegalArgumentException("Expressions of type " + expr.getClass().getSimpleName() + + " are not supported. Expected: one of Apply, AttributeDesignator, AttributeSelector, AttributeValue, Function or VariableReference."); + } + + return expression; + } + + /* + * (non-Javadoc) + * + * @see com.thalesgroup.authzforce.core.eval.ExpressionFactory# createAttributeValueExpression(oasis .names.tc.xacml._3_0.core.schema.wd_17.AttributeValueType) + */ + /** {@inheritDoc} */ + @Override + public ValueExpression getInstance(AttributeValueType jaxbAttrVal, XPathCompiler xPathCompiler) throws IllegalArgumentException + { + return this.datatypeFactoryRegistry.createValueExpression(jaxbAttrVal, xPathCompiler); + } + + /** {@inheritDoc} */ + @Override + public void close() throws IOException + { + if (attributeProvider != null) + { + attributeProvider.close(); + } + } + +} diff --git a/src/main/java/org/ow2/authzforce/core/pdp/impl/expression/PrimitiveValueExpression.java b/src/main/java/org/ow2/authzforce/core/pdp/impl/expression/PrimitiveValueExpression.java index fdbfe0ec..e003dfa8 100644 --- a/src/main/java/org/ow2/authzforce/core/pdp/impl/expression/PrimitiveValueExpression.java +++ b/src/main/java/org/ow2/authzforce/core/pdp/impl/expression/PrimitiveValueExpression.java @@ -1,59 +1,60 @@ -/** - * Copyright (C) 2012-2015 Thales Services SAS. - * - * This file is part of AuthZForce CE. - * - * AuthZForce CE is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software - * Foundation, either version 3 of the License, or (at your option) any later version. - * - * AuthZForce CE 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with AuthZForce CE. If not, see . - */ -package org.ow2.authzforce.core.pdp.impl.expression; - -import javax.xml.bind.JAXBElement; - -import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeValueType; - -import org.ow2.authzforce.core.pdp.api.AttributeValue; -import org.ow2.authzforce.core.pdp.api.Datatype; -import org.ow2.authzforce.core.pdp.api.JaxbXACMLUtils; -import org.ow2.authzforce.core.pdp.api.ValueExpression; - -/** - * - * Expression wrapper for primitive static values to be used as Expressions, e.g. as function arguments; 'static' here means the actual value does not depend on - * the evaluation context; it evaluates to itself. - * - * @param - * concrete value type - * - */ -public final class PrimitiveValueExpression extends ValueExpression -{ - - /** - * Creates instance - * - * @param type - * value datatype - * @param v - * static value - * @param isStatic - * true iff the expression based on this value always evaluates to the same constant in any context (not the case for xpathExpressions for - * instance) - */ - public PrimitiveValueExpression(Datatype type, V v, boolean isStatic) - { - super(type, v, isStatic); - } - - @Override - public JAXBElement getJAXBElement() - { - // create new JAXB AttributeValue as defensive copy (JAXB AttributeValue is not immutable) - return JaxbXACMLUtils.XACML_3_0_OBJECT_FACTORY.createAttributeValue(this.value); - } -} +/** + * Copyright (C) 2012-2015 Thales Services SAS. + * + * This file is part of AuthZForce CE. + * + * AuthZForce CE is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * AuthZForce CE 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with AuthZForce CE. If not, see . + */ +package org.ow2.authzforce.core.pdp.impl.expression; + +import javax.xml.bind.JAXBElement; + +import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeValueType; + +import org.ow2.authzforce.core.pdp.api.AttributeValue; +import org.ow2.authzforce.core.pdp.api.Datatype; +import org.ow2.authzforce.core.pdp.api.JaxbXACMLUtils; +import org.ow2.authzforce.core.pdp.api.ValueExpression; + +/** + * + * Expression wrapper for primitive static values to be used as Expressions, e.g. as function arguments; 'static' here means the actual value does not depend on the evaluation context; it evaluates to + * itself. + * + * @param + * concrete value type + * @author cdangerv + * @version $Id: $ + */ +public final class PrimitiveValueExpression extends ValueExpression +{ + + /** + * Creates instance + * + * @param type + * value datatype + * @param v + * static value + * @param isStatic + * true iff the expression based on this value always evaluates to the same constant in any context (not the case for xpathExpressions for instance) + */ + public PrimitiveValueExpression(Datatype type, V v, boolean isStatic) + { + super(type, v, isStatic); + } + + /** {@inheritDoc} */ + @Override + public JAXBElement getJAXBElement() + { + // create new JAXB AttributeValue as defensive copy (JAXB AttributeValue is not immutable) + return JaxbXACMLUtils.XACML_3_0_OBJECT_FACTORY.createAttributeValue(this.value); + } +} diff --git a/src/main/java/org/ow2/authzforce/core/pdp/impl/func/BaseFunctionSet.java b/src/main/java/org/ow2/authzforce/core/pdp/impl/func/BaseFunctionSet.java index 2e5c84e8..9bbdfa9c 100644 --- a/src/main/java/org/ow2/authzforce/core/pdp/impl/func/BaseFunctionSet.java +++ b/src/main/java/org/ow2/authzforce/core/pdp/impl/func/BaseFunctionSet.java @@ -1,112 +1,118 @@ -/** - * Copyright (C) 2011-2015 Thales Services SAS. - * - * This file is part of AuthZForce. - * - * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software - * Foundation, either version 3 of the License, or (at your option) any later version. - * - * AuthZForce 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . - */ -package org.ow2.authzforce.core.pdp.impl.func; - -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; - -import org.ow2.authzforce.core.pdp.api.Function; -import org.ow2.authzforce.core.pdp.api.FunctionSet; - -/** - * Base class for {@link FunctionSet}s - */ -public class BaseFunctionSet implements FunctionSet -{ - /** - * Namespace to be used as default prefix for internal function set IDs - */ - public static final String DEFAULT_ID_NAMESPACE = "urn:ow2:authzforce:xacml:function-set:"; - - private final String id; - - private final Set> functions; - - /** - * Constructor from an identifier and an array of functions - * - * @param id - * globally unique ID of this function set, to be used as PDP extension ID - * @param functions - * functions added to the set - */ - public BaseFunctionSet(String id, Function... functions) - { - this(id, new HashSet<>(Arrays.asList(functions))); - } - - /** - * Constructor from an identifier and a set of functions - * - * @param id - * globally unique ID of this function set, to be used as PDP extension ID - * @param functions - * functions added to the set. This function set uses a immutable copy of this input. - */ - public BaseFunctionSet(String id, Set> functions) - { - this.id = id; - this.functions = Collections.unmodifiableSet(functions); - } - - /** - * Returns a single instance of each of the functions supported by some class. The Set must contain instances of Function, and it must be both non-null and non-empty. It - * may contain only a single Function. - * - * @return the functions members of this group - */ - @Override - public Set> getSupportedFunctions() - { - return functions; - } - - @Override - public String getId() - { - return id; - } - - private volatile int hashCode = 0; - - @Override - public int hashCode() - { - if (hashCode == 0) - { - hashCode = id.hashCode(); - } - - return hashCode; - } - - @Override - public boolean equals(Object obj) - { - if (this == obj) - { - return true; - } - - if (!(obj instanceof BaseFunctionSet)) - { - return false; - } - BaseFunctionSet other = (BaseFunctionSet) obj; - return this.id.equals(other.id); - } - -} +/** + * Copyright (C) 2011-2015 Thales Services SAS. + * + * This file is part of AuthZForce. + * + * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * AuthZForce 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . + */ +package org.ow2.authzforce.core.pdp.impl.func; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import org.ow2.authzforce.core.pdp.api.Function; +import org.ow2.authzforce.core.pdp.api.FunctionSet; + +/** + * Base class for {@link FunctionSet}s + * + * @author cdangerv + * @version $Id: $ + */ +public class BaseFunctionSet implements FunctionSet +{ + /** + * Namespace to be used as default prefix for internal function set IDs + */ + public static final String DEFAULT_ID_NAMESPACE = "urn:ow2:authzforce:xacml:function-set:"; + + private final String id; + + private final Set> functions; + + /** + * Constructor from an identifier and an array of functions + * + * @param id + * globally unique ID of this function set, to be used as PDP extension ID + * @param functions + * functions added to the set + */ + public BaseFunctionSet(String id, Function... functions) + { + this(id, new HashSet<>(Arrays.asList(functions))); + } + + /** + * Constructor from an identifier and a set of functions + * + * @param id + * globally unique ID of this function set, to be used as PDP extension ID + * @param functions + * functions added to the set. This function set uses a immutable copy of this input. + */ + public BaseFunctionSet(String id, Set> functions) + { + this.id = id; + this.functions = Collections.unmodifiableSet(functions); + } + + /** + * {@inheritDoc} + * + * Returns a single instance of each of the functions supported by some class. The Set must contain instances of Function, and it must be both non-null and non-empty. It + * may contain only a single Function. + */ + @Override + public Set> getSupportedFunctions() + { + return functions; + } + + /** {@inheritDoc} */ + @Override + public String getId() + { + return id; + } + + private volatile int hashCode = 0; + + /** {@inheritDoc} */ + @Override + public int hashCode() + { + if (hashCode == 0) + { + hashCode = id.hashCode(); + } + + return hashCode; + } + + /** {@inheritDoc} */ + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + + if (!(obj instanceof BaseFunctionSet)) + { + return false; + } + BaseFunctionSet other = (BaseFunctionSet) obj; + return this.id.equals(other.id); + } + +} diff --git a/src/main/java/org/ow2/authzforce/core/pdp/impl/func/ComparisonFunction.java b/src/main/java/org/ow2/authzforce/core/pdp/impl/func/ComparisonFunction.java index f9d747d3..aa9b0db9 100644 --- a/src/main/java/org/ow2/authzforce/core/pdp/impl/func/ComparisonFunction.java +++ b/src/main/java/org/ow2/authzforce/core/pdp/impl/func/ComparisonFunction.java @@ -1,237 +1,240 @@ -/** - * Copyright (C) 2011-2015 Thales Services SAS. - * - * This file is part of AuthZForce. - * - * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software - * Foundation, either version 3 of the License, or (at your option) any later version. - * - * AuthZForce 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . - */ -/** - * - */ -package org.ow2.authzforce.core.pdp.impl.func; - -import java.util.Arrays; -import java.util.Deque; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import org.ow2.authzforce.core.pdp.api.AttributeValue; -import org.ow2.authzforce.core.pdp.api.Datatype; -import org.ow2.authzforce.core.pdp.api.Expression; -import org.ow2.authzforce.core.pdp.api.FirstOrderFunction; -import org.ow2.authzforce.core.pdp.api.FirstOrderFunctionCall; -import org.ow2.authzforce.core.pdp.api.FirstOrderFunctionCall.EagerSinglePrimitiveTypeEval; -import org.ow2.authzforce.core.pdp.api.Function; -import org.ow2.authzforce.core.pdp.api.FunctionSet; -import org.ow2.authzforce.core.pdp.api.FunctionSignature; -import org.ow2.authzforce.core.pdp.api.IndeterminateEvaluationException; -import org.ow2.authzforce.core.pdp.api.StatusHelper; -import org.ow2.authzforce.core.pdp.impl.value.BooleanValue; -import org.ow2.authzforce.core.pdp.impl.value.DatatypeConstants; - -/** - * A superclass of all the standard comparison functions (return a boolean). - * - * @param - * function parameter type - */ -public final class ComparisonFunction> extends FirstOrderFunction.SingleParameterTyped -{ - enum PostCondition - { - - /** - * - */ - GREATER_THAN("-greater-than", new Checker() - { - @Override - public boolean check(int comparisonResult) - { - return comparisonResult > 0; - } - }), - /** - * - */ - GREATER_THAN_OR_EQUAL("-greater-than-or-equal", new Checker() - { - @Override - public boolean check(int comparisonResult) - { - return comparisonResult >= 0; - } - }), - /** - * - */ - LESS_THAN("-less-than", new Checker() - { - @Override - public boolean check(int comparisonResult) - { - return comparisonResult < 0; - } - }), - /** - * - */ - LESS_THAN_OR_EQUAL("-less-than-or-equal", new Checker() - { - @Override - public boolean check(int comparisonResult) - { - return comparisonResult <= 0; - } - }); - - private final String functionSuffix; - private final Checker checker; - - private PostCondition(String funcSuffix, Checker checker) - { - this.functionSuffix = funcSuffix; - this.checker = checker; - } - - boolean isTrue(int comparisonResult) - { - return checker.check(comparisonResult); - } - - private interface Checker - { - boolean check(int comparisonResult); - } - } - - private static final class CallFactory> - { - private final PostCondition postCondition; - private final FunctionSignature.SingleParameterTyped funcSig; - private final String illegalComparisonMsgPrefix; - - /** - * Creates comparison function call factory - * - * @param condition - * post-condition to hold true when comparing the result of arg0.compareTo(arg1) to zero; where compateTo() function is similar - * to {@link Comparable#compareTo(Object)}. - */ - private CallFactory(FunctionSignature.SingleParameterTyped functionSig, PostCondition postCondition) - { - this.funcSig = functionSig; - this.postCondition = postCondition; - illegalComparisonMsgPrefix = "Function " + funcSig.getName() + ": cannot compare arguments: "; - } - - private FirstOrderFunctionCall getInstance(List> argExpressions, Datatype[] remainingArgTypes) - throws IllegalArgumentException - { - return new EagerSinglePrimitiveTypeEval(funcSig, argExpressions, remainingArgTypes) - { - - @Override - protected BooleanValue evaluate(Deque args) throws IndeterminateEvaluationException - { - // Now that we have real values, perform the comparison operation - final V arg0 = args.poll(); - final V arg1 = args.poll(); - final int comparResult; - try - { - comparResult = arg0.compareTo(arg1); - } catch (IllegalArgumentException e) - { - // See BaseTimeValue#compareTo() for example of comparison throwing such exception - throw new IndeterminateEvaluationException(illegalComparisonMsgPrefix + arg0.getContent() + ", " + arg1.getContent(), - StatusHelper.STATUS_PROCESSING_ERROR, e); - } - // Return the result as a BooleanAttributeValue. - return BooleanValue.valueOf(postCondition.isTrue(comparResult)); - } - }; - } - - } - - private final CallFactory funcCallFactory; - - /** - * Creates a new BaseComparisonFunction object. - * - * @param paramTypeDef - * parameter type - * @param postCondition - * post-condition to hold true when comparing the result of arg0.compareTo(arg1) to zero; where compateTo() function is similar to - * {@link Comparable#compareTo(Object)} - * - * @throws IllegalArgumentException - * if the function is unknown - */ - private ComparisonFunction(DatatypeConstants paramTypeDef, PostCondition postCondition) - { - super(paramTypeDef.FUNCTION_ID_PREFIX + postCondition.functionSuffix, DatatypeConstants.BOOLEAN.TYPE, false, Arrays.asList(paramTypeDef.TYPE, - paramTypeDef.TYPE)); - this.funcCallFactory = new CallFactory<>(functionSignature, postCondition); - } - - private static Set> getTotalComparisonFunctions() - { - final Set> mutableSet = new HashSet<>(); - for (final PostCondition condition : PostCondition.values()) - { - mutableSet.add(new ComparisonFunction<>(DatatypeConstants.INTEGER, condition)); - mutableSet.add(new ComparisonFunction<>(DatatypeConstants.DOUBLE, condition)); - mutableSet.add(new ComparisonFunction<>(DatatypeConstants.STRING, condition)); - } - - return mutableSet; - } - - /** - * Set of functions implementing comparison of {@link Comparable} values, i.e. imposing total ordering of compared objects. In particular, this applies to - * all numeric types (integers, doubles...) and string, but not to XML schema date/times that may have indeterminate relationship to each other (see - * {@link #TEMPORAL_SET}). - */ - public static final FunctionSet TOTAL_ORDER_SET = new BaseFunctionSet(FunctionSet.DEFAULT_ID_NAMESPACE + "total-order-comparison", - getTotalComparisonFunctions()); - - /* - * (non-Javadoc) - * - * @see com.thalesgroup.authzforce.core.func.FirstOrderFunction#getFunctionCall(java.util.List, com.thalesgroup.authzforce.core.eval.DatatypeDef[]) - */ - @Override - public FirstOrderFunctionCall newCall(List> argExpressions, Datatype... remainingArgTypes) - { - return funcCallFactory.getInstance(argExpressions, remainingArgTypes); - } - - private static Set> getTemporalFunctions() - { - final Set> mutableSet = new HashSet<>(); - for (final PostCondition condition : PostCondition.values()) - { - mutableSet.add(new ComparisonFunction<>(DatatypeConstants.TIME, condition)); - mutableSet.add(new ComparisonFunction<>(DatatypeConstants.DATE, condition)); - mutableSet.add(new ComparisonFunction<>(DatatypeConstants.DATETIME, condition)); - } - - return mutableSet; - } - - /** - * Set of functions comparing XML schema date/time values, i.e. not imposing total ordering of compared objects, as such date/times may have indeterminate - * relationship to each other. - * - */ - public static final FunctionSet TEMPORAL_SET = new BaseFunctionSet(FunctionSet.DEFAULT_ID_NAMESPACE + "temporal-comparison", getTemporalFunctions()); -} +/** + * Copyright (C) 2011-2015 Thales Services SAS. + * + * This file is part of AuthZForce. + * + * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * AuthZForce 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . + */ +/** + * + */ +package org.ow2.authzforce.core.pdp.impl.func; + +import java.util.Arrays; +import java.util.Deque; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.ow2.authzforce.core.pdp.api.AttributeValue; +import org.ow2.authzforce.core.pdp.api.Datatype; +import org.ow2.authzforce.core.pdp.api.Expression; +import org.ow2.authzforce.core.pdp.api.FirstOrderFunction; +import org.ow2.authzforce.core.pdp.api.FirstOrderFunctionCall; +import org.ow2.authzforce.core.pdp.api.FirstOrderFunctionCall.EagerSinglePrimitiveTypeEval; +import org.ow2.authzforce.core.pdp.api.Function; +import org.ow2.authzforce.core.pdp.api.FunctionSet; +import org.ow2.authzforce.core.pdp.api.FunctionSignature; +import org.ow2.authzforce.core.pdp.api.IndeterminateEvaluationException; +import org.ow2.authzforce.core.pdp.api.StatusHelper; +import org.ow2.authzforce.core.pdp.impl.value.BooleanValue; +import org.ow2.authzforce.core.pdp.impl.value.DatatypeConstants; + +/** + * A superclass of all the standard comparison functions (return a boolean). + * + * @param + * function parameter type + * @author cdangerv + * @version $Id: $ + */ +public final class ComparisonFunction> extends FirstOrderFunction.SingleParameterTyped +{ + enum PostCondition + { + + /** + * + */ + GREATER_THAN("-greater-than", new Checker() + { + @Override + public boolean check(int comparisonResult) + { + return comparisonResult > 0; + } + }), + /** + * + */ + GREATER_THAN_OR_EQUAL("-greater-than-or-equal", new Checker() + { + @Override + public boolean check(int comparisonResult) + { + return comparisonResult >= 0; + } + }), + /** + * + */ + LESS_THAN("-less-than", new Checker() + { + @Override + public boolean check(int comparisonResult) + { + return comparisonResult < 0; + } + }), + /** + * + */ + LESS_THAN_OR_EQUAL("-less-than-or-equal", new Checker() + { + @Override + public boolean check(int comparisonResult) + { + return comparisonResult <= 0; + } + }); + + private final String functionSuffix; + private final Checker checker; + + private PostCondition(String funcSuffix, Checker checker) + { + this.functionSuffix = funcSuffix; + this.checker = checker; + } + + boolean isTrue(int comparisonResult) + { + return checker.check(comparisonResult); + } + + private interface Checker + { + boolean check(int comparisonResult); + } + } + + private static final class CallFactory> + { + private final PostCondition postCondition; + private final FunctionSignature.SingleParameterTyped funcSig; + private final String illegalComparisonMsgPrefix; + + /** + * Creates comparison function call factory + * + * @param condition + * post-condition to hold true when comparing the result of arg0.compareTo(arg1) to zero; where compateTo() function is similar + * to {@link Comparable#compareTo(Object)}. + */ + private CallFactory(FunctionSignature.SingleParameterTyped functionSig, PostCondition postCondition) + { + this.funcSig = functionSig; + this.postCondition = postCondition; + illegalComparisonMsgPrefix = "Function " + funcSig.getName() + ": cannot compare arguments: "; + } + + private FirstOrderFunctionCall getInstance(List> argExpressions, Datatype[] remainingArgTypes) + throws IllegalArgumentException + { + return new EagerSinglePrimitiveTypeEval(funcSig, argExpressions, remainingArgTypes) + { + + @Override + protected BooleanValue evaluate(Deque args) throws IndeterminateEvaluationException + { + // Now that we have real values, perform the comparison operation + final V arg0 = args.poll(); + final V arg1 = args.poll(); + final int comparResult; + try + { + comparResult = arg0.compareTo(arg1); + } catch (IllegalArgumentException e) + { + // See BaseTimeValue#compareTo() for example of comparison throwing such exception + throw new IndeterminateEvaluationException(illegalComparisonMsgPrefix + arg0.getContent() + ", " + arg1.getContent(), + StatusHelper.STATUS_PROCESSING_ERROR, e); + } + // Return the result as a BooleanAttributeValue. + return BooleanValue.valueOf(postCondition.isTrue(comparResult)); + } + }; + } + + } + + private final CallFactory funcCallFactory; + + /** + * Creates a new BaseComparisonFunction object. + * + * @param paramTypeDef + * parameter type + * @param postCondition + * post-condition to hold true when comparing the result of arg0.compareTo(arg1) to zero; where compateTo() function is similar to + * {@link Comparable#compareTo(Object)} + * + * @throws IllegalArgumentException + * if the function is unknown + */ + private ComparisonFunction(DatatypeConstants paramTypeDef, PostCondition postCondition) + { + super(paramTypeDef.FUNCTION_ID_PREFIX + postCondition.functionSuffix, DatatypeConstants.BOOLEAN.TYPE, false, Arrays.asList(paramTypeDef.TYPE, + paramTypeDef.TYPE)); + this.funcCallFactory = new CallFactory<>(functionSignature, postCondition); + } + + private static Set> getTotalComparisonFunctions() + { + final Set> mutableSet = new HashSet<>(); + for (final PostCondition condition : PostCondition.values()) + { + mutableSet.add(new ComparisonFunction<>(DatatypeConstants.INTEGER, condition)); + mutableSet.add(new ComparisonFunction<>(DatatypeConstants.DOUBLE, condition)); + mutableSet.add(new ComparisonFunction<>(DatatypeConstants.STRING, condition)); + } + + return mutableSet; + } + + /** + * Set of functions implementing comparison of {@link Comparable} values, i.e. imposing total ordering of compared objects. In particular, this applies to + * all numeric types (integers, doubles...) and string, but not to XML schema date/times that may have indeterminate relationship to each other (see + * {@link #TEMPORAL_SET}). + */ + public static final FunctionSet TOTAL_ORDER_SET = new BaseFunctionSet(FunctionSet.DEFAULT_ID_NAMESPACE + "total-order-comparison", + getTotalComparisonFunctions()); + + /* + * (non-Javadoc) + * + * @see com.thalesgroup.authzforce.core.func.FirstOrderFunction#getFunctionCall(java.util.List, com.thalesgroup.authzforce.core.eval.DatatypeDef[]) + */ + /** {@inheritDoc} */ + @Override + public FirstOrderFunctionCall newCall(List> argExpressions, Datatype... remainingArgTypes) + { + return funcCallFactory.getInstance(argExpressions, remainingArgTypes); + } + + private static Set> getTemporalFunctions() + { + final Set> mutableSet = new HashSet<>(); + for (final PostCondition condition : PostCondition.values()) + { + mutableSet.add(new ComparisonFunction<>(DatatypeConstants.TIME, condition)); + mutableSet.add(new ComparisonFunction<>(DatatypeConstants.DATE, condition)); + mutableSet.add(new ComparisonFunction<>(DatatypeConstants.DATETIME, condition)); + } + + return mutableSet; + } + + /** + * Set of functions comparing XML schema date/time values, i.e. not imposing total ordering of compared objects, as such date/times may have indeterminate + * relationship to each other. + * + */ + public static final FunctionSet TEMPORAL_SET = new BaseFunctionSet(FunctionSet.DEFAULT_ID_NAMESPACE + "temporal-comparison", getTemporalFunctions()); +} diff --git a/src/main/java/org/ow2/authzforce/core/pdp/impl/func/DatatypeConversionFunction.java b/src/main/java/org/ow2/authzforce/core/pdp/impl/func/DatatypeConversionFunction.java index 9bbc7dd4..87f577bf 100644 --- a/src/main/java/org/ow2/authzforce/core/pdp/impl/func/DatatypeConversionFunction.java +++ b/src/main/java/org/ow2/authzforce/core/pdp/impl/func/DatatypeConversionFunction.java @@ -1,428 +1,431 @@ -/** - * Copyright (C) 2011-2015 Thales Services SAS. - * - * This file is part of AuthZForce. - * - * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software - * Foundation, either version 3 of the License, or (at your option) any later version. - * - * AuthZForce 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . - */ -package org.ow2.authzforce.core.pdp.impl.func; - -import java.util.Arrays; -import java.util.Deque; -import java.util.List; - -import org.ow2.authzforce.core.pdp.api.AttributeValue; -import org.ow2.authzforce.core.pdp.api.Datatype; -import org.ow2.authzforce.core.pdp.api.Expression; -import org.ow2.authzforce.core.pdp.api.FirstOrderFunction; -import org.ow2.authzforce.core.pdp.api.FirstOrderFunctionCall; -import org.ow2.authzforce.core.pdp.api.FirstOrderFunctionCall.EagerSinglePrimitiveTypeEval; -import org.ow2.authzforce.core.pdp.api.FunctionSet; -import org.ow2.authzforce.core.pdp.api.FunctionSignature; -import org.ow2.authzforce.core.pdp.api.IndeterminateEvaluationException; -import org.ow2.authzforce.core.pdp.api.StatusHelper; -import org.ow2.authzforce.core.pdp.api.Value; -import org.ow2.authzforce.core.pdp.impl.value.AnyURIValue; -import org.ow2.authzforce.core.pdp.impl.value.BooleanValue; -import org.ow2.authzforce.core.pdp.impl.value.DNSNameValue; -import org.ow2.authzforce.core.pdp.impl.value.DatatypeConstants; -import org.ow2.authzforce.core.pdp.impl.value.DateTimeValue; -import org.ow2.authzforce.core.pdp.impl.value.DateValue; -import org.ow2.authzforce.core.pdp.impl.value.DayTimeDurationValue; -import org.ow2.authzforce.core.pdp.impl.value.DoubleValue; -import org.ow2.authzforce.core.pdp.impl.value.IPAddressValue; -import org.ow2.authzforce.core.pdp.impl.value.IntegerValue; -import org.ow2.authzforce.core.pdp.impl.value.RFC822NameValue; -import org.ow2.authzforce.core.pdp.impl.value.SimpleValue; -import org.ow2.authzforce.core.pdp.impl.value.StringValue; -import org.ow2.authzforce.core.pdp.impl.value.TimeValue; -import org.ow2.authzforce.core.pdp.impl.value.X500NameValue; -import org.ow2.authzforce.core.pdp.impl.value.YearMonthDurationValue; - -/** - * A class that implements all the primitive datatype conversion functions: double-to-integer, integer-to-double, *-from-string, *-to-string, etc. It takes one - * argument of the appropriate type, converts that argument to the other type, and returns the result. - * - * @param - * parameter/input type - * @param - * return/output type - */ -public final class DatatypeConversionFunction extends - FirstOrderFunction.SingleParameterTyped -{ - - /** - * Standard identifier for the double-to-integer function. - */ - public static final String NAME_DOUBLE_TO_INTEGER = XACML_NS_1_0 + "double-to-integer"; - - /** - * Standard identifier for the integer-to-double function. - */ - public static final String NAME_INTEGER_TO_DOUBLE = XACML_NS_1_0 + "integer-to-double"; - - /** - * Standard identifier for the boolean-from-string function. - */ - public static final String NAME_BOOLEAN_FROM_STRING = XACML_NS_3_0 + "boolean-from-string"; - - /** - * Standard identifier for the string-from-boolean function. - */ - public static final String NAME_STRING_FROM_BOOLEAN = XACML_NS_3_0 + "string-from-boolean"; - - /** - * Standard identifier for the integer-from-string function. - */ - public static final String NAME_INTEGER_FROM_STRING = XACML_NS_3_0 + "integer-from-string"; - - /** - * Standard identifier for the string-from-integer function. - */ - public static final String NAME_STRING_FROM_INTEGER = XACML_NS_3_0 + "string-from-integer"; - - /** - * Standard identifier for the double-from-string function. - */ - public static final String NAME_DOUBLE_FROM_STRING = XACML_NS_3_0 + "double-from-string"; - - /** - * Standard identifier for the string-from-double function. - */ - public static final String NAME_STRING_FROM_DOUBLE = XACML_NS_3_0 + "string-from-double"; - - /** - * Standard identifier for the time-from-string function. - */ - public static final String NAME_TIME_FROM_STRING = XACML_NS_3_0 + "time-from-string"; - - /** - * Standard identifier for the string-from-time function. - */ - public static final String NAME_STRING_FROM_TIME = XACML_NS_3_0 + "string-from-time"; - - /** - * Standard identifier for the date-from-string function. - */ - public static final String NAME_DATE_FROM_STRING = XACML_NS_3_0 + "date-from-string"; - - /** - * Standard identifier for the string-from-date function. - */ - public static final String NAME_STRING_FROM_DATE = XACML_NS_3_0 + "string-from-date"; - - /** - * Standard identifier for the dateTime-from-string function. - */ - public static final String NAME_DATETIME_FROM_STRING = XACML_NS_3_0 + "dateTime-from-string"; - - /** - * Standard identifier for the string-from-dateTime function. - */ - public static final String NAME_STRING_FROM_DATETIME = XACML_NS_3_0 + "string-from-dateTime"; - - /** - * Standard identifier for the anyURI-from-string function. - */ - public static final String NAME_ANYURI_FROM_STRING = XACML_NS_3_0 + "anyURI-from-string"; - - /** - * Standard identifier for the string-from-anyURI function. - */ - public static final String NAME_STRING_FROM_ANYURI = XACML_NS_3_0 + "string-from-anyURI"; - - /** - * Standard identifier for the dayTimeDuration-from-string function. - */ - public static final String NAME_DAYTIMEDURATION_FROM_STRING = XACML_NS_3_0 + "dayTimeDuration-from-string"; - - /** - * Standard identifier for the string-from-dayTimeDuration function. - */ - public static final String NAME_STRING_FROM_DAYTIMEDURATION = XACML_NS_3_0 + "string-from-dayTimeDuration"; - - /** - * Standard identifier for the yearMonthDuration-from-string function. - */ - public static final String NAME_YEARMONTHDURATION_FROM_STRING = XACML_NS_3_0 + "yearMonthDuration-from-string"; - - /** - * Standard identifier for the string-from-yearMonthDuration function. - */ - public static final String NAME_STRING_FROM_YEARMONTHDURATION = XACML_NS_3_0 + "string-from-yearMonthDuration"; - - /** - * Standard identifier for the x500Name-from-string function. - */ - public static final String NAME_X500NAME_FROM_STRING = XACML_NS_3_0 + "x500Name-from-string"; - - /** - * Standard identifier for the string-from-x500Name function. - */ - public static final String NAME_STRING_FROM_X500NAME = XACML_NS_3_0 + "string-from-x500Name"; - - /** - * Standard identifier for the rfc822Name-from-string function. - */ - public static final String NAME_RFC822NAME_FROM_STRING = XACML_NS_3_0 + "rfc822Name-from-string"; - - /** - * Standard identifier for the string-from-rfc822Name function. - */ - public static final String NAME_STRING_FROM_RFC822NAME = XACML_NS_3_0 + "string-from-rfc822Name"; - - /** - * Standard identifier for the ipAddress-from-string function. - */ - public static final String NAME_IPADDRESS_FROM_STRING = XACML_NS_3_0 + "ipAddress-from-string"; - - /** - * Standard identifier for the string-from-ipAddress function. - */ - public static final String NAME_STRING_FROM_IPADDRESS = XACML_NS_3_0 + "string-from-ipAddress"; - - /** - * Standard identifier for the dnsName-from-string function. - */ - public static final String NAME_DNSNAME_FROM_STRING = XACML_NS_3_0 + "dnsName-from-string"; - - /** - * Standard identifier for the string-from-dnsName function. - */ - public static final String NAME_STRING_FROM_DNSNAME = XACML_NS_3_0 + "string-from-dnsName"; - - private interface TypeConverter - { - - RETURN convert(PARAM arg) throws IllegalArgumentException; - } - - private static final class CallFactory - { - private final TypeConverter converter; - private final FunctionSignature.SingleParameterTyped funcSig; - private final String invalidArgMsgPrefix; - - private CallFactory(FunctionSignature.SingleParameterTyped functionSignature, TypeConverter converter) - { - this.funcSig = functionSignature; - this.converter = converter; - this.invalidArgMsgPrefix = "Function " + functionSignature.getName() + ": invalid arg: "; - } - - public FirstOrderFunctionCall getInstance(List> argExpressions, Datatype[] remainingArgTypes) - { - return new EagerSinglePrimitiveTypeEval(funcSig, argExpressions, remainingArgTypes) - { - @Override - protected RETURN evaluate(Deque args) throws IndeterminateEvaluationException - { - final PARAM arg0 = args.getFirst(); - try - { - return converter.convert(arg0); - } catch (IllegalArgumentException e) - { - throw new IndeterminateEvaluationException(invalidArgMsgPrefix + arg0, StatusHelper.STATUS_PROCESSING_ERROR, e); - } - } - - }; - } - } - - private final CallFactory funcCallFactory; - - /** - * Creates a new DatatypeConversionFunction object. - * - * @param funcURI - * function URI - * - * @param paramArrayType - * function parameter array type - * @param paramType - * parameter type - * @param returnType - * return type - * - */ - private DatatypeConversionFunction(String funcURI, Datatype paramType, Datatype returnType, TypeConverter converter) - { - super(funcURI, returnType, false, Arrays.asList(paramType)); - this.funcCallFactory = new CallFactory<>(functionSignature, converter); - } - - @Override - public FirstOrderFunctionCall newCall(List> argExpressions, Datatype... remainingArgTypes) throws IllegalArgumentException - { - return this.funcCallFactory.getInstance(argExpressions, remainingArgTypes); - } - - private static final TypeConverter DOUBLE_TO_INTEGER_CONVERTER = new TypeConverter() - { - - @Override - public final IntegerValue convert(DoubleValue arg) - { - return new IntegerValue(arg.longValue()); - } - - }; - - private static class IntegerToDoubleConverter implements TypeConverter - { - private static final IllegalArgumentException INTEGER_OUT_OF_RANGE_EXCEPTION = new IllegalArgumentException( - "Integer argument is outside the range which can be represented by a double"); - - @Override - public final DoubleValue convert(IntegerValue arg) - { - try - { - return new DoubleValue(arg.doubleValue()); - } catch (IllegalArgumentException e) - { - throw INTEGER_OUT_OF_RANGE_EXCEPTION; - } - } - } - - private static class FromStringConverter> implements TypeConverter - { - private final SimpleValue.Factory returnTypeFactory; - - private FromStringConverter(SimpleValue.Factory returnTypeFactory) - { - this.returnTypeFactory = returnTypeFactory; - } - - @Override - public final RETURN convert(StringValue arg) - { - return returnTypeFactory.getInstance(arg.getUnderlyingValue(), null, null); - - } - - } - - private static class ToStringConverter> implements TypeConverter - { - // not final because overriden specially by BooleanToString - @Override - public StringValue convert(PARAM arg) - { - return new StringValue(arg.toString()); - } - - } - - private static final ToStringConverter BOOLEAN_TO_STRING_CONVERTER = new ToStringConverter() - { - @Override - public final StringValue convert(BooleanValue arg) - { - return StringValue.getInstance(arg); - } - - }; - - /** - * Datatype-conversion function cluster - */ - public static final FunctionSet CLUSTER = new BaseFunctionSet( - FunctionSet.DEFAULT_ID_NAMESPACE + "type-conversion", - // - new DatatypeConversionFunction<>(NAME_DOUBLE_TO_INTEGER, DatatypeConstants.DOUBLE.TYPE, DatatypeConstants.INTEGER.TYPE, DOUBLE_TO_INTEGER_CONVERTER), - // - new DatatypeConversionFunction<>(NAME_INTEGER_TO_DOUBLE, DatatypeConstants.INTEGER.TYPE, DatatypeConstants.DOUBLE.TYPE, - new IntegerToDoubleConverter()), - // - new DatatypeConversionFunction<>(NAME_BOOLEAN_FROM_STRING, DatatypeConstants.STRING.TYPE, DatatypeConstants.BOOLEAN.TYPE, - new FromStringConverter<>(DatatypeConstants.BOOLEAN.FACTORY)), - // - new DatatypeConversionFunction<>(NAME_STRING_FROM_BOOLEAN, DatatypeConstants.BOOLEAN.TYPE, DatatypeConstants.STRING.TYPE, - BOOLEAN_TO_STRING_CONVERTER), - // - new DatatypeConversionFunction<>(NAME_INTEGER_FROM_STRING, DatatypeConstants.STRING.TYPE, DatatypeConstants.INTEGER.TYPE, - new FromStringConverter<>(DatatypeConstants.INTEGER.FACTORY)), - // - new DatatypeConversionFunction<>(NAME_STRING_FROM_INTEGER, DatatypeConstants.INTEGER.TYPE, DatatypeConstants.STRING.TYPE, - new ToStringConverter()), - // - new DatatypeConversionFunction<>(NAME_DOUBLE_FROM_STRING, DatatypeConstants.STRING.TYPE, DatatypeConstants.DOUBLE.TYPE, new FromStringConverter<>( - DatatypeConstants.DOUBLE.FACTORY)), - // - new DatatypeConversionFunction<>(NAME_STRING_FROM_DOUBLE, DatatypeConstants.DOUBLE.TYPE, DatatypeConstants.STRING.TYPE, - new ToStringConverter()), - // - new DatatypeConversionFunction<>(NAME_TIME_FROM_STRING, DatatypeConstants.STRING.TYPE, DatatypeConstants.TIME.TYPE, new FromStringConverter<>( - DatatypeConstants.TIME.FACTORY)), - // - new DatatypeConversionFunction<>(NAME_STRING_FROM_TIME, DatatypeConstants.TIME.TYPE, DatatypeConstants.STRING.TYPE, - new ToStringConverter()), - // - new DatatypeConversionFunction<>(NAME_DATE_FROM_STRING, DatatypeConstants.STRING.TYPE, DatatypeConstants.DATE.TYPE, new FromStringConverter<>( - DatatypeConstants.DATE.FACTORY)), - // - new DatatypeConversionFunction<>(NAME_STRING_FROM_DATE, DatatypeConstants.DATE.TYPE, DatatypeConstants.STRING.TYPE, - new ToStringConverter()), - // - new DatatypeConversionFunction<>(NAME_DATETIME_FROM_STRING, DatatypeConstants.STRING.TYPE, DatatypeConstants.DATETIME.TYPE, - new FromStringConverter<>(DatatypeConstants.DATETIME.FACTORY)), - // - new DatatypeConversionFunction<>(NAME_STRING_FROM_DATETIME, DatatypeConstants.DATETIME.TYPE, DatatypeConstants.STRING.TYPE, - new ToStringConverter()), - // - new DatatypeConversionFunction<>(NAME_ANYURI_FROM_STRING, DatatypeConstants.STRING.TYPE, DatatypeConstants.ANYURI.TYPE, new FromStringConverter<>( - DatatypeConstants.ANYURI.FACTORY)), - // - new DatatypeConversionFunction<>(NAME_STRING_FROM_ANYURI, DatatypeConstants.ANYURI.TYPE, DatatypeConstants.STRING.TYPE, - new ToStringConverter()), - // - new DatatypeConversionFunction<>(NAME_DAYTIMEDURATION_FROM_STRING, DatatypeConstants.STRING.TYPE, DatatypeConstants.DAYTIMEDURATION.TYPE, - new FromStringConverter<>(DatatypeConstants.DAYTIMEDURATION.FACTORY)), - // - new DatatypeConversionFunction<>(NAME_STRING_FROM_DAYTIMEDURATION, DatatypeConstants.DAYTIMEDURATION.TYPE, DatatypeConstants.STRING.TYPE, - new ToStringConverter()), - // - new DatatypeConversionFunction<>(NAME_YEARMONTHDURATION_FROM_STRING, DatatypeConstants.STRING.TYPE, DatatypeConstants.YEARMONTHDURATION.TYPE, - new FromStringConverter<>(DatatypeConstants.YEARMONTHDURATION.FACTORY)), - // - new DatatypeConversionFunction<>(NAME_STRING_FROM_YEARMONTHDURATION, DatatypeConstants.YEARMONTHDURATION.TYPE, DatatypeConstants.STRING.TYPE, - new ToStringConverter()), - // - new DatatypeConversionFunction<>(NAME_X500NAME_FROM_STRING, DatatypeConstants.STRING.TYPE, DatatypeConstants.X500NAME.TYPE, - new FromStringConverter<>(DatatypeConstants.X500NAME.FACTORY)), - // - new DatatypeConversionFunction<>(NAME_STRING_FROM_X500NAME, DatatypeConstants.X500NAME.TYPE, DatatypeConstants.STRING.TYPE, - new ToStringConverter()), - // - new DatatypeConversionFunction<>(NAME_RFC822NAME_FROM_STRING, DatatypeConstants.STRING.TYPE, DatatypeConstants.RFC822NAME.TYPE, - new FromStringConverter<>(DatatypeConstants.RFC822NAME.FACTORY)), - // - new DatatypeConversionFunction<>(NAME_STRING_FROM_RFC822NAME, DatatypeConstants.RFC822NAME.TYPE, DatatypeConstants.STRING.TYPE, - new ToStringConverter()), - // - new DatatypeConversionFunction<>(NAME_IPADDRESS_FROM_STRING, DatatypeConstants.STRING.TYPE, DatatypeConstants.IPADDRESS.TYPE, - new FromStringConverter<>(DatatypeConstants.IPADDRESS.FACTORY)), - // - new DatatypeConversionFunction<>(NAME_STRING_FROM_IPADDRESS, DatatypeConstants.IPADDRESS.TYPE, DatatypeConstants.STRING.TYPE, - new ToStringConverter()), - // - new DatatypeConversionFunction<>(NAME_DNSNAME_FROM_STRING, DatatypeConstants.STRING.TYPE, DatatypeConstants.DNSNAME.TYPE, - new FromStringConverter<>(DatatypeConstants.DNSNAME.FACTORY)), - // - new DatatypeConversionFunction<>(NAME_STRING_FROM_DNSNAME, DatatypeConstants.DNSNAME.TYPE, DatatypeConstants.STRING.TYPE, - new ToStringConverter()) - // - ); - -} +/** + * Copyright (C) 2011-2015 Thales Services SAS. + * + * This file is part of AuthZForce. + * + * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * AuthZForce 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . + */ +package org.ow2.authzforce.core.pdp.impl.func; + +import java.util.Arrays; +import java.util.Deque; +import java.util.List; + +import org.ow2.authzforce.core.pdp.api.AttributeValue; +import org.ow2.authzforce.core.pdp.api.Datatype; +import org.ow2.authzforce.core.pdp.api.Expression; +import org.ow2.authzforce.core.pdp.api.FirstOrderFunction; +import org.ow2.authzforce.core.pdp.api.FirstOrderFunctionCall; +import org.ow2.authzforce.core.pdp.api.FirstOrderFunctionCall.EagerSinglePrimitiveTypeEval; +import org.ow2.authzforce.core.pdp.api.FunctionSet; +import org.ow2.authzforce.core.pdp.api.FunctionSignature; +import org.ow2.authzforce.core.pdp.api.IndeterminateEvaluationException; +import org.ow2.authzforce.core.pdp.api.StatusHelper; +import org.ow2.authzforce.core.pdp.api.Value; +import org.ow2.authzforce.core.pdp.impl.value.AnyURIValue; +import org.ow2.authzforce.core.pdp.impl.value.BooleanValue; +import org.ow2.authzforce.core.pdp.impl.value.DNSNameValue; +import org.ow2.authzforce.core.pdp.impl.value.DatatypeConstants; +import org.ow2.authzforce.core.pdp.impl.value.DateTimeValue; +import org.ow2.authzforce.core.pdp.impl.value.DateValue; +import org.ow2.authzforce.core.pdp.impl.value.DayTimeDurationValue; +import org.ow2.authzforce.core.pdp.impl.value.DoubleValue; +import org.ow2.authzforce.core.pdp.impl.value.IPAddressValue; +import org.ow2.authzforce.core.pdp.impl.value.IntegerValue; +import org.ow2.authzforce.core.pdp.impl.value.RFC822NameValue; +import org.ow2.authzforce.core.pdp.impl.value.SimpleValue; +import org.ow2.authzforce.core.pdp.impl.value.StringValue; +import org.ow2.authzforce.core.pdp.impl.value.TimeValue; +import org.ow2.authzforce.core.pdp.impl.value.X500NameValue; +import org.ow2.authzforce.core.pdp.impl.value.YearMonthDurationValue; + +/** + * A class that implements all the primitive datatype conversion functions: double-to-integer, integer-to-double, *-from-string, *-to-string, etc. It takes one + * argument of the appropriate type, converts that argument to the other type, and returns the result. + * + * @param + * parameter/input type + * @param + * return/output type + * @author cdangerv + * @version $Id: $ + */ +public final class DatatypeConversionFunction extends + FirstOrderFunction.SingleParameterTyped +{ + + /** + * Standard identifier for the double-to-integer function. + */ + public static final String NAME_DOUBLE_TO_INTEGER = XACML_NS_1_0 + "double-to-integer"; + + /** + * Standard identifier for the integer-to-double function. + */ + public static final String NAME_INTEGER_TO_DOUBLE = XACML_NS_1_0 + "integer-to-double"; + + /** + * Standard identifier for the boolean-from-string function. + */ + public static final String NAME_BOOLEAN_FROM_STRING = XACML_NS_3_0 + "boolean-from-string"; + + /** + * Standard identifier for the string-from-boolean function. + */ + public static final String NAME_STRING_FROM_BOOLEAN = XACML_NS_3_0 + "string-from-boolean"; + + /** + * Standard identifier for the integer-from-string function. + */ + public static final String NAME_INTEGER_FROM_STRING = XACML_NS_3_0 + "integer-from-string"; + + /** + * Standard identifier for the string-from-integer function. + */ + public static final String NAME_STRING_FROM_INTEGER = XACML_NS_3_0 + "string-from-integer"; + + /** + * Standard identifier for the double-from-string function. + */ + public static final String NAME_DOUBLE_FROM_STRING = XACML_NS_3_0 + "double-from-string"; + + /** + * Standard identifier for the string-from-double function. + */ + public static final String NAME_STRING_FROM_DOUBLE = XACML_NS_3_0 + "string-from-double"; + + /** + * Standard identifier for the time-from-string function. + */ + public static final String NAME_TIME_FROM_STRING = XACML_NS_3_0 + "time-from-string"; + + /** + * Standard identifier for the string-from-time function. + */ + public static final String NAME_STRING_FROM_TIME = XACML_NS_3_0 + "string-from-time"; + + /** + * Standard identifier for the date-from-string function. + */ + public static final String NAME_DATE_FROM_STRING = XACML_NS_3_0 + "date-from-string"; + + /** + * Standard identifier for the string-from-date function. + */ + public static final String NAME_STRING_FROM_DATE = XACML_NS_3_0 + "string-from-date"; + + /** + * Standard identifier for the dateTime-from-string function. + */ + public static final String NAME_DATETIME_FROM_STRING = XACML_NS_3_0 + "dateTime-from-string"; + + /** + * Standard identifier for the string-from-dateTime function. + */ + public static final String NAME_STRING_FROM_DATETIME = XACML_NS_3_0 + "string-from-dateTime"; + + /** + * Standard identifier for the anyURI-from-string function. + */ + public static final String NAME_ANYURI_FROM_STRING = XACML_NS_3_0 + "anyURI-from-string"; + + /** + * Standard identifier for the string-from-anyURI function. + */ + public static final String NAME_STRING_FROM_ANYURI = XACML_NS_3_0 + "string-from-anyURI"; + + /** + * Standard identifier for the dayTimeDuration-from-string function. + */ + public static final String NAME_DAYTIMEDURATION_FROM_STRING = XACML_NS_3_0 + "dayTimeDuration-from-string"; + + /** + * Standard identifier for the string-from-dayTimeDuration function. + */ + public static final String NAME_STRING_FROM_DAYTIMEDURATION = XACML_NS_3_0 + "string-from-dayTimeDuration"; + + /** + * Standard identifier for the yearMonthDuration-from-string function. + */ + public static final String NAME_YEARMONTHDURATION_FROM_STRING = XACML_NS_3_0 + "yearMonthDuration-from-string"; + + /** + * Standard identifier for the string-from-yearMonthDuration function. + */ + public static final String NAME_STRING_FROM_YEARMONTHDURATION = XACML_NS_3_0 + "string-from-yearMonthDuration"; + + /** + * Standard identifier for the x500Name-from-string function. + */ + public static final String NAME_X500NAME_FROM_STRING = XACML_NS_3_0 + "x500Name-from-string"; + + /** + * Standard identifier for the string-from-x500Name function. + */ + public static final String NAME_STRING_FROM_X500NAME = XACML_NS_3_0 + "string-from-x500Name"; + + /** + * Standard identifier for the rfc822Name-from-string function. + */ + public static final String NAME_RFC822NAME_FROM_STRING = XACML_NS_3_0 + "rfc822Name-from-string"; + + /** + * Standard identifier for the string-from-rfc822Name function. + */ + public static final String NAME_STRING_FROM_RFC822NAME = XACML_NS_3_0 + "string-from-rfc822Name"; + + /** + * Standard identifier for the ipAddress-from-string function. + */ + public static final String NAME_IPADDRESS_FROM_STRING = XACML_NS_3_0 + "ipAddress-from-string"; + + /** + * Standard identifier for the string-from-ipAddress function. + */ + public static final String NAME_STRING_FROM_IPADDRESS = XACML_NS_3_0 + "string-from-ipAddress"; + + /** + * Standard identifier for the dnsName-from-string function. + */ + public static final String NAME_DNSNAME_FROM_STRING = XACML_NS_3_0 + "dnsName-from-string"; + + /** + * Standard identifier for the string-from-dnsName function. + */ + public static final String NAME_STRING_FROM_DNSNAME = XACML_NS_3_0 + "string-from-dnsName"; + + private interface TypeConverter + { + + RETURN convert(PARAM arg) throws IllegalArgumentException; + } + + private static final class CallFactory + { + private final TypeConverter converter; + private final FunctionSignature.SingleParameterTyped funcSig; + private final String invalidArgMsgPrefix; + + private CallFactory(FunctionSignature.SingleParameterTyped functionSignature, TypeConverter converter) + { + this.funcSig = functionSignature; + this.converter = converter; + this.invalidArgMsgPrefix = "Function " + functionSignature.getName() + ": invalid arg: "; + } + + public FirstOrderFunctionCall getInstance(List> argExpressions, Datatype[] remainingArgTypes) + { + return new EagerSinglePrimitiveTypeEval(funcSig, argExpressions, remainingArgTypes) + { + @Override + protected RETURN evaluate(Deque args) throws IndeterminateEvaluationException + { + final PARAM arg0 = args.getFirst(); + try + { + return converter.convert(arg0); + } catch (IllegalArgumentException e) + { + throw new IndeterminateEvaluationException(invalidArgMsgPrefix + arg0, StatusHelper.STATUS_PROCESSING_ERROR, e); + } + } + + }; + } + } + + private final CallFactory funcCallFactory; + + /** + * Creates a new DatatypeConversionFunction object. + * + * @param funcURI + * function URI + * + * @param paramArrayType + * function parameter array type + * @param paramType + * parameter type + * @param returnType + * return type + * + */ + private DatatypeConversionFunction(String funcURI, Datatype paramType, Datatype returnType, TypeConverter converter) + { + super(funcURI, returnType, false, Arrays.asList(paramType)); + this.funcCallFactory = new CallFactory<>(functionSignature, converter); + } + + /** {@inheritDoc} */ + @Override + public FirstOrderFunctionCall newCall(List> argExpressions, Datatype... remainingArgTypes) throws IllegalArgumentException + { + return this.funcCallFactory.getInstance(argExpressions, remainingArgTypes); + } + + private static final TypeConverter DOUBLE_TO_INTEGER_CONVERTER = new TypeConverter() + { + + @Override + public final IntegerValue convert(DoubleValue arg) + { + return new IntegerValue(arg.longValue()); + } + + }; + + private static class IntegerToDoubleConverter implements TypeConverter + { + private static final IllegalArgumentException INTEGER_OUT_OF_RANGE_EXCEPTION = new IllegalArgumentException( + "Integer argument is outside the range which can be represented by a double"); + + @Override + public final DoubleValue convert(IntegerValue arg) + { + try + { + return new DoubleValue(arg.doubleValue()); + } catch (IllegalArgumentException e) + { + throw INTEGER_OUT_OF_RANGE_EXCEPTION; + } + } + } + + private static class FromStringConverter> implements TypeConverter + { + private final SimpleValue.Factory returnTypeFactory; + + private FromStringConverter(SimpleValue.Factory returnTypeFactory) + { + this.returnTypeFactory = returnTypeFactory; + } + + @Override + public final RETURN convert(StringValue arg) + { + return returnTypeFactory.getInstance(arg.getUnderlyingValue(), null, null); + + } + + } + + private static class ToStringConverter> implements TypeConverter + { + // not final because overriden specially by BooleanToString + @Override + public StringValue convert(PARAM arg) + { + return new StringValue(arg.toString()); + } + + } + + private static final ToStringConverter BOOLEAN_TO_STRING_CONVERTER = new ToStringConverter() + { + @Override + public final StringValue convert(BooleanValue arg) + { + return StringValue.getInstance(arg); + } + + }; + + /** + * Datatype-conversion function cluster + */ + public static final FunctionSet CLUSTER = new BaseFunctionSet( + FunctionSet.DEFAULT_ID_NAMESPACE + "type-conversion", + // + new DatatypeConversionFunction<>(NAME_DOUBLE_TO_INTEGER, DatatypeConstants.DOUBLE.TYPE, DatatypeConstants.INTEGER.TYPE, DOUBLE_TO_INTEGER_CONVERTER), + // + new DatatypeConversionFunction<>(NAME_INTEGER_TO_DOUBLE, DatatypeConstants.INTEGER.TYPE, DatatypeConstants.DOUBLE.TYPE, + new IntegerToDoubleConverter()), + // + new DatatypeConversionFunction<>(NAME_BOOLEAN_FROM_STRING, DatatypeConstants.STRING.TYPE, DatatypeConstants.BOOLEAN.TYPE, + new FromStringConverter<>(DatatypeConstants.BOOLEAN.FACTORY)), + // + new DatatypeConversionFunction<>(NAME_STRING_FROM_BOOLEAN, DatatypeConstants.BOOLEAN.TYPE, DatatypeConstants.STRING.TYPE, + BOOLEAN_TO_STRING_CONVERTER), + // + new DatatypeConversionFunction<>(NAME_INTEGER_FROM_STRING, DatatypeConstants.STRING.TYPE, DatatypeConstants.INTEGER.TYPE, + new FromStringConverter<>(DatatypeConstants.INTEGER.FACTORY)), + // + new DatatypeConversionFunction<>(NAME_STRING_FROM_INTEGER, DatatypeConstants.INTEGER.TYPE, DatatypeConstants.STRING.TYPE, + new ToStringConverter()), + // + new DatatypeConversionFunction<>(NAME_DOUBLE_FROM_STRING, DatatypeConstants.STRING.TYPE, DatatypeConstants.DOUBLE.TYPE, new FromStringConverter<>( + DatatypeConstants.DOUBLE.FACTORY)), + // + new DatatypeConversionFunction<>(NAME_STRING_FROM_DOUBLE, DatatypeConstants.DOUBLE.TYPE, DatatypeConstants.STRING.TYPE, + new ToStringConverter()), + // + new DatatypeConversionFunction<>(NAME_TIME_FROM_STRING, DatatypeConstants.STRING.TYPE, DatatypeConstants.TIME.TYPE, new FromStringConverter<>( + DatatypeConstants.TIME.FACTORY)), + // + new DatatypeConversionFunction<>(NAME_STRING_FROM_TIME, DatatypeConstants.TIME.TYPE, DatatypeConstants.STRING.TYPE, + new ToStringConverter()), + // + new DatatypeConversionFunction<>(NAME_DATE_FROM_STRING, DatatypeConstants.STRING.TYPE, DatatypeConstants.DATE.TYPE, new FromStringConverter<>( + DatatypeConstants.DATE.FACTORY)), + // + new DatatypeConversionFunction<>(NAME_STRING_FROM_DATE, DatatypeConstants.DATE.TYPE, DatatypeConstants.STRING.TYPE, + new ToStringConverter()), + // + new DatatypeConversionFunction<>(NAME_DATETIME_FROM_STRING, DatatypeConstants.STRING.TYPE, DatatypeConstants.DATETIME.TYPE, + new FromStringConverter<>(DatatypeConstants.DATETIME.FACTORY)), + // + new DatatypeConversionFunction<>(NAME_STRING_FROM_DATETIME, DatatypeConstants.DATETIME.TYPE, DatatypeConstants.STRING.TYPE, + new ToStringConverter()), + // + new DatatypeConversionFunction<>(NAME_ANYURI_FROM_STRING, DatatypeConstants.STRING.TYPE, DatatypeConstants.ANYURI.TYPE, new FromStringConverter<>( + DatatypeConstants.ANYURI.FACTORY)), + // + new DatatypeConversionFunction<>(NAME_STRING_FROM_ANYURI, DatatypeConstants.ANYURI.TYPE, DatatypeConstants.STRING.TYPE, + new ToStringConverter()), + // + new DatatypeConversionFunction<>(NAME_DAYTIMEDURATION_FROM_STRING, DatatypeConstants.STRING.TYPE, DatatypeConstants.DAYTIMEDURATION.TYPE, + new FromStringConverter<>(DatatypeConstants.DAYTIMEDURATION.FACTORY)), + // + new DatatypeConversionFunction<>(NAME_STRING_FROM_DAYTIMEDURATION, DatatypeConstants.DAYTIMEDURATION.TYPE, DatatypeConstants.STRING.TYPE, + new ToStringConverter()), + // + new DatatypeConversionFunction<>(NAME_YEARMONTHDURATION_FROM_STRING, DatatypeConstants.STRING.TYPE, DatatypeConstants.YEARMONTHDURATION.TYPE, + new FromStringConverter<>(DatatypeConstants.YEARMONTHDURATION.FACTORY)), + // + new DatatypeConversionFunction<>(NAME_STRING_FROM_YEARMONTHDURATION, DatatypeConstants.YEARMONTHDURATION.TYPE, DatatypeConstants.STRING.TYPE, + new ToStringConverter()), + // + new DatatypeConversionFunction<>(NAME_X500NAME_FROM_STRING, DatatypeConstants.STRING.TYPE, DatatypeConstants.X500NAME.TYPE, + new FromStringConverter<>(DatatypeConstants.X500NAME.FACTORY)), + // + new DatatypeConversionFunction<>(NAME_STRING_FROM_X500NAME, DatatypeConstants.X500NAME.TYPE, DatatypeConstants.STRING.TYPE, + new ToStringConverter()), + // + new DatatypeConversionFunction<>(NAME_RFC822NAME_FROM_STRING, DatatypeConstants.STRING.TYPE, DatatypeConstants.RFC822NAME.TYPE, + new FromStringConverter<>(DatatypeConstants.RFC822NAME.FACTORY)), + // + new DatatypeConversionFunction<>(NAME_STRING_FROM_RFC822NAME, DatatypeConstants.RFC822NAME.TYPE, DatatypeConstants.STRING.TYPE, + new ToStringConverter()), + // + new DatatypeConversionFunction<>(NAME_IPADDRESS_FROM_STRING, DatatypeConstants.STRING.TYPE, DatatypeConstants.IPADDRESS.TYPE, + new FromStringConverter<>(DatatypeConstants.IPADDRESS.FACTORY)), + // + new DatatypeConversionFunction<>(NAME_STRING_FROM_IPADDRESS, DatatypeConstants.IPADDRESS.TYPE, DatatypeConstants.STRING.TYPE, + new ToStringConverter()), + // + new DatatypeConversionFunction<>(NAME_DNSNAME_FROM_STRING, DatatypeConstants.STRING.TYPE, DatatypeConstants.DNSNAME.TYPE, + new FromStringConverter<>(DatatypeConstants.DNSNAME.FACTORY)), + // + new DatatypeConversionFunction<>(NAME_STRING_FROM_DNSNAME, DatatypeConstants.DNSNAME.TYPE, DatatypeConstants.STRING.TYPE, + new ToStringConverter()) + // + ); + +} diff --git a/src/main/java/org/ow2/authzforce/core/pdp/impl/func/EqualTypeMatchFunction.java b/src/main/java/org/ow2/authzforce/core/pdp/impl/func/EqualTypeMatchFunction.java index 17560206..2ccaa63c 100644 --- a/src/main/java/org/ow2/authzforce/core/pdp/impl/func/EqualTypeMatchFunction.java +++ b/src/main/java/org/ow2/authzforce/core/pdp/impl/func/EqualTypeMatchFunction.java @@ -47,9 +47,11 @@ import org.ow2.authzforce.core.pdp.impl.value.YearMonthDurationValue; /** * Implements generic match functions taking parameters of same/equal type, i.e. standard (A.3.1) Equality predicates and special match function x500Name-match - * + * * @param * type of compared parameters + * @author cdangerv + * @version $Id: $ */ public class EqualTypeMatchFunction extends FirstOrderFunction.SingleParameterTyped { @@ -235,6 +237,7 @@ public class EqualTypeMatchFunction extends FirstO * * @see com.thalesgroup.authzforce.core.func.FirstOrderFunction#getFunctionCall(java.util.List, com.thalesgroup.authzforce.core.eval.DatatypeDef[]) */ + /** {@inheritDoc} */ @Override public FirstOrderFunctionCall newCall(List> argExpressions, Datatype... remainingArgTypes) { diff --git a/src/main/java/org/ow2/authzforce/core/pdp/impl/func/FirstOrderBagFunctionSet.java b/src/main/java/org/ow2/authzforce/core/pdp/impl/func/FirstOrderBagFunctionSet.java index f3394b86..528651a6 100644 --- a/src/main/java/org/ow2/authzforce/core/pdp/impl/func/FirstOrderBagFunctionSet.java +++ b/src/main/java/org/ow2/authzforce/core/pdp/impl/func/FirstOrderBagFunctionSet.java @@ -42,7 +42,9 @@ import org.ow2.authzforce.core.pdp.impl.value.IntegerValue; /** * First-order bag function groups, as opposed to the higher-order bag functions (see {@link HigherOrderBagFunction}); such as the Bag functions of section * A.3.10, and the Set functions of A.3.11 of the XACML spec. - * + * + * @author cdangerv + * @version $Id: $ */ public final class FirstOrderBagFunctionSet { diff --git a/src/main/java/org/ow2/authzforce/core/pdp/impl/func/FunctionRegistry.java b/src/main/java/org/ow2/authzforce/core/pdp/impl/func/FunctionRegistry.java index f109934a..e9457ced 100644 --- a/src/main/java/org/ow2/authzforce/core/pdp/impl/func/FunctionRegistry.java +++ b/src/main/java/org/ow2/authzforce/core/pdp/impl/func/FunctionRegistry.java @@ -1,117 +1,119 @@ -/** - * Copyright (C) 2011-2015 Thales Services SAS. - * - * This file is part of AuthZForce. - * - * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software - * Foundation, either version 3 of the License, or (at your option) any later version. - * - * AuthZForce 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . - */ -/** - * - */ -package org.ow2.authzforce.core.pdp.impl.func; - -import org.ow2.authzforce.core.pdp.api.AttributeValue; -import org.ow2.authzforce.core.pdp.api.DatatypeFactory; -import org.ow2.authzforce.core.pdp.api.Function; -import org.ow2.authzforce.core.pdp.impl.BasePdpExtensionRegistry; - -/** - * - * - */ -public class FunctionRegistry -{ - - private final BasePdpExtensionRegistry> nonGenericFunctionRegistry; - private final BasePdpExtensionRegistry genericHigherOrderFunctionFactoryRegistry; - - /** - * Low-level constructor - * - * @param nonGenericFunctionRegistry - * (mandatory) non-generic function registry - * @param genericFunctionFactoryRegistry - * (optional) generic function factory registry - */ - protected FunctionRegistry(BasePdpExtensionRegistry> nonGenericFunctionRegistry, BasePdpExtensionRegistry genericFunctionFactoryRegistry) - { - this.nonGenericFunctionRegistry = new BasePdpExtensionRegistry<>(Function.class, nonGenericFunctionRegistry); - this.genericHigherOrderFunctionFactoryRegistry = genericFunctionFactoryRegistry == null ? null : new BasePdpExtensionRegistry<>(GenericHigherOrderFunctionFactory.class, - genericFunctionFactoryRegistry); - } - - /** - * Constructor that sets a "base registry" from which this inherits all the extensions. Used for instance to build a new registry based on a standard one (e.g. {@link StandardFunctionRegistry} for - * standard functions). - * - * @param baseRegistry - * the base/parent registry on which this one is based or null - */ - public FunctionRegistry(FunctionRegistry baseRegistry) - { - this(baseRegistry == null ? new BasePdpExtensionRegistry>(Function.class) : new BasePdpExtensionRegistry<>(Function.class, baseRegistry.nonGenericFunctionRegistry), - baseRegistry == null ? new BasePdpExtensionRegistry<>(GenericHigherOrderFunctionFactory.class) : new BasePdpExtensionRegistry<>(GenericHigherOrderFunctionFactory.class, - baseRegistry.genericHigherOrderFunctionFactoryRegistry)); - } - - /** - * Adds (non-generic) function - * - * @param function - * function - */ - public void addFunction(Function function) - { - nonGenericFunctionRegistry.addExtension(function); - - } - - /** - * Get a (non-generic) function by ID. 'Non-generic' here means the function is either first-order, or higher-order but does not need the specific sub-function parameter to be instantiated. - * - * @param functionId - * ID of function to loop up - * - * @return function instance, null if none with such ID in the registry of non-generic functions, in which case it may be a generic function and you should try - * {@link #getFunction(String, DatatypeFactory)} instead. - */ - public Function getFunction(String functionId) - { - return nonGenericFunctionRegistry.getExtension(functionId); - } - - /** - * Get any function including generic ones. 'Generic' here means the function is a higher-order function that is instantiated for a specific sub-function. For instance, the XACML 'map' function ( - * {@link MapFunctionFactory}) function class takes the sub-function's return type as type parameter and therefore it needs this sub-function's return type to be instantiated (this is done via the - * {@link MapFunctionFactory}). - * - * @param functionId - * function ID - * @param subFunctionReturnTypeFactory - * sub-function return datatype factory - * @return function instance - */ - public Function getFunction(String functionId, DatatypeFactory subFunctionReturnTypeFactory) - { - final Function nonGenericFunc = nonGenericFunctionRegistry.getExtension(functionId); - if (nonGenericFunc != null) - { - return nonGenericFunc; - } - - if (genericHigherOrderFunctionFactoryRegistry == null) - { - return null; - } - - final GenericHigherOrderFunctionFactory funcFactory = genericHigherOrderFunctionFactoryRegistry.getExtension(functionId); - return funcFactory.getInstance(subFunctionReturnTypeFactory); - } - -} +/** + * Copyright (C) 2011-2015 Thales Services SAS. + * + * This file is part of AuthZForce. + * + * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * AuthZForce 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . + */ +/** + * + */ +package org.ow2.authzforce.core.pdp.impl.func; + +import org.ow2.authzforce.core.pdp.api.AttributeValue; +import org.ow2.authzforce.core.pdp.api.DatatypeFactory; +import org.ow2.authzforce.core.pdp.api.Function; +import org.ow2.authzforce.core.pdp.impl.BasePdpExtensionRegistry; + +/** + *

FunctionRegistry class.

+ * + * @author cdangerv + * @version $Id: $ + */ +public class FunctionRegistry +{ + + private final BasePdpExtensionRegistry> nonGenericFunctionRegistry; + private final BasePdpExtensionRegistry genericHigherOrderFunctionFactoryRegistry; + + /** + * Low-level constructor + * + * @param nonGenericFunctionRegistry + * (mandatory) non-generic function registry + * @param genericFunctionFactoryRegistry + * (optional) generic function factory registry + */ + protected FunctionRegistry(BasePdpExtensionRegistry> nonGenericFunctionRegistry, BasePdpExtensionRegistry genericFunctionFactoryRegistry) + { + this.nonGenericFunctionRegistry = new BasePdpExtensionRegistry<>(Function.class, nonGenericFunctionRegistry); + this.genericHigherOrderFunctionFactoryRegistry = genericFunctionFactoryRegistry == null ? null : new BasePdpExtensionRegistry<>(GenericHigherOrderFunctionFactory.class, + genericFunctionFactoryRegistry); + } + + /** + * Constructor that sets a "base registry" from which this inherits all the extensions. Used for instance to build a new registry based on a standard one (e.g. {@link StandardFunctionRegistry} for + * standard functions). + * + * @param baseRegistry + * the base/parent registry on which this one is based or null + */ + public FunctionRegistry(FunctionRegistry baseRegistry) + { + this(baseRegistry == null ? new BasePdpExtensionRegistry>(Function.class) : new BasePdpExtensionRegistry<>(Function.class, baseRegistry.nonGenericFunctionRegistry), + baseRegistry == null ? new BasePdpExtensionRegistry<>(GenericHigherOrderFunctionFactory.class) : new BasePdpExtensionRegistry<>(GenericHigherOrderFunctionFactory.class, + baseRegistry.genericHigherOrderFunctionFactoryRegistry)); + } + + /** + * Adds (non-generic) function + * + * @param function + * function + */ + public void addFunction(Function function) + { + nonGenericFunctionRegistry.addExtension(function); + + } + + /** + * Get a (non-generic) function by ID. 'Non-generic' here means the function is either first-order, or higher-order but does not need the specific sub-function parameter to be instantiated. + * + * @param functionId + * ID of function to loop up + * @return function instance, null if none with such ID in the registry of non-generic functions, in which case it may be a generic function and you should try + * {@link #getFunction(String, DatatypeFactory)} instead. + */ + public Function getFunction(String functionId) + { + return nonGenericFunctionRegistry.getExtension(functionId); + } + + /** + * Get any function including generic ones. 'Generic' here means the function is a higher-order function that is instantiated for a specific sub-function. For instance, the XACML 'map' function ( + * {@link MapFunctionFactory}) function class takes the sub-function's return type as type parameter and therefore it needs this sub-function's return type to be instantiated (this is done via the + * {@link MapFunctionFactory}). + * + * @param functionId + * function ID + * @param subFunctionReturnTypeFactory + * sub-function return datatype factory + * @return function instance + * @param a SUB_RETURN_T object. + */ + public Function getFunction(String functionId, DatatypeFactory subFunctionReturnTypeFactory) + { + final Function nonGenericFunc = nonGenericFunctionRegistry.getExtension(functionId); + if (nonGenericFunc != null) + { + return nonGenericFunc; + } + + if (genericHigherOrderFunctionFactoryRegistry == null) + { + return null; + } + + final GenericHigherOrderFunctionFactory funcFactory = genericHigherOrderFunctionFactoryRegistry.getExtension(functionId); + return funcFactory.getInstance(subFunctionReturnTypeFactory); + } + +} diff --git a/src/main/java/org/ow2/authzforce/core/pdp/impl/func/GenericHigherOrderFunctionFactory.java b/src/main/java/org/ow2/authzforce/core/pdp/impl/func/GenericHigherOrderFunctionFactory.java index 04321415..d21ac404 100644 --- a/src/main/java/org/ow2/authzforce/core/pdp/impl/func/GenericHigherOrderFunctionFactory.java +++ b/src/main/java/org/ow2/authzforce/core/pdp/impl/func/GenericHigherOrderFunctionFactory.java @@ -1,44 +1,48 @@ -/** - * Copyright (C) 2011-2015 Thales Services SAS. - * - * This file is part of AuthZForce. - * - * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software - * Foundation, either version 3 of the License, or (at your option) any later version. - * - * AuthZForce 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . - */ -package org.ow2.authzforce.core.pdp.impl.func; - -import org.ow2.authzforce.core.pdp.api.AttributeValue; -import org.ow2.authzforce.core.pdp.api.DatatypeFactory; -import org.ow2.authzforce.core.pdp.api.PdpExtension; - -/** - * Interface for generic higher-order function factories, e.g. {@link MapFunctionFactory}. A generic function is a function class with a type parameter - * depending on the sub-function's return type, e.g. {@link MapFunctionFactory}, therefore the function is instantiated for a specific sub-function's return - * type. - * - */ -public abstract class GenericHigherOrderFunctionFactory implements PdpExtension -{ - /** - * Returns instance of the Higher-order function - * - * @param subFunctionReturnTypeFactory - * sub-function's return datatype factory - * @return higher-order function instance - */ - public abstract HigherOrderBagFunction getInstance( - DatatypeFactory subFunctionReturnTypeFactory); - - @Override - public String toString() - { - return this.getId(); - } - -} +/** + * Copyright (C) 2011-2015 Thales Services SAS. + * + * This file is part of AuthZForce. + * + * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * AuthZForce 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . + */ +package org.ow2.authzforce.core.pdp.impl.func; + +import org.ow2.authzforce.core.pdp.api.AttributeValue; +import org.ow2.authzforce.core.pdp.api.DatatypeFactory; +import org.ow2.authzforce.core.pdp.api.PdpExtension; + +/** + * Interface for generic higher-order function factories, e.g. {@link MapFunctionFactory}. A generic function is a function class with a type parameter + * depending on the sub-function's return type, e.g. {@link MapFunctionFactory}, therefore the function is instantiated for a specific sub-function's return + * type. + * + * @author cdangerv + * @version $Id: $ + */ +public abstract class GenericHigherOrderFunctionFactory implements PdpExtension +{ + /** + * Returns instance of the Higher-order function + * + * @param subFunctionReturnTypeFactory + * sub-function's return datatype factory + * @return higher-order function instance + * @param a SUB_RETURN_T object. + */ + public abstract HigherOrderBagFunction getInstance( + DatatypeFactory subFunctionReturnTypeFactory); + + /** {@inheritDoc} */ + @Override + public String toString() + { + return this.getId(); + } + +} diff --git a/src/main/java/org/ow2/authzforce/core/pdp/impl/func/HigherOrderBagFunction.java b/src/main/java/org/ow2/authzforce/core/pdp/impl/func/HigherOrderBagFunction.java index 7b82620f..a59a652c 100644 --- a/src/main/java/org/ow2/authzforce/core/pdp/impl/func/HigherOrderBagFunction.java +++ b/src/main/java/org/ow2/authzforce/core/pdp/impl/func/HigherOrderBagFunction.java @@ -1,145 +1,153 @@ -/** - * Copyright (C) 2012-2015 Thales Services SAS. - * - * This file is part of AuthZForce CE. - * - * AuthZForce CE is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software - * Foundation, either version 3 of the License, or (at your option) any later version. - * - * AuthZForce CE 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with AuthZForce CE. If not, see . - */ -package org.ow2.authzforce.core.pdp.impl.func; - -import java.util.Iterator; -import java.util.List; - -import org.ow2.authzforce.core.pdp.api.AttributeValue; -import org.ow2.authzforce.core.pdp.api.BaseFunction; -import org.ow2.authzforce.core.pdp.api.Datatype; -import org.ow2.authzforce.core.pdp.api.Expression; -import org.ow2.authzforce.core.pdp.api.FirstOrderFunction; -import org.ow2.authzforce.core.pdp.api.Function; -import org.ow2.authzforce.core.pdp.api.FunctionCall; -import org.ow2.authzforce.core.pdp.api.Value; -import org.ow2.authzforce.core.pdp.api.VariableReference; -import org.ow2.authzforce.core.pdp.impl.expression.BaseVariableReference; - -/** - * Higher-order bag function - * - * @param - * return type - * @param - * sub-function's return (primitive) type. Only functions returning primitive type of result are compatible with higher-order functions here. - */ -public abstract class HigherOrderBagFunction extends BaseFunction -{ - - private final Datatype returnType; - - private final Datatype subFuncReturnType; - - /** - * Instantiates higher-order bag function - * - * @param functionId - * function ID - * @param returnType - * function's return type - * @param subFunctionReturnType - * sub-function's return datatype; may be null to indicate any datatype (e.g. map function's sub-function return datatype can be any primitive type) - */ - HigherOrderBagFunction(String functionId, Datatype returnType, Datatype subFunctionReturnType) - { - super(functionId); - this.returnType = returnType; - this.subFuncReturnType = subFunctionReturnType; - } - - /** - * Returns the type of attribute value that will be returned by this function. - * - * @return the return type - */ - @Override - public Datatype getReturnType() - { - return returnType; - } - - /** - * Creates function call from sub-function definition and all inputs to higher-order function. To be overriden by OneBagOnlyFunctions (any-of/all-of) - * - * @param subFunc - * first-order sub-function - * @param inputsAfterSubFunc - * sub-function arguments - * @return function call - */ - protected abstract FunctionCall createFunctionCallFromSubFunction(FirstOrderFunction subFunc, List> inputsAfterSubFunc); - - @Override - public final FunctionCall newCall(List> inputs) throws IllegalArgumentException - { - final int numInputs = inputs.size(); - checkNumberOfArgs(numInputs); - - final Iterator> inputsIterator = inputs.iterator(); - final Expression input0 = inputsIterator.next(); - // first arg must be a boolean function - final Function inputFunc; - if (input0 instanceof Function) - { - inputFunc = (Function) input0; - } else if (input0 instanceof BaseVariableReference) - { - final Expression varRefExp = ((VariableReference) input0).getReferencedExpression(); - if (!(varRefExp instanceof Function)) - { - throw new IllegalArgumentException(this + ": Invalid type of first argument: " + varRefExp.getClass().getSimpleName() + ". Required: Function"); - } - - inputFunc = (Function) varRefExp; - } else - { - throw new IllegalArgumentException(this + ": Invalid type of first argument: " + input0.getClass().getSimpleName() + ". Required: Function"); - } - - /* - * Check whether it is a FirstOrderFunction because it is the only type of function for which we have a generic way to validate argument types as done later below - */ - if (!(inputFunc instanceof FirstOrderFunction)) - { - throw new IllegalArgumentException(this + ": Invalid function in first argument: " + inputFunc + " is not supported as such argument"); - } - - final Datatype inputFuncReturnType = inputFunc.getReturnType(); - if (subFuncReturnType == null) - { - /* - * sub-function's return type can be any primitive datatype; check at least it is primitive - */ - if (inputFuncReturnType.getTypeParameter() != null) - { - throw new IllegalArgumentException(this + ": Invalid return type of function in first argument: " + inputFuncReturnType + " (bag type). Required: any primitive type"); - } - } else - { - if (!inputFuncReturnType.equals(subFuncReturnType)) - { - throw new IllegalArgumentException(this + ": Invalid return type of function in first argument: " + inputFuncReturnType + ". Required: " + subFuncReturnType); - } - } - - // so now we know we have a boolean FirstOrderFunction - @SuppressWarnings("unchecked") - final FirstOrderFunction subFunc = (FirstOrderFunction) inputFunc; - - return createFunctionCallFromSubFunction(subFunc, inputs.subList(1, numInputs)); - } - - protected abstract void checkNumberOfArgs(int numInputs); -} \ No newline at end of file +/** + * Copyright (C) 2012-2015 Thales Services SAS. + * + * This file is part of AuthZForce CE. + * + * AuthZForce CE is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * AuthZForce CE 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with AuthZForce CE. If not, see . + */ +package org.ow2.authzforce.core.pdp.impl.func; + +import java.util.Iterator; +import java.util.List; + +import org.ow2.authzforce.core.pdp.api.AttributeValue; +import org.ow2.authzforce.core.pdp.api.BaseFunction; +import org.ow2.authzforce.core.pdp.api.Datatype; +import org.ow2.authzforce.core.pdp.api.Expression; +import org.ow2.authzforce.core.pdp.api.FirstOrderFunction; +import org.ow2.authzforce.core.pdp.api.Function; +import org.ow2.authzforce.core.pdp.api.FunctionCall; +import org.ow2.authzforce.core.pdp.api.Value; +import org.ow2.authzforce.core.pdp.api.VariableReference; +import org.ow2.authzforce.core.pdp.impl.expression.BaseVariableReference; + +/** + * Higher-order bag function + * + * @param + * return type + * @param + * sub-function's return (primitive) type. Only functions returning primitive type of result are compatible with higher-order functions here. + * @author cdangerv + * @version $Id: $ + */ +public abstract class HigherOrderBagFunction extends BaseFunction +{ + + private final Datatype returnType; + + private final Datatype subFuncReturnType; + + /** + * Instantiates higher-order bag function + * + * @param functionId + * function ID + * @param returnType + * function's return type + * @param subFunctionReturnType + * sub-function's return datatype; may be null to indicate any datatype (e.g. map function's sub-function return datatype can be any primitive type) + */ + HigherOrderBagFunction(String functionId, Datatype returnType, Datatype subFunctionReturnType) + { + super(functionId); + this.returnType = returnType; + this.subFuncReturnType = subFunctionReturnType; + } + + /** + * {@inheritDoc} + * + * Returns the type of attribute value that will be returned by this function. + */ + @Override + public Datatype getReturnType() + { + return returnType; + } + + /** + * Creates function call from sub-function definition and all inputs to higher-order function. To be overriden by OneBagOnlyFunctions (any-of/all-of) + * + * @param subFunc + * first-order sub-function + * @param inputsAfterSubFunc + * sub-function arguments + * @return function call + */ + protected abstract FunctionCall createFunctionCallFromSubFunction(FirstOrderFunction subFunc, List> inputsAfterSubFunc); + + /** {@inheritDoc} */ + @Override + public final FunctionCall newCall(List> inputs) throws IllegalArgumentException + { + final int numInputs = inputs.size(); + checkNumberOfArgs(numInputs); + + final Iterator> inputsIterator = inputs.iterator(); + final Expression input0 = inputsIterator.next(); + // first arg must be a boolean function + final Function inputFunc; + if (input0 instanceof Function) + { + inputFunc = (Function) input0; + } else if (input0 instanceof BaseVariableReference) + { + final Expression varRefExp = ((VariableReference) input0).getReferencedExpression(); + if (!(varRefExp instanceof Function)) + { + throw new IllegalArgumentException(this + ": Invalid type of first argument: " + varRefExp.getClass().getSimpleName() + ". Required: Function"); + } + + inputFunc = (Function) varRefExp; + } else + { + throw new IllegalArgumentException(this + ": Invalid type of first argument: " + input0.getClass().getSimpleName() + ". Required: Function"); + } + + /* + * Check whether it is a FirstOrderFunction because it is the only type of function for which we have a generic way to validate argument types as done later below + */ + if (!(inputFunc instanceof FirstOrderFunction)) + { + throw new IllegalArgumentException(this + ": Invalid function in first argument: " + inputFunc + " is not supported as such argument"); + } + + final Datatype inputFuncReturnType = inputFunc.getReturnType(); + if (subFuncReturnType == null) + { + /* + * sub-function's return type can be any primitive datatype; check at least it is primitive + */ + if (inputFuncReturnType.getTypeParameter() != null) + { + throw new IllegalArgumentException(this + ": Invalid return type of function in first argument: " + inputFuncReturnType + " (bag type). Required: any primitive type"); + } + } else + { + if (!inputFuncReturnType.equals(subFuncReturnType)) + { + throw new IllegalArgumentException(this + ": Invalid return type of function in first argument: " + inputFuncReturnType + ". Required: " + subFuncReturnType); + } + } + + // so now we know we have a boolean FirstOrderFunction + @SuppressWarnings("unchecked") + final FirstOrderFunction subFunc = (FirstOrderFunction) inputFunc; + + return createFunctionCallFromSubFunction(subFunc, inputs.subList(1, numInputs)); + } + + /** + *

checkNumberOfArgs

+ * + * @param numInputs a int. + */ + protected abstract void checkNumberOfArgs(int numInputs); +} diff --git a/src/main/java/org/ow2/authzforce/core/pdp/impl/func/HigherOrderBagFunctionSet.java b/src/main/java/org/ow2/authzforce/core/pdp/impl/func/HigherOrderBagFunctionSet.java index dc7e4132..f6488f2a 100644 --- a/src/main/java/org/ow2/authzforce/core/pdp/impl/func/HigherOrderBagFunctionSet.java +++ b/src/main/java/org/ow2/authzforce/core/pdp/impl/func/HigherOrderBagFunctionSet.java @@ -1,893 +1,895 @@ -/** - * Copyright (C) 2012-2015 Thales Services SAS. - * - * This file is part of AuthZForce CE. - * - * AuthZForce CE is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software - * Foundation, either version 3 of the License, or (at your option) any later version. - * - * AuthZForce CE 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with AuthZForce CE. If not, see . - */ -package org.ow2.authzforce.core.pdp.impl.func; - -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Deque; -import java.util.Iterator; -import java.util.List; -import java.util.ListIterator; - -import org.ow2.authzforce.core.pdp.api.AttributeValue; -import org.ow2.authzforce.core.pdp.api.Bag; -import org.ow2.authzforce.core.pdp.api.BagDatatype; -import org.ow2.authzforce.core.pdp.api.Datatype; -import org.ow2.authzforce.core.pdp.api.EvaluationContext; -import org.ow2.authzforce.core.pdp.api.Expression; -import org.ow2.authzforce.core.pdp.api.Expressions; -import org.ow2.authzforce.core.pdp.api.FirstOrderFunction; -import org.ow2.authzforce.core.pdp.api.FirstOrderFunctionCall; -import org.ow2.authzforce.core.pdp.api.Function; -import org.ow2.authzforce.core.pdp.api.FunctionCall; -import org.ow2.authzforce.core.pdp.api.FunctionSet; -import org.ow2.authzforce.core.pdp.api.IndeterminateEvaluationException; -import org.ow2.authzforce.core.pdp.api.StatusHelper; -import org.ow2.authzforce.core.pdp.api.Value; -import org.ow2.authzforce.core.pdp.impl.value.BooleanValue; -import org.ow2.authzforce.core.pdp.impl.value.DatatypeConstants; - -/** - * Set of higher-order bag functions - * - */ -public final class HigherOrderBagFunctionSet -{ - /** - * Standard identifier for the any-of function. WARNING: XACML 1.0 any-of planned for deprecation as of XACML 3.0. Only 3.0 version supported henceforth. - */ - public static final String NAME_ANY_OF = Function.XACML_NS_3_0 + "any-of"; - - /** - * Standard identifier for the all-of function. - */ - public static final String NAME_ALL_OF = Function.XACML_NS_3_0 + "all-of"; - - /** - * Standard identifier for the any-of-any function. - */ - public static final String NAME_ANY_OF_ANY = Function.XACML_NS_3_0 + "any-of-any"; - - /** - * Standard identifier for the all-of-any function. - */ - public static final String NAME_ALL_OF_ANY = Function.XACML_NS_1_0 + "all-of-any"; - - /** - * Standard identifier for the any-of-all function. - */ - public static final String NAME_ANY_OF_ALL = Function.XACML_NS_1_0 + "any-of-all"; - - /** - * Standard identifier for the all-of-all function. - */ - public static final String NAME_ALL_OF_ALL = Function.XACML_NS_1_0 + "all-of-all"; - - private static abstract class BooleanHigherOrderBagFunction extends HigherOrderBagFunction - { - private BooleanHigherOrderBagFunction(String functionId) - { - super(functionId, DatatypeConstants.BOOLEAN.TYPE, DatatypeConstants.BOOLEAN.TYPE); - } - } - - /** - * Higher-order boolean function taking three arguments: sub-function and two bags - * - */ - private static abstract class BooleanHigherOrderTwoBagFunction extends BooleanHigherOrderBagFunction - { - private final IllegalArgumentException invalidLastArgTypeException = new IllegalArgumentException("Function" + this + ": Invalid last argument type: primitive (not a bag). Required: a bag"); - - private BooleanHigherOrderTwoBagFunction(String functionName) - { - super(functionName); - } - - @Override - protected final void checkNumberOfArgs(int numInputs) - { - if (numInputs != 3) - { - throw new IllegalArgumentException("Function " + this + ": Invalid number of arguments (" + numInputs + "). Required: 3"); - } - } - - private static abstract class Call implements FunctionCall - { - protected final FirstOrderFunctionCall subFuncCall; - private final String errorEvalArg1Message; - private final String errorEvalArg2Message; - private final Expression> bagArgExpr0; - private final Expression> bagArgExpr1; - - private Call(String functionId, FirstOrderFunction subFunc, Expression> input0, Expression> input1) - { - final Datatype[] subFuncArgTypes = { input0.getReturnType().getTypeParameter(), input1.getReturnType().getTypeParameter() }; - this.subFuncCall = subFunc.newCall(Collections.> emptyList(), subFuncArgTypes); - this.bagArgExpr0 = input0; - this.bagArgExpr1 = input1; - this.errorEvalArg1Message = "Function " + functionId + ": Error evaluating second arg #1"; - this.errorEvalArg2Message = "Function " + functionId + ": Error evaluating arg #2"; - } - - protected abstract BooleanValue evaluate(Bag bag0, Bag bag1, EvaluationContext context) throws IndeterminateEvaluationException; - - @Override - public final BooleanValue evaluate(EvaluationContext context) throws IndeterminateEvaluationException - { - final Bag bag0; - try - { - bag0 = bagArgExpr0.evaluate(context); - } catch (IndeterminateEvaluationException e) - { - throw new IndeterminateEvaluationException(errorEvalArg1Message, StatusHelper.STATUS_PROCESSING_ERROR); - } - - /* - * If result bag empty, returns False as there will be no possibility for a Predicate that is "True". AttributeDesignator/AttributeSelector with MustBePresent=False may evaluate to - * empty bags (Indeterminate Exception if MustBePresent=True). empty bag. - */ - if (bag0.isEmpty()) - { - return BooleanValue.FALSE; - } - - final Bag bag1; - try - { - bag1 = bagArgExpr1.evaluate(context); - } catch (IndeterminateEvaluationException e) - { - throw new IndeterminateEvaluationException(errorEvalArg2Message, StatusHelper.STATUS_PROCESSING_ERROR); - } - - if (bag1.isEmpty()) - { - return BooleanValue.FALSE; - } - - return evaluate(bag0, bag1, context); - } - - @Override - public final Datatype getReturnType() - { - return DatatypeConstants.BOOLEAN.TYPE; - } - - } - - /** - * Creates function call - * - * @param subFunc - * sub-function - * @param arg0 - * first input expression returning a bag - * @param arg1 - * second input expression returning a bag - * @return function call - */ - protected abstract Call newFunctionCall(FirstOrderFunction subFunc, Expression> arg0, Expression> arg1); - - @Override - protected final FunctionCall createFunctionCallFromSubFunction(FirstOrderFunction subFunc, List> inputsAfterSubFunc) - { - - final Iterator> inputsAfterSubfuncIterator = inputsAfterSubFunc.iterator(); - - while (inputsAfterSubfuncIterator.hasNext()) - { - // all must be bag - if (inputsAfterSubfuncIterator.next().getReturnType().getTypeParameter() == null) - { - throw invalidLastArgTypeException; - } - } - - final Expression> input0 = (Expression>) inputsAfterSubFunc.get(0); - final Expression> input1 = (Expression>) inputsAfterSubFunc.get(1); - return newFunctionCall(subFunc, input0, input1); - } - } - - /** - * one-bag-only functions (only last arg is bag): any-of, all-of, map. - * - */ - static abstract class OneBagOnlyHigherOrderFunction extends HigherOrderBagFunction - { - private final String invalidArityMsgPrefix = "Function " + this + ": Invalid number of arguments: expected: >= 2; actual: "; - private final String unexpectedBagInputErrorMsg = " Function " + this + ": Invalid type (expected: primitive, actual: bag) of argument #"; - private final IllegalArgumentException invalidLastArgTypeException = new IllegalArgumentException(this + ": Invalid last argument type: expected: bag; actual: primitive"); - - static abstract class Call implements FunctionCall - { - private final String errorEvalLastArgMsg; - protected final FirstOrderFunctionCall subFuncCall; - private final Expression lastArgBagExpr; - private final BagDatatype lastArgBagDatatype; - private final Datatype returnType; - - protected Call(String functionId, Datatype returnType, FirstOrderFunction subFunction, List> primitiveInputs, Expression lastInputBag) - { - final Datatype lastArgExpDatatype = lastInputBag.getReturnType(); - if (lastArgExpDatatype.getTypeParameter() == null) - { - throw new IllegalArgumentException("Function " + functionId + ": last argument expression's return type: expected: Bag; actual: " + lastArgExpDatatype); - } - - lastArgBagExpr = lastInputBag; - // Bag.Datatype is the only Datatype implementation for Datatype> - lastArgBagDatatype = (BagDatatype) lastArgExpDatatype; - - /* - * The actual expression passed as last argument to the sub-function is not yet known; but we know the expected datatype is the type of each element lastInputBag's evaluation result - * bag, therefore the element datatype, i.e. type parameter to the returned bag datatype - */ - - this.subFuncCall = subFunction.newCall(primitiveInputs, lastInputBag.getReturnType().getTypeParameter()); - this.errorEvalLastArgMsg = "Function " + functionId + ": Error evaluating last arg (bag)"; - this.returnType = returnType; - } - - /** - * Evaluates the function call. The evaluation combines the results of evali for i in [0.. {@code lastArgBag.size()-1}], where evali is the - * evaluation of the sub-function (in this higher-order function call, i.e. first arg) with the n-1 first arguments defined in this function call - n being the arity of the sub-function - - * and the n-th/last argument is the i-th value in {@code lastArgBag} in parameter - * - * @param lastArgBag - * the bag of which each value is used successively as the last argument to the sub-function for each sub-function evaluation - * @param context - * evaluation context in which arguments are evaluated - * @return result combined result (depending on the implementation) - * @throws IndeterminateEvaluationException - * if any error occurred during evaluation - */ - protected abstract RETURN evaluate(Bag lastArgBag, EvaluationContext context) throws IndeterminateEvaluationException; - - @Override - public final RETURN evaluate(EvaluationContext context) throws IndeterminateEvaluationException - { - final Bag lastArgBag; - try - { - lastArgBag = Expressions.eval(lastArgBagExpr, context, lastArgBagDatatype); - } catch (IndeterminateEvaluationException e) - { - throw new IndeterminateEvaluationException(errorEvalLastArgMsg, e.getStatusCode(), e); - } - - return evaluate(lastArgBag, context); - } - - @Override - public final Datatype getReturnType() - { - return returnType; - } - } - - /* - * 'protected' because used by separate MapFunction class - */ - protected OneBagOnlyHigherOrderFunction(String functionName, Datatype returnType, Datatype subFunctionReturnType) - { - super(functionName, returnType, subFunctionReturnType); - } - - /* - * (non-Javadoc) - * - * @see com.thalesgroup.authzforce.core.func.HigherOrderBagFunction#checkNumberOfArgs(int) - */ - @Override - protected final void checkNumberOfArgs(int numInputs) - { - if (numInputs < 2) - { - throw new IllegalArgumentException(invalidArityMsgPrefix + numInputs); - } - } - - /** - * Creates function call - * - * @param subFunc - * sub-function - * @param primitiveInputs - * all arguments before last, all primitive (datatype already checked). - * @param lastInputBag - * last argument - bag (datatype already checked) - * @return function call - */ - protected abstract Call newFunctionCall(FirstOrderFunction subFunc, List> primitiveInputs, Expression lastInputBag); - - /* - * (non-Javadoc) - * - * @see com.thalesgroup.authzforce.core.func.HigherOrderBagFunction# createFunctionCallFromSubFunction (com.thalesgroup.authzforce.core.func.FirstOrderFunction, java.util.List, java.util.List) - */ - @Override - protected final FunctionCall createFunctionCallFromSubFunction(FirstOrderFunction subFunc, List> inputsAfterSubFunc) - { - final Iterator> inputsAfterSubfuncIterator = inputsAfterSubFunc.iterator(); - // inputs that we can parse/validate for the sub-function are the primitive inputs, i.e. - // all except last one which is a bag - final List> primitiveInputs = new ArrayList<>(); - int argIndex = 0; - Expression lastInputBag = null; - boolean hasNextInput = true; - while (hasNextInput) - { - final Expression input = inputsAfterSubfuncIterator.next(); - argIndex++; - hasNextInput = inputsAfterSubfuncIterator.hasNext(); - final Datatype inputType = input.getReturnType(); - if (hasNextInput) - { - // not the last input, must be primitive - if (inputType.getTypeParameter() != null) - { - throw new IllegalArgumentException(unexpectedBagInputErrorMsg + argIndex); - } - - primitiveInputs.add(input); - } else - { - // last input, must be a bag - if (inputType.getTypeParameter() == null) - { - throw invalidLastArgTypeException; - } - - lastInputBag = input; - } - } - - return newFunctionCall(subFunc, primitiveInputs, lastInputBag); - } - - } - - private static final class BooleanOneBagOnlyFunction extends OneBagOnlyHigherOrderFunction - { - private interface CallFactory - { - OneBagOnlyHigherOrderFunction.Call getInstance(FirstOrderFunction subFunc, List> primitiveInputs, Expression lastInputBag); - } - - private final CallFactory funcCallFactory; - - protected BooleanOneBagOnlyFunction(String functionId, CallFactory functionCallFactory) - { - super(functionId, DatatypeConstants.BOOLEAN.TYPE, DatatypeConstants.BOOLEAN.TYPE); - this.funcCallFactory = functionCallFactory; - } - - @Override - protected OneBagOnlyHigherOrderFunction.Call newFunctionCall(FirstOrderFunction subFunc, List> primitiveInputs, - Expression lastInputBag) - { - return funcCallFactory.getInstance(subFunc, primitiveInputs, lastInputBag); - } - - } - - /** - * any-of function - * - */ - private static final class AnyOfCallFactory implements BooleanOneBagOnlyFunction.CallFactory - { - private static final String SUBFUNC_CALL_WITH_LAST_ARG_ERROR_MSG_PREFIX = "Function " + NAME_ANY_OF + ": Error calling sub-function (specified as first argument) with last arg="; - - @Override - public OneBagOnlyHigherOrderFunction.Call getInstance(FirstOrderFunction subFunc, List> primitiveInputs, Expression lastInputBag) - { - return new OneBagOnlyHigherOrderFunction.Call(NAME_ANY_OF, DatatypeConstants.BOOLEAN.TYPE, subFunc, primitiveInputs, lastInputBag) - { - - @Override - protected BooleanValue evaluate(Bag lastArgBag, EvaluationContext context) throws IndeterminateEvaluationException - { - for (final AttributeValue attrVal : lastArgBag) - { - final BooleanValue subResult; - try - { - subResult = subFuncCall.evaluate(context, attrVal); - } catch (IndeterminateEvaluationException e) - { - throw new IndeterminateEvaluationException(SUBFUNC_CALL_WITH_LAST_ARG_ERROR_MSG_PREFIX + attrVal, e.getStatusCode(), e); - } - - if (subResult.getUnderlyingValue()) - { - return BooleanValue.TRUE; - } - } - - return BooleanValue.FALSE; - } - - }; - } - - } - - /** - * all-of function - * - */ - private static final class AllOfCallFactory implements BooleanOneBagOnlyFunction.CallFactory - { - private static final String SUBFUNC_CALL_WITH_LAST_ARG_ERROR_MSG_PREFIX = "Function " + NAME_ALL_OF + ": Error calling sub-function (specified as first argument) with last arg="; - - @Override - public OneBagOnlyHigherOrderFunction.Call getInstance(FirstOrderFunction subFunc, List> primitiveInputs, Expression lastInputBag) - { - return new OneBagOnlyHigherOrderFunction.Call(NAME_ALL_OF, DatatypeConstants.BOOLEAN.TYPE, subFunc, primitiveInputs, lastInputBag) - { - - @Override - protected BooleanValue evaluate(Bag lastArgBag, EvaluationContext context) throws IndeterminateEvaluationException - { - for (final AttributeValue attrVal : lastArgBag) - { - final BooleanValue subResult; - try - { - subResult = subFuncCall.evaluate(context, attrVal); - } catch (IndeterminateEvaluationException e) - { - throw new IndeterminateEvaluationException(SUBFUNC_CALL_WITH_LAST_ARG_ERROR_MSG_PREFIX + attrVal, e.getStatusCode(), e); - } - - if (!subResult.getUnderlyingValue()) - { - return BooleanValue.FALSE; - } - } - - return BooleanValue.TRUE; - } - - }; - } - - } - - /** - * any-of-any function - * - */ - private static final class AnyOfAny extends BooleanHigherOrderBagFunction - { - - private static final String SUB_FUNC_EVAL_ERROR_MSG = "Function " + NAME_ANY_OF_ANY + ": Error evaluating one of the arguments after sub-function (first arg)"; - - /** - * Default constructor - */ - private AnyOfAny() - { - super(NAME_ANY_OF_ANY); - } - - @Override - protected void checkNumberOfArgs(int numInputs) - { - if (numInputs < 2) - { - throw new IllegalArgumentException("Function " + this + ": Invalid number of arguments (" + numInputs + "). Required: >= 2"); - } - } - - @Override - protected FunctionCall createFunctionCallFromSubFunction(FirstOrderFunction subFunc, List> inputsAfterSubFunc) - { - return new AnyOfAnyFunctionCall(subFunc, inputsAfterSubFunc); - } - - private static final class AnyOfAnyFunctionCall implements FunctionCall - { - private final FirstOrderFunctionCall subFuncCall; - private final int subFuncArity; - private final List> inputsAfterSubFunc; - private final String subFunctionCallErrorMessagePrefix; - - protected AnyOfAnyFunctionCall(FirstOrderFunction subFunc, List> inputsAfterSubFunc) - { - /* - * According to spec of an-of-any function, the remaining arguments (inputsAfterSubFunc here) are either primitive data types or bags of primitive types. The expression SHALL be - * evaluated as if the function named in the argument (subFunc here) was applied between every tuple of the cross product on all bags and the primitive values. - */ - this.subFuncArity = inputsAfterSubFunc.size(); - final Datatype[] subFuncArgTypes = new Datatype[subFuncArity]; - int i = 0; - for (final Expression input : inputsAfterSubFunc) - { - final Datatype inputDatatype = input.getReturnType(); - /* - * Always primitive datatype used in the sub-function call (typeParameter of the datatype for a bag datatype, else the datatype itself (already primitive)) - */ - subFuncArgTypes[i] = inputDatatype.getTypeParameter() == null ? inputDatatype : inputDatatype.getTypeParameter(); - i++; - } - - this.subFuncCall = subFunc.newCall(Collections.> emptyList(), subFuncArgTypes); - this.inputsAfterSubFunc = inputsAfterSubFunc; - this.subFunctionCallErrorMessagePrefix = "Function " + NAME_ANY_OF_ANY + ": Error evaluating sub-function with arguments (evaluated to): "; - } - - private BooleanValue eval(Iterator> argExpressionsAfterSubFuncIterator, ListIterator argValuesAfterSubFuncIterator, Deque subFuncArgsStack, - EvaluationContext context) throws IndeterminateEvaluationException - { - final Value argVal; - if (argExpressionsAfterSubFuncIterator.hasNext()) - { - // we are still evaluating argument expressions for the first time - try - { - argVal = argExpressionsAfterSubFuncIterator.next().evaluate(context); - - } catch (IndeterminateEvaluationException e) - { - throw new IndeterminateEvaluationException(SUB_FUNC_EVAL_ERROR_MSG, e.getStatusCode(), e); - } - // save the result for reuse when building the next list of sub-function - // arguments to avoid re-evaluation - argValuesAfterSubFuncIterator.add(argVal); - } else - { - /* - * No more arg expression to evaluate, but we may have evaluated them all with results put in argValuesAfterSubFuncIterator, then started a new combination of arguments from the - * start, working with argValuesAfterSubFuncIterator only after that. So check where we are with argValuesAfterSubFuncIterator - */ - if (argValuesAfterSubFuncIterator.hasNext()) - { - argVal = argValuesAfterSubFuncIterator.next(); - } else - { - // no more argument to add to the list of sub-function arguments - argVal = null; - } - } - - if (argVal == null) - { - // we finished a list of sub-function arguments, so we can call the sub-function - // with it - final AttributeValue[] subFuncArgValues = subFuncArgsStack.toArray(new AttributeValue[subFuncArity]); - try - { - return subFuncCall.evaluate(context, subFuncArgValues); - } catch (IndeterminateEvaluationException e) - { - throw new IndeterminateEvaluationException(subFunctionCallErrorMessagePrefix + subFuncArgsStack, e.getStatusCode(), e); - } - } - - // argVal != null - if (argVal instanceof Bag) - { - // arg value is a bag - /* - * If bag empty, returns False as there will be no possibility for a predicate to be "True"; in particular if AttributeDesignator/AttributeSelector with MustBePresent=False - * evaluates to empty bag. - */ - final Bag argBag = (Bag) argVal; - if (argBag.isEmpty()) - { - return BooleanValue.FALSE; - } - /* - * For each value in the arg bag, add it to the sub-function argument stack and call eval() - */ - - for (final AttributeValue argBagVal : argBag) - { - subFuncArgsStack.add(argBagVal); - final BooleanValue subResult = eval(argExpressionsAfterSubFuncIterator, argValuesAfterSubFuncIterator, subFuncArgsStack, context); - if (subResult.getUnderlyingValue()) - { - return BooleanValue.TRUE; - } - - /* - * Remove the arg we just added at the start of the iteration, to leave the place for the new arg in the next iteration - */ - subFuncArgsStack.removeLast(); - } - - } else - { - // arg value is primitive - // add it to the sub-function call's argument stack - subFuncArgsStack.add((AttributeValue) argVal); - // evaluate with the new arg stack - final BooleanValue subResult = eval(argExpressionsAfterSubFuncIterator, argValuesAfterSubFuncIterator, subFuncArgsStack, context); - if (subResult.getUnderlyingValue()) - { - return BooleanValue.TRUE; - } - - /* - * Remove the arg we just added at the start of the iteration, to leave the place for the new arg in the next iteration - */ - subFuncArgsStack.removeLast(); - } - - /* - * argVal != null and either argValuesAfterSubFuncIterator.next() or argValuesAfterSubFuncIterator.add(...) was called so we need to go backwards now to prepare next eval(). - */ - argValuesAfterSubFuncIterator.previous(); - return BooleanValue.FALSE; - - } - - @Override - public BooleanValue evaluate(EvaluationContext context) throws IndeterminateEvaluationException - { - /* - * For each input expression coming from inputsAfterSubFunc, the evaluation result will be added to the following list, to avoid evaluating the same expression again as each one will - * be reused in multiple combination of arguments: - */ - final List inputsAfterSubFuncEvalResults = new ArrayList<>(); - - /* - * We build the stack (Deque) of sub-function argument values (extracted progressively from inputsAfterSubFuncEvalResults). Deque provides LIFO stack which is convenient for managing - * the sub-function arguments because we will be pushing/popping for each value in each bag argument, to make the list of the sub-function's arguments before we can make each - * sub-function call. - */ - final Deque subFuncArgsStack = new ArrayDeque<>(subFuncArity); - - // the subsequent logic is put in separated method because we need to call it - // recursively over nonFirstArgExpsIterator - return eval(inputsAfterSubFunc.iterator(), inputsAfterSubFuncEvalResults.listIterator(), subFuncArgsStack, context); - } - - @Override - public Datatype getReturnType() - { - return DatatypeConstants.BOOLEAN.TYPE; - } - - } - - } - - /** - * all-of-any function - * - */ - private static final class AllOfAny extends BooleanHigherOrderTwoBagFunction - { - - private final String subFunctionCallErrorMessagePrefix; - - /** - * Default constructor - */ - private AllOfAny() - { - super(NAME_ALL_OF_ANY); - this.subFunctionCallErrorMessagePrefix = "Function " + NAME_ALL_OF_ANY + ": Error evaluating sub-function with arguments (evaluated to): "; - } - - @Override - protected BooleanHigherOrderTwoBagFunction.Call newFunctionCall(FirstOrderFunction subFunc, Expression> arg0, Expression> arg1) - { - return new BooleanHigherOrderTwoBagFunction.Call(NAME_ALL_OF_ANY, subFunc, arg0, arg1) - { - - @Override - protected BooleanValue evaluate(Bag bag0, Bag bag1, EvaluationContext context) throws IndeterminateEvaluationException - { - final Deque subFuncArgStack = new ArrayDeque<>(2); - for (final AttributeValue bag0Val : bag0) - { - subFuncArgStack.add(bag0Val); - boolean isAnyTrue = false; - for (final AttributeValue bag1Val : bag1) - { - subFuncArgStack.add(bag1Val); - final AttributeValue[] subFuncArgValues = subFuncArgStack.toArray(new AttributeValue[2]); - final BooleanValue subResult; - try - { - subResult = subFuncCall.evaluate(context, subFuncArgValues); - } catch (IndeterminateEvaluationException e) - { - throw new IndeterminateEvaluationException(subFunctionCallErrorMessagePrefix + subFuncArgStack, StatusHelper.STATUS_PROCESSING_ERROR); - } - - subFuncArgStack.removeLast(); - if (subResult.getUnderlyingValue()) - { - isAnyTrue = true; - break; - } - } - - if (!isAnyTrue) - { - return BooleanValue.FALSE; - } - - subFuncArgStack.removeLast(); - } - - return BooleanValue.TRUE; - } - - }; - } - } - - /** - * any-of-all function - * - */ - private static final class AnyOfAll extends BooleanHigherOrderTwoBagFunction - { - - private final String subFunctionCallErrorMessagePrefix; - - /** - * Default constructor - */ - private AnyOfAll() - { - super(NAME_ANY_OF_ALL); - this.subFunctionCallErrorMessagePrefix = "Function " + NAME_ANY_OF_ALL + ": Error evaluating sub-function with arguments (evaluated to): "; - } - - @Override - protected BooleanHigherOrderTwoBagFunction.Call newFunctionCall(FirstOrderFunction subFunc, Expression> arg0, Expression> arg1) - { - return new BooleanHigherOrderTwoBagFunction.Call(NAME_ANY_OF_ALL, subFunc, arg0, arg1) - { - - @Override - protected BooleanValue evaluate(Bag bag0, Bag bag1, EvaluationContext context) throws IndeterminateEvaluationException - { - final Deque subFuncArgStack = new ArrayDeque<>(2); - - // same as all-of-any but in reverse order of bag0 and bag1 - for (final AttributeValue bag1Val : bag1) - { - boolean isAnyTrue = false; - for (final AttributeValue bag0Val : bag0) - { - subFuncArgStack.add(bag0Val); - subFuncArgStack.add(bag1Val); - final AttributeValue[] subFuncArgValues = subFuncArgStack.toArray(new AttributeValue[2]); - final BooleanValue subResult; - try - { - subResult = subFuncCall.evaluate(context, subFuncArgValues); - } catch (IndeterminateEvaluationException e) - { - throw new IndeterminateEvaluationException(subFunctionCallErrorMessagePrefix + subFuncArgStack, StatusHelper.STATUS_PROCESSING_ERROR); - } - - subFuncArgStack.removeLast();// remove bag1val - subFuncArgStack.removeLast();// remove bag0val - if (subResult.getUnderlyingValue()) - { - isAnyTrue = true; - break; - } - } - - if (!isAnyTrue) - { - return BooleanValue.FALSE; - } - } - - return BooleanValue.TRUE; - } - }; - } - } - - /** - * any-of-all function - * - */ - private static final class AllOfAll extends BooleanHigherOrderTwoBagFunction - { - - private final String subFunctionCallErrorMessagePrefix; - - /** - * Default constructor - */ - private AllOfAll() - { - super(NAME_ALL_OF_ALL); - this.subFunctionCallErrorMessagePrefix = "Function " + NAME_ALL_OF_ALL + ": Error evaluating sub-function with arguments (evaluated to): "; - - } - - @Override - protected BooleanHigherOrderTwoBagFunction.Call newFunctionCall(FirstOrderFunction subFunc, Expression> arg0, Expression> arg1) - { - return new BooleanHigherOrderTwoBagFunction.Call(NAME_ALL_OF_ALL, subFunc, arg0, arg1) - { - - @Override - protected BooleanValue evaluate(Bag bag0, Bag bag1, EvaluationContext context) throws IndeterminateEvaluationException - { - final Deque subFuncArgStack = new ArrayDeque<>(2); - for (final AttributeValue bag0Val : bag0) - { - subFuncArgStack.add(bag0Val); - boolean areAllTrue = true; - for (final AttributeValue bag1Val : bag1) - { - subFuncArgStack.add(bag1Val); - final AttributeValue[] subFuncArgValues = subFuncArgStack.toArray(new AttributeValue[2]); - final BooleanValue subResult; - try - { - subResult = subFuncCall.evaluate(context, subFuncArgValues); - } catch (IndeterminateEvaluationException e) - { - throw new IndeterminateEvaluationException(subFunctionCallErrorMessagePrefix + subFuncArgStack, StatusHelper.STATUS_PROCESSING_ERROR); - } - - subFuncArgStack.removeLast(); - if (!subResult.getUnderlyingValue()) - { - areAllTrue = false; - break; - } - } - - if (!areAllTrue) - { - return BooleanValue.FALSE; - } - - subFuncArgStack.removeLast(); - } - - return BooleanValue.TRUE; - } - }; - } - } - - private HigherOrderBagFunctionSet() - { - - } - - /** - * Function cluster - */ - public static final FunctionSet INSTANCE = new BaseFunctionSet(FunctionSet.DEFAULT_ID_NAMESPACE + "higher-order-bag", - // - new BooleanOneBagOnlyFunction(NAME_ANY_OF, new AnyOfCallFactory()),// - new BooleanOneBagOnlyFunction(NAME_ALL_OF, new AllOfCallFactory()),// - new AnyOfAny(), new AllOfAny(), new AnyOfAll(), new AllOfAll()); - -} +/** + * Copyright (C) 2012-2015 Thales Services SAS. + * + * This file is part of AuthZForce CE. + * + * AuthZForce CE is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * AuthZForce CE 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with AuthZForce CE. If not, see . + */ +package org.ow2.authzforce.core.pdp.impl.func; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Deque; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; + +import org.ow2.authzforce.core.pdp.api.AttributeValue; +import org.ow2.authzforce.core.pdp.api.Bag; +import org.ow2.authzforce.core.pdp.api.BagDatatype; +import org.ow2.authzforce.core.pdp.api.Datatype; +import org.ow2.authzforce.core.pdp.api.EvaluationContext; +import org.ow2.authzforce.core.pdp.api.Expression; +import org.ow2.authzforce.core.pdp.api.Expressions; +import org.ow2.authzforce.core.pdp.api.FirstOrderFunction; +import org.ow2.authzforce.core.pdp.api.FirstOrderFunctionCall; +import org.ow2.authzforce.core.pdp.api.Function; +import org.ow2.authzforce.core.pdp.api.FunctionCall; +import org.ow2.authzforce.core.pdp.api.FunctionSet; +import org.ow2.authzforce.core.pdp.api.IndeterminateEvaluationException; +import org.ow2.authzforce.core.pdp.api.StatusHelper; +import org.ow2.authzforce.core.pdp.api.Value; +import org.ow2.authzforce.core.pdp.impl.value.BooleanValue; +import org.ow2.authzforce.core.pdp.impl.value.DatatypeConstants; + +/** + * Set of higher-order bag functions + * + * @author cdangerv + * @version $Id: $ + */ +public final class HigherOrderBagFunctionSet +{ + /** + * Standard identifier for the any-of function. WARNING: XACML 1.0 any-of planned for deprecation as of XACML 3.0. Only 3.0 version supported henceforth. + */ + public static final String NAME_ANY_OF = Function.XACML_NS_3_0 + "any-of"; + + /** + * Standard identifier for the all-of function. + */ + public static final String NAME_ALL_OF = Function.XACML_NS_3_0 + "all-of"; + + /** + * Standard identifier for the any-of-any function. + */ + public static final String NAME_ANY_OF_ANY = Function.XACML_NS_3_0 + "any-of-any"; + + /** + * Standard identifier for the all-of-any function. + */ + public static final String NAME_ALL_OF_ANY = Function.XACML_NS_1_0 + "all-of-any"; + + /** + * Standard identifier for the any-of-all function. + */ + public static final String NAME_ANY_OF_ALL = Function.XACML_NS_1_0 + "any-of-all"; + + /** + * Standard identifier for the all-of-all function. + */ + public static final String NAME_ALL_OF_ALL = Function.XACML_NS_1_0 + "all-of-all"; + + private static abstract class BooleanHigherOrderBagFunction extends HigherOrderBagFunction + { + private BooleanHigherOrderBagFunction(String functionId) + { + super(functionId, DatatypeConstants.BOOLEAN.TYPE, DatatypeConstants.BOOLEAN.TYPE); + } + } + + /** + * Higher-order boolean function taking three arguments: sub-function and two bags + * + */ + private static abstract class BooleanHigherOrderTwoBagFunction extends BooleanHigherOrderBagFunction + { + private final IllegalArgumentException invalidLastArgTypeException = new IllegalArgumentException("Function" + this + ": Invalid last argument type: primitive (not a bag). Required: a bag"); + + private BooleanHigherOrderTwoBagFunction(String functionName) + { + super(functionName); + } + + @Override + protected final void checkNumberOfArgs(int numInputs) + { + if (numInputs != 3) + { + throw new IllegalArgumentException("Function " + this + ": Invalid number of arguments (" + numInputs + "). Required: 3"); + } + } + + private static abstract class Call implements FunctionCall + { + protected final FirstOrderFunctionCall subFuncCall; + private final String errorEvalArg1Message; + private final String errorEvalArg2Message; + private final Expression> bagArgExpr0; + private final Expression> bagArgExpr1; + + private Call(String functionId, FirstOrderFunction subFunc, Expression> input0, Expression> input1) + { + final Datatype[] subFuncArgTypes = { input0.getReturnType().getTypeParameter(), input1.getReturnType().getTypeParameter() }; + this.subFuncCall = subFunc.newCall(Collections.> emptyList(), subFuncArgTypes); + this.bagArgExpr0 = input0; + this.bagArgExpr1 = input1; + this.errorEvalArg1Message = "Function " + functionId + ": Error evaluating second arg #1"; + this.errorEvalArg2Message = "Function " + functionId + ": Error evaluating arg #2"; + } + + protected abstract BooleanValue evaluate(Bag bag0, Bag bag1, EvaluationContext context) throws IndeterminateEvaluationException; + + @Override + public final BooleanValue evaluate(EvaluationContext context) throws IndeterminateEvaluationException + { + final Bag bag0; + try + { + bag0 = bagArgExpr0.evaluate(context); + } catch (IndeterminateEvaluationException e) + { + throw new IndeterminateEvaluationException(errorEvalArg1Message, StatusHelper.STATUS_PROCESSING_ERROR); + } + + /* + * If result bag empty, returns False as there will be no possibility for a Predicate that is "True". AttributeDesignator/AttributeSelector with MustBePresent=False may evaluate to + * empty bags (Indeterminate Exception if MustBePresent=True). empty bag. + */ + if (bag0.isEmpty()) + { + return BooleanValue.FALSE; + } + + final Bag bag1; + try + { + bag1 = bagArgExpr1.evaluate(context); + } catch (IndeterminateEvaluationException e) + { + throw new IndeterminateEvaluationException(errorEvalArg2Message, StatusHelper.STATUS_PROCESSING_ERROR); + } + + if (bag1.isEmpty()) + { + return BooleanValue.FALSE; + } + + return evaluate(bag0, bag1, context); + } + + @Override + public final Datatype getReturnType() + { + return DatatypeConstants.BOOLEAN.TYPE; + } + + } + + /** + * Creates function call + * + * @param subFunc + * sub-function + * @param arg0 + * first input expression returning a bag + * @param arg1 + * second input expression returning a bag + * @return function call + */ + protected abstract Call newFunctionCall(FirstOrderFunction subFunc, Expression> arg0, Expression> arg1); + + @Override + protected final FunctionCall createFunctionCallFromSubFunction(FirstOrderFunction subFunc, List> inputsAfterSubFunc) + { + + final Iterator> inputsAfterSubfuncIterator = inputsAfterSubFunc.iterator(); + + while (inputsAfterSubfuncIterator.hasNext()) + { + // all must be bag + if (inputsAfterSubfuncIterator.next().getReturnType().getTypeParameter() == null) + { + throw invalidLastArgTypeException; + } + } + + final Expression> input0 = (Expression>) inputsAfterSubFunc.get(0); + final Expression> input1 = (Expression>) inputsAfterSubFunc.get(1); + return newFunctionCall(subFunc, input0, input1); + } + } + + /** + * one-bag-only functions (only last arg is bag): any-of, all-of, map. + * + */ + static abstract class OneBagOnlyHigherOrderFunction extends HigherOrderBagFunction + { + private final String invalidArityMsgPrefix = "Function " + this + ": Invalid number of arguments: expected: >= 2; actual: "; + private final String unexpectedBagInputErrorMsg = " Function " + this + ": Invalid type (expected: primitive, actual: bag) of argument #"; + private final IllegalArgumentException invalidLastArgTypeException = new IllegalArgumentException(this + ": Invalid last argument type: expected: bag; actual: primitive"); + + static abstract class Call implements FunctionCall + { + private final String errorEvalLastArgMsg; + protected final FirstOrderFunctionCall subFuncCall; + private final Expression lastArgBagExpr; + private final BagDatatype lastArgBagDatatype; + private final Datatype returnType; + + protected Call(String functionId, Datatype returnType, FirstOrderFunction subFunction, List> primitiveInputs, Expression lastInputBag) + { + final Datatype lastArgExpDatatype = lastInputBag.getReturnType(); + if (lastArgExpDatatype.getTypeParameter() == null) + { + throw new IllegalArgumentException("Function " + functionId + ": last argument expression's return type: expected: Bag; actual: " + lastArgExpDatatype); + } + + lastArgBagExpr = lastInputBag; + // Bag.Datatype is the only Datatype implementation for Datatype> + lastArgBagDatatype = (BagDatatype) lastArgExpDatatype; + + /* + * The actual expression passed as last argument to the sub-function is not yet known; but we know the expected datatype is the type of each element lastInputBag's evaluation result + * bag, therefore the element datatype, i.e. type parameter to the returned bag datatype + */ + + this.subFuncCall = subFunction.newCall(primitiveInputs, lastInputBag.getReturnType().getTypeParameter()); + this.errorEvalLastArgMsg = "Function " + functionId + ": Error evaluating last arg (bag)"; + this.returnType = returnType; + } + + /** + * Evaluates the function call. The evaluation combines the results of evali for i in [0.. {@code lastArgBag.size()-1}], where evali is the + * evaluation of the sub-function (in this higher-order function call, i.e. first arg) with the n-1 first arguments defined in this function call - n being the arity of the sub-function - + * and the n-th/last argument is the i-th value in {@code lastArgBag} in parameter + * + * @param lastArgBag + * the bag of which each value is used successively as the last argument to the sub-function for each sub-function evaluation + * @param context + * evaluation context in which arguments are evaluated + * @return result combined result (depending on the implementation) + * @throws IndeterminateEvaluationException + * if any error occurred during evaluation + */ + protected abstract RETURN evaluate(Bag lastArgBag, EvaluationContext context) throws IndeterminateEvaluationException; + + @Override + public final RETURN evaluate(EvaluationContext context) throws IndeterminateEvaluationException + { + final Bag lastArgBag; + try + { + lastArgBag = Expressions.eval(lastArgBagExpr, context, lastArgBagDatatype); + } catch (IndeterminateEvaluationException e) + { + throw new IndeterminateEvaluationException(errorEvalLastArgMsg, e.getStatusCode(), e); + } + + return evaluate(lastArgBag, context); + } + + @Override + public final Datatype getReturnType() + { + return returnType; + } + } + + /* + * 'protected' because used by separate MapFunction class + */ + protected OneBagOnlyHigherOrderFunction(String functionName, Datatype returnType, Datatype subFunctionReturnType) + { + super(functionName, returnType, subFunctionReturnType); + } + + /* + * (non-Javadoc) + * + * @see com.thalesgroup.authzforce.core.func.HigherOrderBagFunction#checkNumberOfArgs(int) + */ + @Override + protected final void checkNumberOfArgs(int numInputs) + { + if (numInputs < 2) + { + throw new IllegalArgumentException(invalidArityMsgPrefix + numInputs); + } + } + + /** + * Creates function call + * + * @param subFunc + * sub-function + * @param primitiveInputs + * all arguments before last, all primitive (datatype already checked). + * @param lastInputBag + * last argument - bag (datatype already checked) + * @return function call + */ + protected abstract Call newFunctionCall(FirstOrderFunction subFunc, List> primitiveInputs, Expression lastInputBag); + + /* + * (non-Javadoc) + * + * @see com.thalesgroup.authzforce.core.func.HigherOrderBagFunction# createFunctionCallFromSubFunction (com.thalesgroup.authzforce.core.func.FirstOrderFunction, java.util.List, java.util.List) + */ + @Override + protected final FunctionCall createFunctionCallFromSubFunction(FirstOrderFunction subFunc, List> inputsAfterSubFunc) + { + final Iterator> inputsAfterSubfuncIterator = inputsAfterSubFunc.iterator(); + // inputs that we can parse/validate for the sub-function are the primitive inputs, i.e. + // all except last one which is a bag + final List> primitiveInputs = new ArrayList<>(); + int argIndex = 0; + Expression lastInputBag = null; + boolean hasNextInput = true; + while (hasNextInput) + { + final Expression input = inputsAfterSubfuncIterator.next(); + argIndex++; + hasNextInput = inputsAfterSubfuncIterator.hasNext(); + final Datatype inputType = input.getReturnType(); + if (hasNextInput) + { + // not the last input, must be primitive + if (inputType.getTypeParameter() != null) + { + throw new IllegalArgumentException(unexpectedBagInputErrorMsg + argIndex); + } + + primitiveInputs.add(input); + } else + { + // last input, must be a bag + if (inputType.getTypeParameter() == null) + { + throw invalidLastArgTypeException; + } + + lastInputBag = input; + } + } + + return newFunctionCall(subFunc, primitiveInputs, lastInputBag); + } + + } + + private static final class BooleanOneBagOnlyFunction extends OneBagOnlyHigherOrderFunction + { + private interface CallFactory + { + OneBagOnlyHigherOrderFunction.Call getInstance(FirstOrderFunction subFunc, List> primitiveInputs, Expression lastInputBag); + } + + private final CallFactory funcCallFactory; + + protected BooleanOneBagOnlyFunction(String functionId, CallFactory functionCallFactory) + { + super(functionId, DatatypeConstants.BOOLEAN.TYPE, DatatypeConstants.BOOLEAN.TYPE); + this.funcCallFactory = functionCallFactory; + } + + @Override + protected OneBagOnlyHigherOrderFunction.Call newFunctionCall(FirstOrderFunction subFunc, List> primitiveInputs, + Expression lastInputBag) + { + return funcCallFactory.getInstance(subFunc, primitiveInputs, lastInputBag); + } + + } + + /** + * any-of function + * + */ + private static final class AnyOfCallFactory implements BooleanOneBagOnlyFunction.CallFactory + { + private static final String SUBFUNC_CALL_WITH_LAST_ARG_ERROR_MSG_PREFIX = "Function " + NAME_ANY_OF + ": Error calling sub-function (specified as first argument) with last arg="; + + @Override + public OneBagOnlyHigherOrderFunction.Call getInstance(FirstOrderFunction subFunc, List> primitiveInputs, Expression lastInputBag) + { + return new OneBagOnlyHigherOrderFunction.Call(NAME_ANY_OF, DatatypeConstants.BOOLEAN.TYPE, subFunc, primitiveInputs, lastInputBag) + { + + @Override + protected BooleanValue evaluate(Bag lastArgBag, EvaluationContext context) throws IndeterminateEvaluationException + { + for (final AttributeValue attrVal : lastArgBag) + { + final BooleanValue subResult; + try + { + subResult = subFuncCall.evaluate(context, attrVal); + } catch (IndeterminateEvaluationException e) + { + throw new IndeterminateEvaluationException(SUBFUNC_CALL_WITH_LAST_ARG_ERROR_MSG_PREFIX + attrVal, e.getStatusCode(), e); + } + + if (subResult.getUnderlyingValue()) + { + return BooleanValue.TRUE; + } + } + + return BooleanValue.FALSE; + } + + }; + } + + } + + /** + * all-of function + * + */ + private static final class AllOfCallFactory implements BooleanOneBagOnlyFunction.CallFactory + { + private static final String SUBFUNC_CALL_WITH_LAST_ARG_ERROR_MSG_PREFIX = "Function " + NAME_ALL_OF + ": Error calling sub-function (specified as first argument) with last arg="; + + @Override + public OneBagOnlyHigherOrderFunction.Call getInstance(FirstOrderFunction subFunc, List> primitiveInputs, Expression lastInputBag) + { + return new OneBagOnlyHigherOrderFunction.Call(NAME_ALL_OF, DatatypeConstants.BOOLEAN.TYPE, subFunc, primitiveInputs, lastInputBag) + { + + @Override + protected BooleanValue evaluate(Bag lastArgBag, EvaluationContext context) throws IndeterminateEvaluationException + { + for (final AttributeValue attrVal : lastArgBag) + { + final BooleanValue subResult; + try + { + subResult = subFuncCall.evaluate(context, attrVal); + } catch (IndeterminateEvaluationException e) + { + throw new IndeterminateEvaluationException(SUBFUNC_CALL_WITH_LAST_ARG_ERROR_MSG_PREFIX + attrVal, e.getStatusCode(), e); + } + + if (!subResult.getUnderlyingValue()) + { + return BooleanValue.FALSE; + } + } + + return BooleanValue.TRUE; + } + + }; + } + + } + + /** + * any-of-any function + * + */ + private static final class AnyOfAny extends BooleanHigherOrderBagFunction + { + + private static final String SUB_FUNC_EVAL_ERROR_MSG = "Function " + NAME_ANY_OF_ANY + ": Error evaluating one of the arguments after sub-function (first arg)"; + + /** + * Default constructor + */ + private AnyOfAny() + { + super(NAME_ANY_OF_ANY); + } + + @Override + protected void checkNumberOfArgs(int numInputs) + { + if (numInputs < 2) + { + throw new IllegalArgumentException("Function " + this + ": Invalid number of arguments (" + numInputs + "). Required: >= 2"); + } + } + + @Override + protected FunctionCall createFunctionCallFromSubFunction(FirstOrderFunction subFunc, List> inputsAfterSubFunc) + { + return new AnyOfAnyFunctionCall(subFunc, inputsAfterSubFunc); + } + + private static final class AnyOfAnyFunctionCall implements FunctionCall + { + private final FirstOrderFunctionCall subFuncCall; + private final int subFuncArity; + private final List> inputsAfterSubFunc; + private final String subFunctionCallErrorMessagePrefix; + + protected AnyOfAnyFunctionCall(FirstOrderFunction subFunc, List> inputsAfterSubFunc) + { + /* + * According to spec of an-of-any function, the remaining arguments (inputsAfterSubFunc here) are either primitive data types or bags of primitive types. The expression SHALL be + * evaluated as if the function named in the argument (subFunc here) was applied between every tuple of the cross product on all bags and the primitive values. + */ + this.subFuncArity = inputsAfterSubFunc.size(); + final Datatype[] subFuncArgTypes = new Datatype[subFuncArity]; + int i = 0; + for (final Expression input : inputsAfterSubFunc) + { + final Datatype inputDatatype = input.getReturnType(); + /* + * Always primitive datatype used in the sub-function call (typeParameter of the datatype for a bag datatype, else the datatype itself (already primitive)) + */ + subFuncArgTypes[i] = inputDatatype.getTypeParameter() == null ? inputDatatype : inputDatatype.getTypeParameter(); + i++; + } + + this.subFuncCall = subFunc.newCall(Collections.> emptyList(), subFuncArgTypes); + this.inputsAfterSubFunc = inputsAfterSubFunc; + this.subFunctionCallErrorMessagePrefix = "Function " + NAME_ANY_OF_ANY + ": Error evaluating sub-function with arguments (evaluated to): "; + } + + private BooleanValue eval(Iterator> argExpressionsAfterSubFuncIterator, ListIterator argValuesAfterSubFuncIterator, Deque subFuncArgsStack, + EvaluationContext context) throws IndeterminateEvaluationException + { + final Value argVal; + if (argExpressionsAfterSubFuncIterator.hasNext()) + { + // we are still evaluating argument expressions for the first time + try + { + argVal = argExpressionsAfterSubFuncIterator.next().evaluate(context); + + } catch (IndeterminateEvaluationException e) + { + throw new IndeterminateEvaluationException(SUB_FUNC_EVAL_ERROR_MSG, e.getStatusCode(), e); + } + // save the result for reuse when building the next list of sub-function + // arguments to avoid re-evaluation + argValuesAfterSubFuncIterator.add(argVal); + } else + { + /* + * No more arg expression to evaluate, but we may have evaluated them all with results put in argValuesAfterSubFuncIterator, then started a new combination of arguments from the + * start, working with argValuesAfterSubFuncIterator only after that. So check where we are with argValuesAfterSubFuncIterator + */ + if (argValuesAfterSubFuncIterator.hasNext()) + { + argVal = argValuesAfterSubFuncIterator.next(); + } else + { + // no more argument to add to the list of sub-function arguments + argVal = null; + } + } + + if (argVal == null) + { + // we finished a list of sub-function arguments, so we can call the sub-function + // with it + final AttributeValue[] subFuncArgValues = subFuncArgsStack.toArray(new AttributeValue[subFuncArity]); + try + { + return subFuncCall.evaluate(context, subFuncArgValues); + } catch (IndeterminateEvaluationException e) + { + throw new IndeterminateEvaluationException(subFunctionCallErrorMessagePrefix + subFuncArgsStack, e.getStatusCode(), e); + } + } + + // argVal != null + if (argVal instanceof Bag) + { + // arg value is a bag + /* + * If bag empty, returns False as there will be no possibility for a predicate to be "True"; in particular if AttributeDesignator/AttributeSelector with MustBePresent=False + * evaluates to empty bag. + */ + final Bag argBag = (Bag) argVal; + if (argBag.isEmpty()) + { + return BooleanValue.FALSE; + } + /* + * For each value in the arg bag, add it to the sub-function argument stack and call eval() + */ + + for (final AttributeValue argBagVal : argBag) + { + subFuncArgsStack.add(argBagVal); + final BooleanValue subResult = eval(argExpressionsAfterSubFuncIterator, argValuesAfterSubFuncIterator, subFuncArgsStack, context); + if (subResult.getUnderlyingValue()) + { + return BooleanValue.TRUE; + } + + /* + * Remove the arg we just added at the start of the iteration, to leave the place for the new arg in the next iteration + */ + subFuncArgsStack.removeLast(); + } + + } else + { + // arg value is primitive + // add it to the sub-function call's argument stack + subFuncArgsStack.add((AttributeValue) argVal); + // evaluate with the new arg stack + final BooleanValue subResult = eval(argExpressionsAfterSubFuncIterator, argValuesAfterSubFuncIterator, subFuncArgsStack, context); + if (subResult.getUnderlyingValue()) + { + return BooleanValue.TRUE; + } + + /* + * Remove the arg we just added at the start of the iteration, to leave the place for the new arg in the next iteration + */ + subFuncArgsStack.removeLast(); + } + + /* + * argVal != null and either argValuesAfterSubFuncIterator.next() or argValuesAfterSubFuncIterator.add(...) was called so we need to go backwards now to prepare next eval(). + */ + argValuesAfterSubFuncIterator.previous(); + return BooleanValue.FALSE; + + } + + @Override + public BooleanValue evaluate(EvaluationContext context) throws IndeterminateEvaluationException + { + /* + * For each input expression coming from inputsAfterSubFunc, the evaluation result will be added to the following list, to avoid evaluating the same expression again as each one will + * be reused in multiple combination of arguments: + */ + final List inputsAfterSubFuncEvalResults = new ArrayList<>(); + + /* + * We build the stack (Deque) of sub-function argument values (extracted progressively from inputsAfterSubFuncEvalResults). Deque provides LIFO stack which is convenient for managing + * the sub-function arguments because we will be pushing/popping for each value in each bag argument, to make the list of the sub-function's arguments before we can make each + * sub-function call. + */ + final Deque subFuncArgsStack = new ArrayDeque<>(subFuncArity); + + // the subsequent logic is put in separated method because we need to call it + // recursively over nonFirstArgExpsIterator + return eval(inputsAfterSubFunc.iterator(), inputsAfterSubFuncEvalResults.listIterator(), subFuncArgsStack, context); + } + + @Override + public Datatype getReturnType() + { + return DatatypeConstants.BOOLEAN.TYPE; + } + + } + + } + + /** + * all-of-any function + * + */ + private static final class AllOfAny extends BooleanHigherOrderTwoBagFunction + { + + private final String subFunctionCallErrorMessagePrefix; + + /** + * Default constructor + */ + private AllOfAny() + { + super(NAME_ALL_OF_ANY); + this.subFunctionCallErrorMessagePrefix = "Function " + NAME_ALL_OF_ANY + ": Error evaluating sub-function with arguments (evaluated to): "; + } + + @Override + protected BooleanHigherOrderTwoBagFunction.Call newFunctionCall(FirstOrderFunction subFunc, Expression> arg0, Expression> arg1) + { + return new BooleanHigherOrderTwoBagFunction.Call(NAME_ALL_OF_ANY, subFunc, arg0, arg1) + { + + @Override + protected BooleanValue evaluate(Bag bag0, Bag bag1, EvaluationContext context) throws IndeterminateEvaluationException + { + final Deque subFuncArgStack = new ArrayDeque<>(2); + for (final AttributeValue bag0Val : bag0) + { + subFuncArgStack.add(bag0Val); + boolean isAnyTrue = false; + for (final AttributeValue bag1Val : bag1) + { + subFuncArgStack.add(bag1Val); + final AttributeValue[] subFuncArgValues = subFuncArgStack.toArray(new AttributeValue[2]); + final BooleanValue subResult; + try + { + subResult = subFuncCall.evaluate(context, subFuncArgValues); + } catch (IndeterminateEvaluationException e) + { + throw new IndeterminateEvaluationException(subFunctionCallErrorMessagePrefix + subFuncArgStack, StatusHelper.STATUS_PROCESSING_ERROR); + } + + subFuncArgStack.removeLast(); + if (subResult.getUnderlyingValue()) + { + isAnyTrue = true; + break; + } + } + + if (!isAnyTrue) + { + return BooleanValue.FALSE; + } + + subFuncArgStack.removeLast(); + } + + return BooleanValue.TRUE; + } + + }; + } + } + + /** + * any-of-all function + * + */ + private static final class AnyOfAll extends BooleanHigherOrderTwoBagFunction + { + + private final String subFunctionCallErrorMessagePrefix; + + /** + * Default constructor + */ + private AnyOfAll() + { + super(NAME_ANY_OF_ALL); + this.subFunctionCallErrorMessagePrefix = "Function " + NAME_ANY_OF_ALL + ": Error evaluating sub-function with arguments (evaluated to): "; + } + + @Override + protected BooleanHigherOrderTwoBagFunction.Call newFunctionCall(FirstOrderFunction subFunc, Expression> arg0, Expression> arg1) + { + return new BooleanHigherOrderTwoBagFunction.Call(NAME_ANY_OF_ALL, subFunc, arg0, arg1) + { + + @Override + protected BooleanValue evaluate(Bag bag0, Bag bag1, EvaluationContext context) throws IndeterminateEvaluationException + { + final Deque subFuncArgStack = new ArrayDeque<>(2); + + // same as all-of-any but in reverse order of bag0 and bag1 + for (final AttributeValue bag1Val : bag1) + { + boolean isAnyTrue = false; + for (final AttributeValue bag0Val : bag0) + { + subFuncArgStack.add(bag0Val); + subFuncArgStack.add(bag1Val); + final AttributeValue[] subFuncArgValues = subFuncArgStack.toArray(new AttributeValue[2]); + final BooleanValue subResult; + try + { + subResult = subFuncCall.evaluate(context, subFuncArgValues); + } catch (IndeterminateEvaluationException e) + { + throw new IndeterminateEvaluationException(subFunctionCallErrorMessagePrefix + subFuncArgStack, StatusHelper.STATUS_PROCESSING_ERROR); + } + + subFuncArgStack.removeLast();// remove bag1val + subFuncArgStack.removeLast();// remove bag0val + if (subResult.getUnderlyingValue()) + { + isAnyTrue = true; + break; + } + } + + if (!isAnyTrue) + { + return BooleanValue.FALSE; + } + } + + return BooleanValue.TRUE; + } + }; + } + } + + /** + * any-of-all function + * + */ + private static final class AllOfAll extends BooleanHigherOrderTwoBagFunction + { + + private final String subFunctionCallErrorMessagePrefix; + + /** + * Default constructor + */ + private AllOfAll() + { + super(NAME_ALL_OF_ALL); + this.subFunctionCallErrorMessagePrefix = "Function " + NAME_ALL_OF_ALL + ": Error evaluating sub-function with arguments (evaluated to): "; + + } + + @Override + protected BooleanHigherOrderTwoBagFunction.Call newFunctionCall(FirstOrderFunction subFunc, Expression> arg0, Expression> arg1) + { + return new BooleanHigherOrderTwoBagFunction.Call(NAME_ALL_OF_ALL, subFunc, arg0, arg1) + { + + @Override + protected BooleanValue evaluate(Bag bag0, Bag bag1, EvaluationContext context) throws IndeterminateEvaluationException + { + final Deque subFuncArgStack = new ArrayDeque<>(2); + for (final AttributeValue bag0Val : bag0) + { + subFuncArgStack.add(bag0Val); + boolean areAllTrue = true; + for (final AttributeValue bag1Val : bag1) + { + subFuncArgStack.add(bag1Val); + final AttributeValue[] subFuncArgValues = subFuncArgStack.toArray(new AttributeValue[2]); + final BooleanValue subResult; + try + { + subResult = subFuncCall.evaluate(context, subFuncArgValues); + } catch (IndeterminateEvaluationException e) + { + throw new IndeterminateEvaluationException(subFunctionCallErrorMessagePrefix + subFuncArgStack, StatusHelper.STATUS_PROCESSING_ERROR); + } + + subFuncArgStack.removeLast(); + if (!subResult.getUnderlyingValue()) + { + areAllTrue = false; + break; + } + } + + if (!areAllTrue) + { + return BooleanValue.FALSE; + } + + subFuncArgStack.removeLast(); + } + + return BooleanValue.TRUE; + } + }; + } + } + + private HigherOrderBagFunctionSet() + { + + } + + /** + * Function cluster + */ + public static final FunctionSet INSTANCE = new BaseFunctionSet(FunctionSet.DEFAULT_ID_NAMESPACE + "higher-order-bag", + // + new BooleanOneBagOnlyFunction(NAME_ANY_OF, new AnyOfCallFactory()),// + new BooleanOneBagOnlyFunction(NAME_ALL_OF, new AllOfCallFactory()),// + new AnyOfAny(), new AllOfAny(), new AnyOfAll(), new AllOfAll()); + +} diff --git a/src/main/java/org/ow2/authzforce/core/pdp/impl/func/LogicalAndFunction.java b/src/main/java/org/ow2/authzforce/core/pdp/impl/func/LogicalAndFunction.java index af636c2e..04d2e09c 100644 --- a/src/main/java/org/ow2/authzforce/core/pdp/impl/func/LogicalAndFunction.java +++ b/src/main/java/org/ow2/authzforce/core/pdp/impl/func/LogicalAndFunction.java @@ -1,179 +1,182 @@ -/** - * Copyright (C) 2011-2015 Thales Services SAS. - * - * This file is part of AuthZForce. - * - * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software - * Foundation, either version 3 of the License, or (at your option) any later version. - * - * AuthZForce 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . - */ -package org.ow2.authzforce.core.pdp.impl.func; - -import java.util.Arrays; -import java.util.List; - -import org.ow2.authzforce.core.pdp.api.AttributeValue; -import org.ow2.authzforce.core.pdp.api.Datatype; -import org.ow2.authzforce.core.pdp.api.EvaluationContext; -import org.ow2.authzforce.core.pdp.api.Expression; -import org.ow2.authzforce.core.pdp.api.Expressions; -import org.ow2.authzforce.core.pdp.api.FirstOrderFunction; -import org.ow2.authzforce.core.pdp.api.FirstOrderFunctionCall; -import org.ow2.authzforce.core.pdp.api.FunctionSignature; -import org.ow2.authzforce.core.pdp.api.IndeterminateEvaluationException; -import org.ow2.authzforce.core.pdp.api.StatusHelper; -import org.ow2.authzforce.core.pdp.impl.value.BooleanValue; -import org.ow2.authzforce.core.pdp.impl.value.DatatypeConstants; - -/** - * A class that implements the logical function "and". - *

- * From XACML core specification of function 'urn:oasis:names:tc:xacml:1.0:function:and': This function SHALL return "True" if it has no arguments and SHALL - * return "False" if one of its arguments evaluates to "False". The order of evaluation SHALL be from first argument to last. The evaluation SHALL stop with a - * result of "False" if any argument evaluates to "False", leaving the rest of the arguments unevaluated. - * - */ -public final class LogicalAndFunction extends FirstOrderFunction.SingleParameterTyped -{ - /** - * XACML standard identifier for the "and" logical function - */ - public static final String NAME_AND = XACML_NS_1_0 + "and"; - - /** - * Singleton instance of "and" logical function - */ - public static final LogicalAndFunction INSTANCE = new LogicalAndFunction(); - - private static final class CallFactory - { - - /** - * TODO: optimize this function call by checking the following: - *

    - *
  1. If any argument expression is constant BooleanAttributeValue False, return always False.
  2. - *
  3. Else If all argument expressions are constant BooleanAttributeValue True, return always True.
  4. - *
  5. - * Else If any argument expression is constant BooleanAttributeValue True, remove it from the arguments, as it has no effect on the final result. - * Indeed, and function is commutative and and(true, x, y...) = and(x, y...).
  6. - *
- * The first two optimizations can be achieved by pre-evaluating the function call with context = null and check the result if no - * IndeterminateEvaluationException is thrown. - */ - private static final class Call extends FirstOrderFunctionCall - { - private final List> checkedArgExpressions; - - private Call(FunctionSignature functionSig, List> argExpressions, Datatype[] remainingArgTypes) - throws IllegalArgumentException - { - super(functionSig, argExpressions, remainingArgTypes); - this.checkedArgExpressions = argExpressions; - } - - @Override - public BooleanValue evaluate(EvaluationContext context, AttributeValue... remainingArgs) throws IndeterminateEvaluationException - { - IndeterminateEvaluationException indeterminateException = null; - int argIndex = 0; - for (final Expression arg : checkedArgExpressions) - { - // Evaluate the argument - final BooleanValue attrVal; - try - { - attrVal = Expressions.eval(arg, context, DatatypeConstants.BOOLEAN.TYPE); - if (!attrVal.getUnderlyingValue()) - { - return BooleanValue.FALSE; - } - } catch (IndeterminateEvaluationException e) - { - // keep the indeterminate error to throw later if there was not any FALSE in - // remaining args - indeterminateException = new IndeterminateEvaluationException(INDETERMINATE_ARG_MESSAGE_PREFIX + argIndex, - StatusHelper.STATUS_PROCESSING_ERROR, e); - } - - argIndex++; - } - - // do the same with remaining arg values - if (remainingArgs != null) - { - - for (final AttributeValue arg : remainingArgs) - { - // Evaluate the argument - final BooleanValue attrVal; - try - { - attrVal = BooleanValue.class.cast(arg); - } catch (ClassCastException e) - { - throw new IndeterminateEvaluationException(INVALID_ARG_TYPE_MESSAGE_PREFIX + argIndex + ": " + arg.getClass().getName(), - StatusHelper.STATUS_PROCESSING_ERROR, e); - } - - if (!attrVal.getUnderlyingValue()) - { - return BooleanValue.FALSE; - } - - argIndex++; - } - } - - if (indeterminateException != null) - { - // there was at least one indeterminate arg that could have been TRUE or FALSE -> - // indeterminate result - throw indeterminateException; - } - - return BooleanValue.TRUE; - } - } - - private static final String INVALID_ARG_TYPE_MESSAGE_PREFIX = "Function " + NAME_AND + ": Invalid type (expected = " + DatatypeConstants.BOOLEAN.TYPE - + ") of arg#"; - private static final String INDETERMINATE_ARG_MESSAGE_PREFIX = "Function " + NAME_AND + ": Indeterminate arg #"; - - private final FunctionSignature.SingleParameterTyped funcSig; - - private CallFactory(FunctionSignature.SingleParameterTyped functionSignature) - { - this.funcSig = functionSignature; - } - - protected FirstOrderFunctionCall getInstance(final List> argExpressions, Datatype[] remainingArgTypes) - { - return new Call(funcSig, argExpressions, remainingArgTypes); - } - - } - - private final CallFactory funcCallFactory; - - private LogicalAndFunction() - { - super(NAME_AND, DatatypeConstants.BOOLEAN.TYPE, true, Arrays.asList(DatatypeConstants.BOOLEAN.TYPE)); - this.funcCallFactory = new CallFactory(this.functionSignature); - } - - /* - * (non-Javadoc) - * - * @see com.thalesgroup.authzforce.core.func.FirstOrderFunction#getFunctionCall(java.util.List, com.thalesgroup.authzforce.core.eval.DatatypeDef[]) - */ - @Override - public FirstOrderFunctionCall newCall(final List> argExpressions, Datatype... remainingArgTypes) - throws IllegalArgumentException - { - return this.funcCallFactory.getInstance(argExpressions, remainingArgTypes); - } -} +/** + * Copyright (C) 2011-2015 Thales Services SAS. + * + * This file is part of AuthZForce. + * + * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * AuthZForce 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . + */ +package org.ow2.authzforce.core.pdp.impl.func; + +import java.util.Arrays; +import java.util.List; + +import org.ow2.authzforce.core.pdp.api.AttributeValue; +import org.ow2.authzforce.core.pdp.api.Datatype; +import org.ow2.authzforce.core.pdp.api.EvaluationContext; +import org.ow2.authzforce.core.pdp.api.Expression; +import org.ow2.authzforce.core.pdp.api.Expressions; +import org.ow2.authzforce.core.pdp.api.FirstOrderFunction; +import org.ow2.authzforce.core.pdp.api.FirstOrderFunctionCall; +import org.ow2.authzforce.core.pdp.api.FunctionSignature; +import org.ow2.authzforce.core.pdp.api.IndeterminateEvaluationException; +import org.ow2.authzforce.core.pdp.api.StatusHelper; +import org.ow2.authzforce.core.pdp.impl.value.BooleanValue; +import org.ow2.authzforce.core.pdp.impl.value.DatatypeConstants; + +/** + * A class that implements the logical function "and". + *

+ * From XACML core specification of function 'urn:oasis:names:tc:xacml:1.0:function:and': This function SHALL return "True" if it has no arguments and SHALL + * return "False" if one of its arguments evaluates to "False". The order of evaluation SHALL be from first argument to last. The evaluation SHALL stop with a + * result of "False" if any argument evaluates to "False", leaving the rest of the arguments unevaluated. + * + * @author cdangerv + * @version $Id: $ + */ +public final class LogicalAndFunction extends FirstOrderFunction.SingleParameterTyped +{ + /** + * XACML standard identifier for the "and" logical function + */ + public static final String NAME_AND = XACML_NS_1_0 + "and"; + + /** + * Singleton instance of "and" logical function + */ + public static final LogicalAndFunction INSTANCE = new LogicalAndFunction(); + + private static final class CallFactory + { + + /** + * TODO: optimize this function call by checking the following: + *

    + *
  1. If any argument expression is constant BooleanAttributeValue False, return always False.
  2. + *
  3. Else If all argument expressions are constant BooleanAttributeValue True, return always True.
  4. + *
  5. + * Else If any argument expression is constant BooleanAttributeValue True, remove it from the arguments, as it has no effect on the final result. + * Indeed, and function is commutative and and(true, x, y...) = and(x, y...).
  6. + *
+ * The first two optimizations can be achieved by pre-evaluating the function call with context = null and check the result if no + * IndeterminateEvaluationException is thrown. + */ + private static final class Call extends FirstOrderFunctionCall + { + private final List> checkedArgExpressions; + + private Call(FunctionSignature functionSig, List> argExpressions, Datatype[] remainingArgTypes) + throws IllegalArgumentException + { + super(functionSig, argExpressions, remainingArgTypes); + this.checkedArgExpressions = argExpressions; + } + + @Override + public BooleanValue evaluate(EvaluationContext context, AttributeValue... remainingArgs) throws IndeterminateEvaluationException + { + IndeterminateEvaluationException indeterminateException = null; + int argIndex = 0; + for (final Expression arg : checkedArgExpressions) + { + // Evaluate the argument + final BooleanValue attrVal; + try + { + attrVal = Expressions.eval(arg, context, DatatypeConstants.BOOLEAN.TYPE); + if (!attrVal.getUnderlyingValue()) + { + return BooleanValue.FALSE; + } + } catch (IndeterminateEvaluationException e) + { + // keep the indeterminate error to throw later if there was not any FALSE in + // remaining args + indeterminateException = new IndeterminateEvaluationException(INDETERMINATE_ARG_MESSAGE_PREFIX + argIndex, + StatusHelper.STATUS_PROCESSING_ERROR, e); + } + + argIndex++; + } + + // do the same with remaining arg values + if (remainingArgs != null) + { + + for (final AttributeValue arg : remainingArgs) + { + // Evaluate the argument + final BooleanValue attrVal; + try + { + attrVal = BooleanValue.class.cast(arg); + } catch (ClassCastException e) + { + throw new IndeterminateEvaluationException(INVALID_ARG_TYPE_MESSAGE_PREFIX + argIndex + ": " + arg.getClass().getName(), + StatusHelper.STATUS_PROCESSING_ERROR, e); + } + + if (!attrVal.getUnderlyingValue()) + { + return BooleanValue.FALSE; + } + + argIndex++; + } + } + + if (indeterminateException != null) + { + // there was at least one indeterminate arg that could have been TRUE or FALSE -> + // indeterminate result + throw indeterminateException; + } + + return BooleanValue.TRUE; + } + } + + private static final String INVALID_ARG_TYPE_MESSAGE_PREFIX = "Function " + NAME_AND + ": Invalid type (expected = " + DatatypeConstants.BOOLEAN.TYPE + + ") of arg#"; + private static final String INDETERMINATE_ARG_MESSAGE_PREFIX = "Function " + NAME_AND + ": Indeterminate arg #"; + + private final FunctionSignature.SingleParameterTyped funcSig; + + private CallFactory(FunctionSignature.SingleParameterTyped functionSignature) + { + this.funcSig = functionSignature; + } + + protected FirstOrderFunctionCall getInstance(final List> argExpressions, Datatype[] remainingArgTypes) + { + return new Call(funcSig, argExpressions, remainingArgTypes); + } + + } + + private final CallFactory funcCallFactory; + + private LogicalAndFunction() + { + super(NAME_AND, DatatypeConstants.BOOLEAN.TYPE, true, Arrays.asList(DatatypeConstants.BOOLEAN.TYPE)); + this.funcCallFactory = new CallFactory(this.functionSignature); + } + + /* + * (non-Javadoc) + * + * @see com.thalesgroup.authzforce.core.func.FirstOrderFunction#getFunctionCall(java.util.List, com.thalesgroup.authzforce.core.eval.DatatypeDef[]) + */ + /** {@inheritDoc} */ + @Override + public FirstOrderFunctionCall newCall(final List> argExpressions, Datatype... remainingArgTypes) + throws IllegalArgumentException + { + return this.funcCallFactory.getInstance(argExpressions, remainingArgTypes); + } +} diff --git a/src/main/java/org/ow2/authzforce/core/pdp/impl/func/LogicalNOfFunction.java b/src/main/java/org/ow2/authzforce/core/pdp/impl/func/LogicalNOfFunction.java index a000864a..95e5e28f 100644 --- a/src/main/java/org/ow2/authzforce/core/pdp/impl/func/LogicalNOfFunction.java +++ b/src/main/java/org/ow2/authzforce/core/pdp/impl/func/LogicalNOfFunction.java @@ -1,273 +1,277 @@ -/** - * Copyright (C) 2011-2015 Thales Services SAS. - * - * This file is part of AuthZForce. - * - * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software - * Foundation, either version 3 of the License, or (at your option) any later version. - * - * AuthZForce 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . - */ -package org.ow2.authzforce.core.pdp.impl.func; - -import java.util.Arrays; -import java.util.Iterator; -import java.util.List; - -import org.ow2.authzforce.core.pdp.api.AttributeValue; -import org.ow2.authzforce.core.pdp.api.Datatype; -import org.ow2.authzforce.core.pdp.api.EvaluationContext; -import org.ow2.authzforce.core.pdp.api.Expression; -import org.ow2.authzforce.core.pdp.api.Expressions; -import org.ow2.authzforce.core.pdp.api.FirstOrderFunction; -import org.ow2.authzforce.core.pdp.api.FirstOrderFunctionCall; -import org.ow2.authzforce.core.pdp.api.FunctionSignature; -import org.ow2.authzforce.core.pdp.api.IndeterminateEvaluationException; -import org.ow2.authzforce.core.pdp.api.StatusHelper; -import org.ow2.authzforce.core.pdp.impl.value.BooleanValue; -import org.ow2.authzforce.core.pdp.impl.value.DatatypeConstants; -import org.ow2.authzforce.core.pdp.impl.value.IntegerValue; - -/** - * A class that implements the n-of function. From the XACML spec (urn:oasis:names:tc:xacml:1.0:function:n-of): the first argument to this function SHALL be of - * data-type http://www.w3.org/2001/XMLSchema#integer. The remaining arguments SHALL be of data-type http://www.w3.org/2001/XMLSchema#boolean. The first - * argument specifies the minimum number of the remaining arguments that MUST evaluate to "True" for the expression to be considered "True". If the first - * argument is 0, the result SHALL be "True". If the number of arguments after the first one is less than the value of the first argument, then the expression - * SHALL result in "Indeterminate". The order of evaluation SHALL be: first evaluate the integer value, and then evaluate each subsequent argument. The - * evaluation SHALL stop and return "True" if the specified number of arguments evaluate to "True". The evaluation of arguments SHALL stop if it is determined - * that evaluating the remaining arguments will not satisfy the requirement. - *

- * This function evaluates the arguments one at a time, starting with the first one. As soon as the result of the function can be determined, evaluation stops - * and that result is returned. During this process, if any argument evaluates to indeterminate, an indeterminate result is returned. - */ -public final class LogicalNOfFunction extends FirstOrderFunction.MultiParameterTyped -{ - - private static final class Call extends FirstOrderFunctionCall - { - private static final String INVALID_ARG_TYPE_MESSAGE_PREFIX = "Function " + NAME_N_OF + ": Invalid type (expected = " + DatatypeConstants.BOOLEAN.TYPE - + ") of arg#"; - private static final String INDETERMINATE_ARG_MSG_PREFIX = "Function " + NAME_N_OF + ": Indeterminate arg #"; - private static final String INVALID_ARG0_MSG_PREFIX = "Function " + NAME_N_OF - + ": Invalid arg #0 (number of required Trues): expected: (integer) >= 0; actual: "; - private static final String INVALID_ARGS_MSG_PREFIX = "Function " + NAME_N_OF - + ": Invalid arguments to n-of function: value of arg #0 (number of required Trues) > number of boolean args: "; - private static final IndeterminateEvaluationException INDETERMINATE_ARG_EXCEPTION = new IndeterminateEvaluationException("Function " + NAME_N_OF - + ": evaluation failed because of indeterminate arg", StatusHelper.STATUS_PROCESSING_ERROR); - private final List> checkedArgExpressions; - - private Call(FunctionSignature functionSig, List> checkedArgExpressions, Datatype[] remainingArgTypes) - throws IllegalArgumentException - { - super(functionSig, checkedArgExpressions, remainingArgTypes); - this.checkedArgExpressions = checkedArgExpressions; - } - - @Override - public BooleanValue evaluate(EvaluationContext context, AttributeValue... checkedRemainingArgs) throws IndeterminateEvaluationException - { - /* - * Arg datatypes and number is already checked in superclass but we need to do further checks specific to this function such as the first argument - * which must be a positive integer - */ - - /** - * TODO: optimize this function call by checking the following: - *

    - *
  1. If eval(null, checkedArgExpressions, null) returns a result (no IndeterminateEvaluationException thrown), then it will always return this - * result
  2. - *
  3. - * Else If any argument expression is constant BooleanAttributeValue False, remove it from the arguments and always decrement the first integer - * argument by one, as it has no effect on the final result. Indeed, n-of function is commutative except for the first argument, and n-of(N, false, - * x, y...) = n-of(N, x, y...).
  4. - *
- */ - - // Evaluate the arguments one by one. As soon as we can return - // a result, do so. Return Indeterminate if any argument - // evaluated is indeterminate. - final Iterator> argExpsIterator = checkedArgExpressions.iterator(); - - // Evaluate the first argument - final Expression input0 = argExpsIterator.next(); - final IntegerValue intAttrVal; - try - { - intAttrVal = Expressions.eval(input0, context, DatatypeConstants.INTEGER.TYPE); - } catch (IndeterminateEvaluationException e) - { - throw new IndeterminateEvaluationException(INDETERMINATE_ARG_MSG_PREFIX + 0, StatusHelper.STATUS_PROCESSING_ERROR, e); - } - - // intAttrVal is 'n' (number of Trues to reach) - - // We downsize the BigInteger value to int right away, because anyway inputs.size() is an - // int, so we cannot do better and don't need to. - int nOfRequiredTrues = intAttrVal.getUnderlyingValue().intValue(); - - // If the number of trues needed is less than zero, report an error. - if (nOfRequiredTrues < 0) - { - throw new IndeterminateEvaluationException(INVALID_ARG0_MSG_PREFIX + nOfRequiredTrues, StatusHelper.STATUS_PROCESSING_ERROR); - } - - // If the number of trues needed is zero, return true. - if (nOfRequiredTrues == 0) - { - return BooleanValue.TRUE; - } - - // else nOfRequiredTrues > 0 - // make sure it's possible to find n true values in the remaining arguments - int nOfRemainingArgs = checkedArgExpressions.size() + (checkedRemainingArgs == null ? 0 : checkedRemainingArgs.length) - 1; - if (nOfRequiredTrues > nOfRemainingArgs) - { - throw new IndeterminateEvaluationException(INVALID_ARGS_MSG_PREFIX + nOfRequiredTrues + " > " + nOfRemainingArgs, - StatusHelper.STATUS_PROCESSING_ERROR); - } - - IndeterminateEvaluationException lastIndeterminateException = null; - int nOfIndeterminateArgs = 0; - // loop through the inputs, trying to find at least n trues - int argIndex = 1; - while (argExpsIterator.hasNext()) - { - // evaluate the next argument - final Expression input = argExpsIterator.next(); - final BooleanValue attrVal; - try - { - attrVal = Expressions.eval(input, context, DatatypeConstants.BOOLEAN.TYPE); - if (attrVal.getUnderlyingValue()) - { - // we're one closer to our goal...see if we met it - nOfRequiredTrues--; - if (nOfRequiredTrues == 0) - { - return BooleanValue.TRUE; - } - - // nOfRequiredTrues != 0 - if (nOfRequiredTrues == nOfIndeterminateArgs) - { - // nOfIndeterminateArgs != 0 as well - // if all indeterminate args have been TRUE, result would be TRUE -> - // indeterminate result - if (lastIndeterminateException == null) - { - // this should not happen in theory as lastIndeterminateException != - // null when nOfIndeterminateArgs != 0 - throw INDETERMINATE_ARG_EXCEPTION; - } - - throw lastIndeterminateException; - } - - final int nOfPossibleTrues = nOfRemainingArgs + nOfIndeterminateArgs; - if (nOfRequiredTrues > nOfPossibleTrues) - { - // check whether we have enough remaining args - return BooleanValue.FALSE; - } - } - } catch (IndeterminateEvaluationException e) - { - // keep the indeterminate arg error to throw later, in case there was not enough - // TRUEs in the remaining args - lastIndeterminateException = new IndeterminateEvaluationException(INDETERMINATE_ARG_MSG_PREFIX + argIndex, - StatusHelper.STATUS_PROCESSING_ERROR, e); - nOfIndeterminateArgs++; - } - - nOfRemainingArgs--; - argIndex++; - } - - // do the same loop with remaining arg values - if (checkedRemainingArgs != null) - { - for (final AttributeValue arg : checkedRemainingArgs) - { - final BooleanValue attrVal; - try - { - attrVal = BooleanValue.class.cast(arg); - } catch (ClassCastException e) - { - throw new IndeterminateEvaluationException(INVALID_ARG_TYPE_MESSAGE_PREFIX + argIndex + ": " + arg.getClass().getName(), - StatusHelper.STATUS_PROCESSING_ERROR, e); - } - - if (attrVal.getUnderlyingValue()) - { - // we're one closer to our goal...see if we met it - nOfRequiredTrues--; - if (nOfRequiredTrues == 0) - { - return BooleanValue.TRUE; - } - - if (nOfRequiredTrues > nOfRemainingArgs) - { - // check whether we have enough remaining args - return BooleanValue.FALSE; - } - } - - nOfRemainingArgs--; - argIndex++; - } - } - - // if we got here then we didn't meet our quota - // nOfRequiredTrues != 0 - if (nOfRequiredTrues == nOfIndeterminateArgs) - { - // nOfIndeterminateArgs != 0 as well, so lastIndeterminateException != null - // if all indeterminate args have been TRUE, result would be TRUE -> indeterminate - // result - if (lastIndeterminateException == null) - { - // this should not happen in theory as lastIndeterminateException != - // null when nOfIndeterminateArgs != 0 - throw INDETERMINATE_ARG_EXCEPTION; - } - - throw lastIndeterminateException; - } - - return BooleanValue.FALSE; - } - } - - /** - * Standard identifier for the n-of function. - */ - public static final String NAME_N_OF = XACML_NS_1_0 + "n-of"; - - /** - * Singleton instance of "n-of" logical function - */ - public static final LogicalNOfFunction INSTANCE = new LogicalNOfFunction(); - - private LogicalNOfFunction() - { - super(NAME_N_OF, DatatypeConstants.BOOLEAN.TYPE, true, Arrays.asList(DatatypeConstants.INTEGER.TYPE, DatatypeConstants.BOOLEAN.TYPE)); - } - - /* - * (non-Javadoc) - * - * @see com.thalesgroup.authzforce.core.func.FirstOrderFunction#getFunctionCall(java.util.List, com.thalesgroup.authzforce.core.eval.DatatypeDef[]) - */ - @Override - public FirstOrderFunctionCall newCall(final List> checkedArgExpressions, Datatype... remainingArgTypes) - throws IllegalArgumentException - { - return new Call(functionSignature, checkedArgExpressions, remainingArgTypes); - } - -} +/** + * Copyright (C) 2011-2015 Thales Services SAS. + * + * This file is part of AuthZForce. + * + * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * AuthZForce 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . + */ +package org.ow2.authzforce.core.pdp.impl.func; + +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; + +import org.ow2.authzforce.core.pdp.api.AttributeValue; +import org.ow2.authzforce.core.pdp.api.Datatype; +import org.ow2.authzforce.core.pdp.api.EvaluationContext; +import org.ow2.authzforce.core.pdp.api.Expression; +import org.ow2.authzforce.core.pdp.api.Expressions; +import org.ow2.authzforce.core.pdp.api.FirstOrderFunction; +import org.ow2.authzforce.core.pdp.api.FirstOrderFunctionCall; +import org.ow2.authzforce.core.pdp.api.FunctionSignature; +import org.ow2.authzforce.core.pdp.api.IndeterminateEvaluationException; +import org.ow2.authzforce.core.pdp.api.StatusHelper; +import org.ow2.authzforce.core.pdp.impl.value.BooleanValue; +import org.ow2.authzforce.core.pdp.impl.value.DatatypeConstants; +import org.ow2.authzforce.core.pdp.impl.value.IntegerValue; + +/** + * A class that implements the n-of function. From the XACML spec (urn:oasis:names:tc:xacml:1.0:function:n-of): the first argument to this function SHALL be of + * data-type http://www.w3.org/2001/XMLSchema#integer. The remaining arguments SHALL be of data-type http://www.w3.org/2001/XMLSchema#boolean. The first + * argument specifies the minimum number of the remaining arguments that MUST evaluate to "True" for the expression to be considered "True". If the first + * argument is 0, the result SHALL be "True". If the number of arguments after the first one is less than the value of the first argument, then the expression + * SHALL result in "Indeterminate". The order of evaluation SHALL be: first evaluate the integer value, and then evaluate each subsequent argument. The + * evaluation SHALL stop and return "True" if the specified number of arguments evaluate to "True". The evaluation of arguments SHALL stop if it is determined + * that evaluating the remaining arguments will not satisfy the requirement. + *

+ * This function evaluates the arguments one at a time, starting with the first one. As soon as the result of the function can be determined, evaluation stops + * and that result is returned. During this process, if any argument evaluates to indeterminate, an indeterminate result is returned. + * + * @author cdangerv + * @version $Id: $ + */ +public final class LogicalNOfFunction extends FirstOrderFunction.MultiParameterTyped +{ + + private static final class Call extends FirstOrderFunctionCall + { + private static final String INVALID_ARG_TYPE_MESSAGE_PREFIX = "Function " + NAME_N_OF + ": Invalid type (expected = " + DatatypeConstants.BOOLEAN.TYPE + + ") of arg#"; + private static final String INDETERMINATE_ARG_MSG_PREFIX = "Function " + NAME_N_OF + ": Indeterminate arg #"; + private static final String INVALID_ARG0_MSG_PREFIX = "Function " + NAME_N_OF + + ": Invalid arg #0 (number of required Trues): expected: (integer) >= 0; actual: "; + private static final String INVALID_ARGS_MSG_PREFIX = "Function " + NAME_N_OF + + ": Invalid arguments to n-of function: value of arg #0 (number of required Trues) > number of boolean args: "; + private static final IndeterminateEvaluationException INDETERMINATE_ARG_EXCEPTION = new IndeterminateEvaluationException("Function " + NAME_N_OF + + ": evaluation failed because of indeterminate arg", StatusHelper.STATUS_PROCESSING_ERROR); + private final List> checkedArgExpressions; + + private Call(FunctionSignature functionSig, List> checkedArgExpressions, Datatype[] remainingArgTypes) + throws IllegalArgumentException + { + super(functionSig, checkedArgExpressions, remainingArgTypes); + this.checkedArgExpressions = checkedArgExpressions; + } + + @Override + public BooleanValue evaluate(EvaluationContext context, AttributeValue... checkedRemainingArgs) throws IndeterminateEvaluationException + { + /* + * Arg datatypes and number is already checked in superclass but we need to do further checks specific to this function such as the first argument + * which must be a positive integer + */ + + /** + * TODO: optimize this function call by checking the following: + *

    + *
  1. If eval(null, checkedArgExpressions, null) returns a result (no IndeterminateEvaluationException thrown), then it will always return this + * result
  2. + *
  3. + * Else If any argument expression is constant BooleanAttributeValue False, remove it from the arguments and always decrement the first integer + * argument by one, as it has no effect on the final result. Indeed, n-of function is commutative except for the first argument, and n-of(N, false, + * x, y...) = n-of(N, x, y...).
  4. + *
+ */ + + // Evaluate the arguments one by one. As soon as we can return + // a result, do so. Return Indeterminate if any argument + // evaluated is indeterminate. + final Iterator> argExpsIterator = checkedArgExpressions.iterator(); + + // Evaluate the first argument + final Expression input0 = argExpsIterator.next(); + final IntegerValue intAttrVal; + try + { + intAttrVal = Expressions.eval(input0, context, DatatypeConstants.INTEGER.TYPE); + } catch (IndeterminateEvaluationException e) + { + throw new IndeterminateEvaluationException(INDETERMINATE_ARG_MSG_PREFIX + 0, StatusHelper.STATUS_PROCESSING_ERROR, e); + } + + // intAttrVal is 'n' (number of Trues to reach) + + // We downsize the BigInteger value to int right away, because anyway inputs.size() is an + // int, so we cannot do better and don't need to. + int nOfRequiredTrues = intAttrVal.getUnderlyingValue().intValue(); + + // If the number of trues needed is less than zero, report an error. + if (nOfRequiredTrues < 0) + { + throw new IndeterminateEvaluationException(INVALID_ARG0_MSG_PREFIX + nOfRequiredTrues, StatusHelper.STATUS_PROCESSING_ERROR); + } + + // If the number of trues needed is zero, return true. + if (nOfRequiredTrues == 0) + { + return BooleanValue.TRUE; + } + + // else nOfRequiredTrues > 0 + // make sure it's possible to find n true values in the remaining arguments + int nOfRemainingArgs = checkedArgExpressions.size() + (checkedRemainingArgs == null ? 0 : checkedRemainingArgs.length) - 1; + if (nOfRequiredTrues > nOfRemainingArgs) + { + throw new IndeterminateEvaluationException(INVALID_ARGS_MSG_PREFIX + nOfRequiredTrues + " > " + nOfRemainingArgs, + StatusHelper.STATUS_PROCESSING_ERROR); + } + + IndeterminateEvaluationException lastIndeterminateException = null; + int nOfIndeterminateArgs = 0; + // loop through the inputs, trying to find at least n trues + int argIndex = 1; + while (argExpsIterator.hasNext()) + { + // evaluate the next argument + final Expression input = argExpsIterator.next(); + final BooleanValue attrVal; + try + { + attrVal = Expressions.eval(input, context, DatatypeConstants.BOOLEAN.TYPE); + if (attrVal.getUnderlyingValue()) + { + // we're one closer to our goal...see if we met it + nOfRequiredTrues--; + if (nOfRequiredTrues == 0) + { + return BooleanValue.TRUE; + } + + // nOfRequiredTrues != 0 + if (nOfRequiredTrues == nOfIndeterminateArgs) + { + // nOfIndeterminateArgs != 0 as well + // if all indeterminate args have been TRUE, result would be TRUE -> + // indeterminate result + if (lastIndeterminateException == null) + { + // this should not happen in theory as lastIndeterminateException != + // null when nOfIndeterminateArgs != 0 + throw INDETERMINATE_ARG_EXCEPTION; + } + + throw lastIndeterminateException; + } + + final int nOfPossibleTrues = nOfRemainingArgs + nOfIndeterminateArgs; + if (nOfRequiredTrues > nOfPossibleTrues) + { + // check whether we have enough remaining args + return BooleanValue.FALSE; + } + } + } catch (IndeterminateEvaluationException e) + { + // keep the indeterminate arg error to throw later, in case there was not enough + // TRUEs in the remaining args + lastIndeterminateException = new IndeterminateEvaluationException(INDETERMINATE_ARG_MSG_PREFIX + argIndex, + StatusHelper.STATUS_PROCESSING_ERROR, e); + nOfIndeterminateArgs++; + } + + nOfRemainingArgs--; + argIndex++; + } + + // do the same loop with remaining arg values + if (checkedRemainingArgs != null) + { + for (final AttributeValue arg : checkedRemainingArgs) + { + final BooleanValue attrVal; + try + { + attrVal = BooleanValue.class.cast(arg); + } catch (ClassCastException e) + { + throw new IndeterminateEvaluationException(INVALID_ARG_TYPE_MESSAGE_PREFIX + argIndex + ": " + arg.getClass().getName(), + StatusHelper.STATUS_PROCESSING_ERROR, e); + } + + if (attrVal.getUnderlyingValue()) + { + // we're one closer to our goal...see if we met it + nOfRequiredTrues--; + if (nOfRequiredTrues == 0) + { + return BooleanValue.TRUE; + } + + if (nOfRequiredTrues > nOfRemainingArgs) + { + // check whether we have enough remaining args + return BooleanValue.FALSE; + } + } + + nOfRemainingArgs--; + argIndex++; + } + } + + // if we got here then we didn't meet our quota + // nOfRequiredTrues != 0 + if (nOfRequiredTrues == nOfIndeterminateArgs) + { + // nOfIndeterminateArgs != 0 as well, so lastIndeterminateException != null + // if all indeterminate args have been TRUE, result would be TRUE -> indeterminate + // result + if (lastIndeterminateException == null) + { + // this should not happen in theory as lastIndeterminateException != + // null when nOfIndeterminateArgs != 0 + throw INDETERMINATE_ARG_EXCEPTION; + } + + throw lastIndeterminateException; + } + + return BooleanValue.FALSE; + } + } + + /** + * Standard identifier for the n-of function. + */ + public static final String NAME_N_OF = XACML_NS_1_0 + "n-of"; + + /** + * Singleton instance of "n-of" logical function + */ + public static final LogicalNOfFunction INSTANCE = new LogicalNOfFunction(); + + private LogicalNOfFunction() + { + super(NAME_N_OF, DatatypeConstants.BOOLEAN.TYPE, true, Arrays.asList(DatatypeConstants.INTEGER.TYPE, DatatypeConstants.BOOLEAN.TYPE)); + } + + /* + * (non-Javadoc) + * + * @see com.thalesgroup.authzforce.core.func.FirstOrderFunction#getFunctionCall(java.util.List, com.thalesgroup.authzforce.core.eval.DatatypeDef[]) + */ + /** {@inheritDoc} */ + @Override + public FirstOrderFunctionCall newCall(final List> checkedArgExpressions, Datatype... remainingArgTypes) + throws IllegalArgumentException + { + return new Call(functionSignature, checkedArgExpressions, remainingArgTypes); + } + +} diff --git a/src/main/java/org/ow2/authzforce/core/pdp/impl/func/LogicalOrFunction.java b/src/main/java/org/ow2/authzforce/core/pdp/impl/func/LogicalOrFunction.java index 7761acd2..5606ef2b 100644 --- a/src/main/java/org/ow2/authzforce/core/pdp/impl/func/LogicalOrFunction.java +++ b/src/main/java/org/ow2/authzforce/core/pdp/impl/func/LogicalOrFunction.java @@ -1,160 +1,163 @@ -/** - * Copyright (C) 2011-2015 Thales Services SAS. - * - * This file is part of AuthZForce. - * - * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software - * Foundation, either version 3 of the License, or (at your option) any later version. - * - * AuthZForce 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . - */ -package org.ow2.authzforce.core.pdp.impl.func; - -import java.util.Arrays; -import java.util.List; - -import org.ow2.authzforce.core.pdp.api.AttributeValue; -import org.ow2.authzforce.core.pdp.api.Datatype; -import org.ow2.authzforce.core.pdp.api.EvaluationContext; -import org.ow2.authzforce.core.pdp.api.Expression; -import org.ow2.authzforce.core.pdp.api.Expressions; -import org.ow2.authzforce.core.pdp.api.FirstOrderFunction; -import org.ow2.authzforce.core.pdp.api.FirstOrderFunctionCall; -import org.ow2.authzforce.core.pdp.api.FunctionSignature; -import org.ow2.authzforce.core.pdp.api.IndeterminateEvaluationException; -import org.ow2.authzforce.core.pdp.api.StatusHelper; -import org.ow2.authzforce.core.pdp.impl.value.BooleanValue; -import org.ow2.authzforce.core.pdp.impl.value.DatatypeConstants; - -/** - * A class that implements the logical functions "or" and "and". - *

- * From XACML core specification of function 'urn:oasis:names:tc:xacml:1.0:function:or': This function SHALL return "False" if it has no arguments and SHALL - * return "True" if at least one of its arguments evaluates to "True". The order of evaluation SHALL be from first argument to last. The evaluation SHALL stop - * with a result of "True" if any argument evaluates to "True", leaving the rest of the arguments unevaluated. - * - */ -public final class LogicalOrFunction extends FirstOrderFunction.SingleParameterTyped -{ - private static final class Call extends FirstOrderFunctionCall - { - private static final String INDETERMINATE_ARG_MESSAGE_PREFIX = "Function " + NAME_OR + ": Indeterminate arg #"; - private static final String INVALID_ARG_TYPE_MESSAGE_PREFIX = "Function " + NAME_OR + ": Invalid type (expected = " + DatatypeConstants.BOOLEAN.TYPE - + ") of arg#"; - - private final List> checkedArgExpressions; - - private Call(FunctionSignature functionSig, List> argExpressions, Datatype[] remainingArgTypes) - throws IllegalArgumentException - { - super(functionSig, argExpressions, remainingArgTypes); - this.checkedArgExpressions = argExpressions; - } - - @Override - public BooleanValue evaluate(EvaluationContext context, AttributeValue... checkedRemainingArgs) throws IndeterminateEvaluationException - { - /** - * TODO: optimize this function call by checking the following: - *

    - *
  1. If any argument expression is constant BooleanAttributeValue True, return always true.
  2. - *
  3. Else If all argument expressions are constant BooleanAttributeValue False, return always false.
  4. - *
  5. - * Else If any argument expression is constant BooleanAttributeValue False, remove it from the arguments, as it has no effect on the final result. - * Indeed, or function is commutative and or(false, x, y...) = or(x, y...).
  6. - *
- * The first two optimizations can be achieved by pre-evaluating the function call with context = null and check the result if no - * IndeterminateEvaluationException is thrown. - */ - - IndeterminateEvaluationException indeterminateException = null; - int argIndex = 0; - for (final Expression arg : checkedArgExpressions) - { - // Evaluate the argument - final BooleanValue attrVal; - try - { - attrVal = Expressions.eval(arg, context, DatatypeConstants.BOOLEAN.TYPE); - if (attrVal.getUnderlyingValue()) - { - return BooleanValue.TRUE; - } - } catch (IndeterminateEvaluationException e) - { - // save the indeterminate to throw later only if there was not any TRUE in remaining - // args - indeterminateException = new IndeterminateEvaluationException(INDETERMINATE_ARG_MESSAGE_PREFIX + argIndex, - StatusHelper.STATUS_PROCESSING_ERROR, e); - } - - argIndex++; - } - - // do the same with remaining arg values - if (checkedRemainingArgs != null) - { - - for (final AttributeValue arg : checkedRemainingArgs) - { - // Evaluate the argument - final BooleanValue attrVal; - try - { - attrVal = BooleanValue.class.cast(arg); - } catch (ClassCastException e) - { - throw new IndeterminateEvaluationException(INVALID_ARG_TYPE_MESSAGE_PREFIX + argIndex + ": " + arg.getClass().getName(), - StatusHelper.STATUS_PROCESSING_ERROR, e); - } - - if (attrVal.getUnderlyingValue()) - { - return BooleanValue.TRUE; - } - - argIndex++; - } - } - - if (indeterminateException != null) - { - // there was at least one indeterminate arg that could have been TRUE or FALSE -> - // indeterminate result - throw indeterminateException; - } - - return BooleanValue.FALSE; - } - } - - /** - * XACML standard identifier for the "or" logical function - */ - public static final String NAME_OR = XACML_NS_1_0 + "or"; - - private LogicalOrFunction() - { - super(NAME_OR, DatatypeConstants.BOOLEAN.TYPE, true, Arrays.asList(DatatypeConstants.BOOLEAN.TYPE)); - } - - /** - * Singleton instance of "or" logical function - */ - public static final LogicalOrFunction INSTANCE = new LogicalOrFunction(); - - /* - * (non-Javadoc) - * - * @see com.thalesgroup.authzforce.core.func.FirstOrderFunction#getFunctionCall(java.util.List, com.thalesgroup.authzforce.core.eval.DatatypeDef[]) - */ - @Override - public FirstOrderFunctionCall newCall(final List> argExpressions, Datatype... remainingArgTypes) - { - return new Call(functionSignature, argExpressions, remainingArgTypes); - } - -} +/** + * Copyright (C) 2011-2015 Thales Services SAS. + * + * This file is part of AuthZForce. + * + * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * AuthZForce 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . + */ +package org.ow2.authzforce.core.pdp.impl.func; + +import java.util.Arrays; +import java.util.List; + +import org.ow2.authzforce.core.pdp.api.AttributeValue; +import org.ow2.authzforce.core.pdp.api.Datatype; +import org.ow2.authzforce.core.pdp.api.EvaluationContext; +import org.ow2.authzforce.core.pdp.api.Expression; +import org.ow2.authzforce.core.pdp.api.Expressions; +import org.ow2.authzforce.core.pdp.api.FirstOrderFunction; +import org.ow2.authzforce.core.pdp.api.FirstOrderFunctionCall; +import org.ow2.authzforce.core.pdp.api.FunctionSignature; +import org.ow2.authzforce.core.pdp.api.IndeterminateEvaluationException; +import org.ow2.authzforce.core.pdp.api.StatusHelper; +import org.ow2.authzforce.core.pdp.impl.value.BooleanValue; +import org.ow2.authzforce.core.pdp.impl.value.DatatypeConstants; + +/** + * A class that implements the logical functions "or" and "and". + *

+ * From XACML core specification of function 'urn:oasis:names:tc:xacml:1.0:function:or': This function SHALL return "False" if it has no arguments and SHALL + * return "True" if at least one of its arguments evaluates to "True". The order of evaluation SHALL be from first argument to last. The evaluation SHALL stop + * with a result of "True" if any argument evaluates to "True", leaving the rest of the arguments unevaluated. + * + * @author cdangerv + * @version $Id: $ + */ +public final class LogicalOrFunction extends FirstOrderFunction.SingleParameterTyped +{ + private static final class Call extends FirstOrderFunctionCall + { + private static final String INDETERMINATE_ARG_MESSAGE_PREFIX = "Function " + NAME_OR + ": Indeterminate arg #"; + private static final String INVALID_ARG_TYPE_MESSAGE_PREFIX = "Function " + NAME_OR + ": Invalid type (expected = " + DatatypeConstants.BOOLEAN.TYPE + + ") of arg#"; + + private final List> checkedArgExpressions; + + private Call(FunctionSignature functionSig, List> argExpressions, Datatype[] remainingArgTypes) + throws IllegalArgumentException + { + super(functionSig, argExpressions, remainingArgTypes); + this.checkedArgExpressions = argExpressions; + } + + @Override + public BooleanValue evaluate(EvaluationContext context, AttributeValue... checkedRemainingArgs) throws IndeterminateEvaluationException + { + /** + * TODO: optimize this function call by checking the following: + *

    + *
  1. If any argument expression is constant BooleanAttributeValue True, return always true.
  2. + *
  3. Else If all argument expressions are constant BooleanAttributeValue False, return always false.
  4. + *
  5. + * Else If any argument expression is constant BooleanAttributeValue False, remove it from the arguments, as it has no effect on the final result. + * Indeed, or function is commutative and or(false, x, y...) = or(x, y...).
  6. + *
+ * The first two optimizations can be achieved by pre-evaluating the function call with context = null and check the result if no + * IndeterminateEvaluationException is thrown. + */ + + IndeterminateEvaluationException indeterminateException = null; + int argIndex = 0; + for (final Expression arg : checkedArgExpressions) + { + // Evaluate the argument + final BooleanValue attrVal; + try + { + attrVal = Expressions.eval(arg, context, DatatypeConstants.BOOLEAN.TYPE); + if (attrVal.getUnderlyingValue()) + { + return BooleanValue.TRUE; + } + } catch (IndeterminateEvaluationException e) + { + // save the indeterminate to throw later only if there was not any TRUE in remaining + // args + indeterminateException = new IndeterminateEvaluationException(INDETERMINATE_ARG_MESSAGE_PREFIX + argIndex, + StatusHelper.STATUS_PROCESSING_ERROR, e); + } + + argIndex++; + } + + // do the same with remaining arg values + if (checkedRemainingArgs != null) + { + + for (final AttributeValue arg : checkedRemainingArgs) + { + // Evaluate the argument + final BooleanValue attrVal; + try + { + attrVal = BooleanValue.class.cast(arg); + } catch (ClassCastException e) + { + throw new IndeterminateEvaluationException(INVALID_ARG_TYPE_MESSAGE_PREFIX + argIndex + ": " + arg.getClass().getName(), + StatusHelper.STATUS_PROCESSING_ERROR, e); + } + + if (attrVal.getUnderlyingValue()) + { + return BooleanValue.TRUE; + } + + argIndex++; + } + } + + if (indeterminateException != null) + { + // there was at least one indeterminate arg that could have been TRUE or FALSE -> + // indeterminate result + throw indeterminateException; + } + + return BooleanValue.FALSE; + } + } + + /** + * XACML standard identifier for the "or" logical function + */ + public static final String NAME_OR = XACML_NS_1_0 + "or"; + + private LogicalOrFunction() + { + super(NAME_OR, DatatypeConstants.BOOLEAN.TYPE, true, Arrays.asList(DatatypeConstants.BOOLEAN.TYPE)); + } + + /** + * Singleton instance of "or" logical function + */ + public static final LogicalOrFunction INSTANCE = new LogicalOrFunction(); + + /* + * (non-Javadoc) + * + * @see com.thalesgroup.authzforce.core.func.FirstOrderFunction#getFunctionCall(java.util.List, com.thalesgroup.authzforce.core.eval.DatatypeDef[]) + */ + /** {@inheritDoc} */ + @Override + public FirstOrderFunctionCall newCall(final List> argExpressions, Datatype... remainingArgTypes) + { + return new Call(functionSignature, argExpressions, remainingArgTypes); + } + +} diff --git a/src/main/java/org/ow2/authzforce/core/pdp/impl/func/MapFunctionFactory.java b/src/main/java/org/ow2/authzforce/core/pdp/impl/func/MapFunctionFactory.java index ff010cad..fe7c4e2a 100644 --- a/src/main/java/org/ow2/authzforce/core/pdp/impl/func/MapFunctionFactory.java +++ b/src/main/java/org/ow2/authzforce/core/pdp/impl/func/MapFunctionFactory.java @@ -1,133 +1,135 @@ -/** - * Copyright (C) 2011-2015 Thales Services SAS. - * - * This file is part of AuthZForce. - * - * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software - * Foundation, either version 3 of the License, or (at your option) any later version. - * - * AuthZForce 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . - */ -package org.ow2.authzforce.core.pdp.impl.func; - -import java.util.ArrayDeque; -import java.util.Collection; -import java.util.List; - -import org.ow2.authzforce.core.pdp.api.AttributeValue; -import org.ow2.authzforce.core.pdp.api.Bag; -import org.ow2.authzforce.core.pdp.api.BagDatatype; -import org.ow2.authzforce.core.pdp.api.Bags; -import org.ow2.authzforce.core.pdp.api.Datatype; -import org.ow2.authzforce.core.pdp.api.DatatypeFactory; -import org.ow2.authzforce.core.pdp.api.EvaluationContext; -import org.ow2.authzforce.core.pdp.api.Expression; -import org.ow2.authzforce.core.pdp.api.FirstOrderFunction; -import org.ow2.authzforce.core.pdp.api.Function; -import org.ow2.authzforce.core.pdp.api.IndeterminateEvaluationException; -import org.ow2.authzforce.core.pdp.impl.func.HigherOrderBagFunctionSet.OneBagOnlyHigherOrderFunction; - -/** - * - * Map function factory - * - */ -public final class MapFunctionFactory -{ - - /** - * Standard identifier for the map function. - */ - public static final String NAME_MAP = Function.XACML_NS_3_0 + "map"; - - /** - * - * map function - * - * @param - * subfunction return type - * - */ - private static final class MapFunction extends OneBagOnlyHigherOrderFunction, SUB_RETURN_T> - { - - private static final class Call extends OneBagOnlyHigherOrderFunction.Call, SUB_RETURN> - { - private final Datatype returnBagElementType; - private final String indeterminateSubFuncEvalMessagePrefix; - - private Call(Datatype> returnType, FirstOrderFunction subFunction, List> primitiveInputs, - Expression lastInputBag) - { - super(NAME_MAP, returnType, subFunction, primitiveInputs, lastInputBag); - this.returnBagElementType = subFunction.getReturnType(); - this.indeterminateSubFuncEvalMessagePrefix = "Function " + NAME_MAP + ": Error calling sub-function (first argument) with last arg="; - } - - @Override - protected Bag evaluate(Bag lastArgBag, EvaluationContext context) throws IndeterminateEvaluationException - { - final Collection results = new ArrayDeque<>(lastArgBag.size()); - for (final AttributeValue lastArgBagVal : lastArgBag) - { - final SUB_RETURN subResult; - try - { - subResult = subFuncCall.evaluate(context, lastArgBagVal); - } catch (IndeterminateEvaluationException e) - { - throw new IndeterminateEvaluationException(indeterminateSubFuncEvalMessagePrefix + lastArgBagVal, e.getStatusCode(), e); - } - - results.add(subResult); - } - - return Bags.getInstance(returnBagElementType, results); - } - } - - /** - * Creates Map function for specific sub-function's return type - * - * @param subFunctionReturnType - * sub-function return type - */ - private MapFunction(BagDatatype returnType) - { - super(NAME_MAP, returnType, returnType.getElementType()); - } - - @Override - protected OneBagOnlyHigherOrderFunction.Call, SUB_RETURN_T> newFunctionCall(FirstOrderFunction subFunc, - List> primitiveInputs, Expression lastInputBag) - { - return new Call<>(this.getReturnType(), subFunc, primitiveInputs, lastInputBag); - } - - } - - /** - * Instance of generic factory for standard 'map' function (singleton) - */ - public static final GenericHigherOrderFunctionFactory INSTANCE = new GenericHigherOrderFunctionFactory() - { - - @Override - public final String getId() - { - return NAME_MAP; - } - - @Override - public final HigherOrderBagFunction getInstance( - DatatypeFactory subFunctionReturnTypeFactory) - { - return new MapFunction<>(subFunctionReturnTypeFactory.getBagDatatype()); - } - - }; - -} +/** + * Copyright (C) 2011-2015 Thales Services SAS. + * + * This file is part of AuthZForce. + * + * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * AuthZForce 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . + */ +package org.ow2.authzforce.core.pdp.impl.func; + +import java.util.ArrayDeque; +import java.util.Collection; +import java.util.List; + +import org.ow2.authzforce.core.pdp.api.AttributeValue; +import org.ow2.authzforce.core.pdp.api.Bag; +import org.ow2.authzforce.core.pdp.api.BagDatatype; +import org.ow2.authzforce.core.pdp.api.Bags; +import org.ow2.authzforce.core.pdp.api.Datatype; +import org.ow2.authzforce.core.pdp.api.DatatypeFactory; +import org.ow2.authzforce.core.pdp.api.EvaluationContext; +import org.ow2.authzforce.core.pdp.api.Expression; +import org.ow2.authzforce.core.pdp.api.FirstOrderFunction; +import org.ow2.authzforce.core.pdp.api.Function; +import org.ow2.authzforce.core.pdp.api.IndeterminateEvaluationException; +import org.ow2.authzforce.core.pdp.impl.func.HigherOrderBagFunctionSet.OneBagOnlyHigherOrderFunction; + +/** + * + * Map function factory + * + * @author cdangerv + * @version $Id: $ + */ +public final class MapFunctionFactory +{ + + /** + * Standard identifier for the map function. + */ + public static final String NAME_MAP = Function.XACML_NS_3_0 + "map"; + + /** + * + * map function + * + * @param + * subfunction return type + * + */ + private static final class MapFunction extends OneBagOnlyHigherOrderFunction, SUB_RETURN_T> + { + + private static final class Call extends OneBagOnlyHigherOrderFunction.Call, SUB_RETURN> + { + private final Datatype returnBagElementType; + private final String indeterminateSubFuncEvalMessagePrefix; + + private Call(Datatype> returnType, FirstOrderFunction subFunction, List> primitiveInputs, + Expression lastInputBag) + { + super(NAME_MAP, returnType, subFunction, primitiveInputs, lastInputBag); + this.returnBagElementType = subFunction.getReturnType(); + this.indeterminateSubFuncEvalMessagePrefix = "Function " + NAME_MAP + ": Error calling sub-function (first argument) with last arg="; + } + + @Override + protected Bag evaluate(Bag lastArgBag, EvaluationContext context) throws IndeterminateEvaluationException + { + final Collection results = new ArrayDeque<>(lastArgBag.size()); + for (final AttributeValue lastArgBagVal : lastArgBag) + { + final SUB_RETURN subResult; + try + { + subResult = subFuncCall.evaluate(context, lastArgBagVal); + } catch (IndeterminateEvaluationException e) + { + throw new IndeterminateEvaluationException(indeterminateSubFuncEvalMessagePrefix + lastArgBagVal, e.getStatusCode(), e); + } + + results.add(subResult); + } + + return Bags.getInstance(returnBagElementType, results); + } + } + + /** + * Creates Map function for specific sub-function's return type + * + * @param subFunctionReturnType + * sub-function return type + */ + private MapFunction(BagDatatype returnType) + { + super(NAME_MAP, returnType, returnType.getElementType()); + } + + @Override + protected OneBagOnlyHigherOrderFunction.Call, SUB_RETURN_T> newFunctionCall(FirstOrderFunction subFunc, + List> primitiveInputs, Expression lastInputBag) + { + return new Call<>(this.getReturnType(), subFunc, primitiveInputs, lastInputBag); + } + + } + + /** + * Instance of generic factory for standard 'map' function (singleton) + */ + public static final GenericHigherOrderFunctionFactory INSTANCE = new GenericHigherOrderFunctionFactory() + { + + @Override + public final String getId() + { + return NAME_MAP; + } + + @Override + public final HigherOrderBagFunction getInstance( + DatatypeFactory subFunctionReturnTypeFactory) + { + return new MapFunction<>(subFunctionReturnTypeFactory.getBagDatatype()); + } + + }; + +} diff --git a/src/main/java/org/ow2/authzforce/core/pdp/impl/func/NonEqualTypeMatchFunction.java b/src/main/java/org/ow2/authzforce/core/pdp/impl/func/NonEqualTypeMatchFunction.java index 092f8d22..cfc1b9e9 100644 --- a/src/main/java/org/ow2/authzforce/core/pdp/impl/func/NonEqualTypeMatchFunction.java +++ b/src/main/java/org/ow2/authzforce/core/pdp/impl/func/NonEqualTypeMatchFunction.java @@ -1,366 +1,368 @@ -/** - * Copyright (C) 2011-2015 Thales Services SAS. - * - * This file is part of AuthZForce. - * - * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software - * Foundation, either version 3 of the License, or (at your option) any later version. - * - * AuthZForce 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . - */ -package org.ow2.authzforce.core.pdp.impl.func; - -import java.util.Arrays; -import java.util.Deque; -import java.util.List; -import java.util.regex.PatternSyntaxException; - -import org.ow2.authzforce.core.pdp.api.AttributeValue; -import org.ow2.authzforce.core.pdp.api.Datatype; -import org.ow2.authzforce.core.pdp.api.Expression; -import org.ow2.authzforce.core.pdp.api.FirstOrderFunction; -import org.ow2.authzforce.core.pdp.api.FirstOrderFunctionCall; -import org.ow2.authzforce.core.pdp.api.FirstOrderFunctionCall.EagerMultiPrimitiveTypeEval; -import org.ow2.authzforce.core.pdp.api.FunctionSet; -import org.ow2.authzforce.core.pdp.api.FunctionSignature; -import org.ow2.authzforce.core.pdp.api.IndeterminateEvaluationException; -import org.ow2.authzforce.core.pdp.api.StatusHelper; -import org.ow2.authzforce.core.pdp.impl.value.AnyURIValue; -import org.ow2.authzforce.core.pdp.impl.value.BooleanValue; -import org.ow2.authzforce.core.pdp.impl.value.DNSNameValue; -import org.ow2.authzforce.core.pdp.impl.value.DatatypeConstants; -import org.ow2.authzforce.core.pdp.impl.value.IPAddressValue; -import org.ow2.authzforce.core.pdp.impl.value.RFC822NameValue; -import org.ow2.authzforce.core.pdp.impl.value.SimpleValue; -import org.ow2.authzforce.core.pdp.impl.value.StringValue; -import org.ow2.authzforce.core.pdp.impl.value.X500NameValue; - -/** - * Implements generic match functions taking two parameters of possibly different types, e.g. a string and a URI. - * - * @param - * Type of the first parameter of this function. - * @param - * Type of the second parameter of this function. - * - */ -public final class NonEqualTypeMatchFunction extends FirstOrderFunction.MultiParameterTyped -{ - /** - * Standard identifier for the rfc822Name-match function (different from rfc822Name-regexp-match down below). - */ - public static final String NAME_RFC822NAME_MATCH = XACML_NS_1_0 + "rfc822Name-match"; - - /** - * Standard identifier for the anyURI-regexp-match function. - */ - public static final String NAME_ANYURI_REGEXP_MATCH = XACML_NS_2_0 + "anyURI-regexp-match"; - - /** - * Standard identifier for the ipAddress-regexp-match function. - */ - public static final String NAME_IPADDRESS_REGEXP_MATCH = XACML_NS_2_0 + "ipAddress-regexp-match"; - - /** - * Standard identifier for the dnsName-regexp-match function. - */ - public static final String NAME_DNSNAME_REGEXP_MATCH = XACML_NS_2_0 + "dnsName-regexp-match"; - - /** - * Standard identifier for the rfc822Name-regexp-match function. - */ - public static final String NAME_RFC822NAME_REGEXP_MATCH = XACML_NS_2_0 + "rfc822Name-regexp-match"; - - /** - * Standard identifier for the x500Name-regexp-match function. - */ - public static final String NAME_X500NAME_REGEXP_MATCH = XACML_NS_2_0 + "x500Name-regexp-match"; - - /** - * Standard identifier for the anyURI-starts-with function. - */ - public static final String NAME_ANYURI_STARTS_WITH = XACML_NS_3_0 + "anyURI-starts-with"; - - /** - * Standard identifier for the anyURI-ends-with function. - */ - public static final String NAME_ANYURI_ENDS_WITH = XACML_NS_3_0 + "anyURI-ends-with"; - - /** - * Standard identifier for the anyURI-contains-with function. - */ - public static final String NAME_ANYURI_CONTAINS = XACML_NS_3_0 + "anyURI-contains"; - - private interface Matcher - { - /** - * Evaluate function with second parameter as string - * - * @param arg0 - * first function parameter - * @param arg1 - * second function parameter - * @return true if and only if both arguments match according to the matcher definition - * @throws IllegalArgumentException - * if one of the arguments is not valid for this matcher - */ - boolean match(T0 arg0, T1 arg1) throws IllegalArgumentException; - } - - private static class CallFactory - { - private final String invalidArgTypesErrorMsg; - private final String invalidRegexErrorMsg; - private final Class paramClass0; - private final Class paramClass1; - private final Matcher matcher; - private final FunctionSignature funcSig; - - private CallFactory(FunctionSignature functionSig, Datatype paramType0, Datatype paramType1, Matcher matcher) - { - - this.invalidArgTypesErrorMsg = "Function " + functionSig.getName() + ": Invalid arg types: expected: " + paramType0 + "," + paramType1 - + "; actual: "; - this.invalidRegexErrorMsg = "Function " + functionSig.getName() + ": Invalid regular expression in arg#0"; - this.paramClass0 = paramType0.getValueClass(); - this.paramClass1 = paramType1.getValueClass(); - this.matcher = matcher; - this.funcSig = functionSig; - } - - protected FirstOrderFunctionCall getInstance(List> argExpressions, Datatype[] remainingArgTypes) - { - return new EagerMultiPrimitiveTypeEval(funcSig, argExpressions, remainingArgTypes) - { - @Override - protected final BooleanValue evaluate(Deque args) throws IndeterminateEvaluationException - { - final AttributeValue rawArg0 = args.poll(); - final AttributeValue rawArg1 = args.poll(); - - final T0 arg0; - final T1 arg1; - try - { - arg0 = paramClass0.cast(rawArg0); - arg1 = paramClass1.cast(rawArg1); - } catch (ClassCastException e) - { - throw new IndeterminateEvaluationException(invalidArgTypesErrorMsg + rawArg0.getDataType() + ", " + rawArg1.getDataType(), - StatusHelper.STATUS_PROCESSING_ERROR, e); - } - - final boolean isMatched; - try - { - isMatched = matcher.match(arg0, arg1); - } catch (PatternSyntaxException e) - { - throw new IndeterminateEvaluationException(invalidRegexErrorMsg, StatusHelper.STATUS_PROCESSING_ERROR, e); - } - - return BooleanValue.valueOf(isMatched); - } - }; - } - - } - - private interface CallFactoryBuilder - { - CallFactory build(FunctionSignature functionSignature, Datatype paramType0, Datatype paramType1); - } - - private final CallFactory funcCallFactory; - - /** - * Creates a new NonEqualTypeMatchFunction based on the given name. - * - * @param functionName - * the name of the standard match function, including the complete namespace - * @param paramType0 - * first parameter type - * @param paramType1 - * second parameter type - * @param matcher - * matching algorithm - * - */ - private NonEqualTypeMatchFunction(String functionName, Datatype paramType0, Datatype paramType1, Matcher matcher) - { - super(functionName, DatatypeConstants.BOOLEAN.TYPE, false, Arrays.asList(paramType0, paramType1)); - this.funcCallFactory = new CallFactory<>(this.functionSignature, paramType0, paramType1, matcher); - } - - private NonEqualTypeMatchFunction(String functionName, Datatype paramType0, Datatype paramType1, CallFactoryBuilder callFactoryBuilder) - { - super(functionName, DatatypeConstants.BOOLEAN.TYPE, false, Arrays.asList(paramType0, paramType1)); - this.funcCallFactory = callFactoryBuilder.build(functionSignature, paramType0, paramType1); - } - - @Override - public FirstOrderFunctionCall newCall(List> argExpressions, Datatype... remainingArgTypes) throws IllegalArgumentException - { - /* - * Actual argument types are expected to be different, therefore we use the supertype AttributeValue as generic parameter type for all when creating the - * function call - */ - return funcCallFactory.getInstance(argExpressions, remainingArgTypes); - } - - /** - * rfc822Name-match function - * - */ - private static final Matcher RFC822NAME_MATCHER = new Matcher() - { - - @Override - public final boolean match(StringValue arg0, RFC822NameValue arg1) - { - return arg1.match(arg0.getUnderlyingValue()); - } - }; - - /** - * *-regexp-match function - * - * @param - * second parameter type - */ - private static class RegexpMatchCallFactoryBuilder> implements CallFactoryBuilder - { - - private final Matcher regexMatcher = new Matcher() - { - @Override - public boolean match(StringValue regex, AV arg1) - { - return RegexpMatchFunctionHelper.match(regex, arg1); - } - }; - - private class RegexpMatchCallFactory extends CallFactory - { - private final RegexpMatchFunctionHelper regexFuncHelper; - - private RegexpMatchCallFactory(FunctionSignature functionSignature, Datatype secondParamType) - { - super(functionSignature, DatatypeConstants.STRING.TYPE, secondParamType, regexMatcher); - regexFuncHelper = new RegexpMatchFunctionHelper(functionSignature, secondParamType); - } - - @Override - protected FirstOrderFunctionCall getInstance(List> argExpressions, Datatype[] remainingArgTypes) - { - final FirstOrderFunctionCall compiledRegexFuncCall = regexFuncHelper.getCompiledRegexMatchCall(argExpressions, remainingArgTypes); - /* - * compiledRegexFuncCall == null means no optimization using a pre-compiled regex could be done; in this case, use super.newCall() as usual, - * which will call match() down below, compiling the regex on-the-fly for each evaluation. - */ - return compiledRegexFuncCall == null ? super.getInstance(argExpressions, remainingArgTypes) : compiledRegexFuncCall; - } - } - - @Override - public CallFactory build(FunctionSignature functionSignature, Datatype paramType0, Datatype paramType1) - { - return new RegexpMatchCallFactory(functionSignature, paramType1); - } - - } - - // public static void main(String... args) throws XPathException - // { - // String input = "zzztesting"; - // String regex = "^test.*"; - // String flags = ""; - // String xpathlang = "XP20"; - // // - // RegularExpression compiledRegex = Configuration.getPlatform().compileRegularExpression(regex, - // flags, xpathlang, null); - // boolean isMatched = compiledRegex.containsMatch(input); - // System.out.println(isMatched); - // } - - /** - * anyURI-starts-with matcher. For string-starts-with, see {@link EqualTypeMatchFunction} class. - * - */ - private static final Matcher ANYURI_STARTS_WITH_MATCHER = new Matcher() - { - - /** - * WARNING: the XACML spec defines the first argument as the prefix - */ - @Override - public final boolean match(StringValue prefix, AnyURIValue arg1) - { - return arg1.getUnderlyingValue().startsWith(prefix.getUnderlyingValue()); - } - }; - - /** - * anyURI-ends-with matcher - */ - private static final Matcher ANYURI_ENDS_WITH_MATCHER = new Matcher() - { - /** - * WARNING: the XACML spec defines the first argument as the suffix - */ - @Override - public final boolean match(StringValue suffix, AnyURIValue arg1) - { - return arg1.getUnderlyingValue().endsWith(suffix.getUnderlyingValue()); - } - }; - - /** - * anyURI-contains matcher - * - */ - private static final Matcher ANYURI_CONTAINS_MATCHER = new Matcher() - { - - /** - * WARNING: the XACML spec defines the second argument as the string that must contain the other - */ - @Override - public final boolean match(StringValue contained, AnyURIValue arg1) - { - return arg1.getUnderlyingValue().contains(contained.getUnderlyingValue()); - } - }; - - /** - * Function cluster - */ - public static final FunctionSet CLUSTER = new BaseFunctionSet(FunctionSet.DEFAULT_ID_NAMESPACE + "non-equal-type-match", - // - new NonEqualTypeMatchFunction<>(NAME_RFC822NAME_MATCH, DatatypeConstants.STRING.TYPE, DatatypeConstants.RFC822NAME.TYPE, RFC822NAME_MATCHER), - // - new NonEqualTypeMatchFunction<>(NAME_ANYURI_STARTS_WITH, DatatypeConstants.STRING.TYPE, DatatypeConstants.ANYURI.TYPE, ANYURI_STARTS_WITH_MATCHER), - // - new NonEqualTypeMatchFunction<>(NAME_ANYURI_ENDS_WITH, DatatypeConstants.STRING.TYPE, DatatypeConstants.ANYURI.TYPE, ANYURI_ENDS_WITH_MATCHER), - // - new NonEqualTypeMatchFunction<>(NAME_ANYURI_CONTAINS, DatatypeConstants.STRING.TYPE, DatatypeConstants.ANYURI.TYPE, ANYURI_CONTAINS_MATCHER), - // - new NonEqualTypeMatchFunction<>(NAME_ANYURI_REGEXP_MATCH, DatatypeConstants.STRING.TYPE, DatatypeConstants.ANYURI.TYPE, - new RegexpMatchCallFactoryBuilder()), - // - new NonEqualTypeMatchFunction<>(NAME_IPADDRESS_REGEXP_MATCH, DatatypeConstants.STRING.TYPE, DatatypeConstants.IPADDRESS.TYPE, - new RegexpMatchCallFactoryBuilder()), - // - new NonEqualTypeMatchFunction<>(NAME_DNSNAME_REGEXP_MATCH, DatatypeConstants.STRING.TYPE, DatatypeConstants.DNSNAME.TYPE, - new RegexpMatchCallFactoryBuilder()), - // - new NonEqualTypeMatchFunction<>(NAME_RFC822NAME_REGEXP_MATCH, DatatypeConstants.STRING.TYPE, DatatypeConstants.RFC822NAME.TYPE, - new RegexpMatchCallFactoryBuilder()), - // - new NonEqualTypeMatchFunction<>(NAME_X500NAME_REGEXP_MATCH, DatatypeConstants.STRING.TYPE, DatatypeConstants.X500NAME.TYPE, - new RegexpMatchCallFactoryBuilder())); - -} +/** + * Copyright (C) 2011-2015 Thales Services SAS. + * + * This file is part of AuthZForce. + * + * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * AuthZForce 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . + */ +package org.ow2.authzforce.core.pdp.impl.func; + +import java.util.Arrays; +import java.util.Deque; +import java.util.List; +import java.util.regex.PatternSyntaxException; + +import org.ow2.authzforce.core.pdp.api.AttributeValue; +import org.ow2.authzforce.core.pdp.api.Datatype; +import org.ow2.authzforce.core.pdp.api.Expression; +import org.ow2.authzforce.core.pdp.api.FirstOrderFunction; +import org.ow2.authzforce.core.pdp.api.FirstOrderFunctionCall; +import org.ow2.authzforce.core.pdp.api.FirstOrderFunctionCall.EagerMultiPrimitiveTypeEval; +import org.ow2.authzforce.core.pdp.api.FunctionSet; +import org.ow2.authzforce.core.pdp.api.FunctionSignature; +import org.ow2.authzforce.core.pdp.api.IndeterminateEvaluationException; +import org.ow2.authzforce.core.pdp.api.StatusHelper; +import org.ow2.authzforce.core.pdp.impl.value.AnyURIValue; +import org.ow2.authzforce.core.pdp.impl.value.BooleanValue; +import org.ow2.authzforce.core.pdp.impl.value.DNSNameValue; +import org.ow2.authzforce.core.pdp.impl.value.DatatypeConstants; +import org.ow2.authzforce.core.pdp.impl.value.IPAddressValue; +import org.ow2.authzforce.core.pdp.impl.value.RFC822NameValue; +import org.ow2.authzforce.core.pdp.impl.value.SimpleValue; +import org.ow2.authzforce.core.pdp.impl.value.StringValue; +import org.ow2.authzforce.core.pdp.impl.value.X500NameValue; + +/** + * Implements generic match functions taking two parameters of possibly different types, e.g. a string and a URI. + * + * @param + * Type of the first parameter of this function. + * @param + * Type of the second parameter of this function. + * @author cdangerv + * @version $Id: $ + */ +public final class NonEqualTypeMatchFunction extends FirstOrderFunction.MultiParameterTyped +{ + /** + * Standard identifier for the rfc822Name-match function (different from rfc822Name-regexp-match down below). + */ + public static final String NAME_RFC822NAME_MATCH = XACML_NS_1_0 + "rfc822Name-match"; + + /** + * Standard identifier for the anyURI-regexp-match function. + */ + public static final String NAME_ANYURI_REGEXP_MATCH = XACML_NS_2_0 + "anyURI-regexp-match"; + + /** + * Standard identifier for the ipAddress-regexp-match function. + */ + public static final String NAME_IPADDRESS_REGEXP_MATCH = XACML_NS_2_0 + "ipAddress-regexp-match"; + + /** + * Standard identifier for the dnsName-regexp-match function. + */ + public static final String NAME_DNSNAME_REGEXP_MATCH = XACML_NS_2_0 + "dnsName-regexp-match"; + + /** + * Standard identifier for the rfc822Name-regexp-match function. + */ + public static final String NAME_RFC822NAME_REGEXP_MATCH = XACML_NS_2_0 + "rfc822Name-regexp-match"; + + /** + * Standard identifier for the x500Name-regexp-match function. + */ + public static final String NAME_X500NAME_REGEXP_MATCH = XACML_NS_2_0 + "x500Name-regexp-match"; + + /** + * Standard identifier for the anyURI-starts-with function. + */ + public static final String NAME_ANYURI_STARTS_WITH = XACML_NS_3_0 + "anyURI-starts-with"; + + /** + * Standard identifier for the anyURI-ends-with function. + */ + public static final String NAME_ANYURI_ENDS_WITH = XACML_NS_3_0 + "anyURI-ends-with"; + + /** + * Standard identifier for the anyURI-contains-with function. + */ + public static final String NAME_ANYURI_CONTAINS = XACML_NS_3_0 + "anyURI-contains"; + + private interface Matcher + { + /** + * Evaluate function with second parameter as string + * + * @param arg0 + * first function parameter + * @param arg1 + * second function parameter + * @return true if and only if both arguments match according to the matcher definition + * @throws IllegalArgumentException + * if one of the arguments is not valid for this matcher + */ + boolean match(T0 arg0, T1 arg1) throws IllegalArgumentException; + } + + private static class CallFactory + { + private final String invalidArgTypesErrorMsg; + private final String invalidRegexErrorMsg; + private final Class paramClass0; + private final Class paramClass1; + private final Matcher matcher; + private final FunctionSignature funcSig; + + private CallFactory(FunctionSignature functionSig, Datatype paramType0, Datatype paramType1, Matcher matcher) + { + + this.invalidArgTypesErrorMsg = "Function " + functionSig.getName() + ": Invalid arg types: expected: " + paramType0 + "," + paramType1 + + "; actual: "; + this.invalidRegexErrorMsg = "Function " + functionSig.getName() + ": Invalid regular expression in arg#0"; + this.paramClass0 = paramType0.getValueClass(); + this.paramClass1 = paramType1.getValueClass(); + this.matcher = matcher; + this.funcSig = functionSig; + } + + protected FirstOrderFunctionCall getInstance(List> argExpressions, Datatype[] remainingArgTypes) + { + return new EagerMultiPrimitiveTypeEval(funcSig, argExpressions, remainingArgTypes) + { + @Override + protected final BooleanValue evaluate(Deque args) throws IndeterminateEvaluationException + { + final AttributeValue rawArg0 = args.poll(); + final AttributeValue rawArg1 = args.poll(); + + final T0 arg0; + final T1 arg1; + try + { + arg0 = paramClass0.cast(rawArg0); + arg1 = paramClass1.cast(rawArg1); + } catch (ClassCastException e) + { + throw new IndeterminateEvaluationException(invalidArgTypesErrorMsg + rawArg0.getDataType() + ", " + rawArg1.getDataType(), + StatusHelper.STATUS_PROCESSING_ERROR, e); + } + + final boolean isMatched; + try + { + isMatched = matcher.match(arg0, arg1); + } catch (PatternSyntaxException e) + { + throw new IndeterminateEvaluationException(invalidRegexErrorMsg, StatusHelper.STATUS_PROCESSING_ERROR, e); + } + + return BooleanValue.valueOf(isMatched); + } + }; + } + + } + + private interface CallFactoryBuilder + { + CallFactory build(FunctionSignature functionSignature, Datatype paramType0, Datatype paramType1); + } + + private final CallFactory funcCallFactory; + + /** + * Creates a new NonEqualTypeMatchFunction based on the given name. + * + * @param functionName + * the name of the standard match function, including the complete namespace + * @param paramType0 + * first parameter type + * @param paramType1 + * second parameter type + * @param matcher + * matching algorithm + * + */ + private NonEqualTypeMatchFunction(String functionName, Datatype paramType0, Datatype paramType1, Matcher matcher) + { + super(functionName, DatatypeConstants.BOOLEAN.TYPE, false, Arrays.asList(paramType0, paramType1)); + this.funcCallFactory = new CallFactory<>(this.functionSignature, paramType0, paramType1, matcher); + } + + private NonEqualTypeMatchFunction(String functionName, Datatype paramType0, Datatype paramType1, CallFactoryBuilder callFactoryBuilder) + { + super(functionName, DatatypeConstants.BOOLEAN.TYPE, false, Arrays.asList(paramType0, paramType1)); + this.funcCallFactory = callFactoryBuilder.build(functionSignature, paramType0, paramType1); + } + + /** {@inheritDoc} */ + @Override + public FirstOrderFunctionCall newCall(List> argExpressions, Datatype... remainingArgTypes) throws IllegalArgumentException + { + /* + * Actual argument types are expected to be different, therefore we use the supertype AttributeValue as generic parameter type for all when creating the + * function call + */ + return funcCallFactory.getInstance(argExpressions, remainingArgTypes); + } + + /** + * rfc822Name-match function + * + */ + private static final Matcher RFC822NAME_MATCHER = new Matcher() + { + + @Override + public final boolean match(StringValue arg0, RFC822NameValue arg1) + { + return arg1.match(arg0.getUnderlyingValue()); + } + }; + + /** + * *-regexp-match function + * + * @param + * second parameter type + */ + private static class RegexpMatchCallFactoryBuilder> implements CallFactoryBuilder + { + + private final Matcher regexMatcher = new Matcher() + { + @Override + public boolean match(StringValue regex, AV arg1) + { + return RegexpMatchFunctionHelper.match(regex, arg1); + } + }; + + private class RegexpMatchCallFactory extends CallFactory + { + private final RegexpMatchFunctionHelper regexFuncHelper; + + private RegexpMatchCallFactory(FunctionSignature functionSignature, Datatype secondParamType) + { + super(functionSignature, DatatypeConstants.STRING.TYPE, secondParamType, regexMatcher); + regexFuncHelper = new RegexpMatchFunctionHelper(functionSignature, secondParamType); + } + + @Override + protected FirstOrderFunctionCall getInstance(List> argExpressions, Datatype[] remainingArgTypes) + { + final FirstOrderFunctionCall compiledRegexFuncCall = regexFuncHelper.getCompiledRegexMatchCall(argExpressions, remainingArgTypes); + /* + * compiledRegexFuncCall == null means no optimization using a pre-compiled regex could be done; in this case, use super.newCall() as usual, + * which will call match() down below, compiling the regex on-the-fly for each evaluation. + */ + return compiledRegexFuncCall == null ? super.getInstance(argExpressions, remainingArgTypes) : compiledRegexFuncCall; + } + } + + @Override + public CallFactory build(FunctionSignature functionSignature, Datatype paramType0, Datatype paramType1) + { + return new RegexpMatchCallFactory(functionSignature, paramType1); + } + + } + + // public static void main(String... args) throws XPathException + // { + // String input = "zzztesting"; + // String regex = "^test.*"; + // String flags = ""; + // String xpathlang = "XP20"; + // // + // RegularExpression compiledRegex = Configuration.getPlatform().compileRegularExpression(regex, + // flags, xpathlang, null); + // boolean isMatched = compiledRegex.containsMatch(input); + // System.out.println(isMatched); + // } + + /** + * anyURI-starts-with matcher. For string-starts-with, see {@link EqualTypeMatchFunction} class. + * + */ + private static final Matcher ANYURI_STARTS_WITH_MATCHER = new Matcher() + { + + /** + * WARNING: the XACML spec defines the first argument as the prefix + */ + @Override + public final boolean match(StringValue prefix, AnyURIValue arg1) + { + return arg1.getUnderlyingValue().startsWith(prefix.getUnderlyingValue()); + } + }; + + /** + * anyURI-ends-with matcher + */ + private static final Matcher ANYURI_ENDS_WITH_MATCHER = new Matcher() + { + /** + * WARNING: the XACML spec defines the first argument as the suffix + */ + @Override + public final boolean match(StringValue suffix, AnyURIValue arg1) + { + return arg1.getUnderlyingValue().endsWith(suffix.getUnderlyingValue()); + } + }; + + /** + * anyURI-contains matcher + * + */ + private static final Matcher ANYURI_CONTAINS_MATCHER = new Matcher() + { + + /** + * WARNING: the XACML spec defines the second argument as the string that must contain the other + */ + @Override + public final boolean match(StringValue contained, AnyURIValue arg1) + { + return arg1.getUnderlyingValue().contains(contained.getUnderlyingValue()); + } + }; + + /** + * Function cluster + */ + public static final FunctionSet CLUSTER = new BaseFunctionSet(FunctionSet.DEFAULT_ID_NAMESPACE + "non-equal-type-match", + // + new NonEqualTypeMatchFunction<>(NAME_RFC822NAME_MATCH, DatatypeConstants.STRING.TYPE, DatatypeConstants.RFC822NAME.TYPE, RFC822NAME_MATCHER), + // + new NonEqualTypeMatchFunction<>(NAME_ANYURI_STARTS_WITH, DatatypeConstants.STRING.TYPE, DatatypeConstants.ANYURI.TYPE, ANYURI_STARTS_WITH_MATCHER), + // + new NonEqualTypeMatchFunction<>(NAME_ANYURI_ENDS_WITH, DatatypeConstants.STRING.TYPE, DatatypeConstants.ANYURI.TYPE, ANYURI_ENDS_WITH_MATCHER), + // + new NonEqualTypeMatchFunction<>(NAME_ANYURI_CONTAINS, DatatypeConstants.STRING.TYPE, DatatypeConstants.ANYURI.TYPE, ANYURI_CONTAINS_MATCHER), + // + new NonEqualTypeMatchFunction<>(NAME_ANYURI_REGEXP_MATCH, DatatypeConstants.STRING.TYPE, DatatypeConstants.ANYURI.TYPE, + new RegexpMatchCallFactoryBuilder()), + // + new NonEqualTypeMatchFunction<>(NAME_IPADDRESS_REGEXP_MATCH, DatatypeConstants.STRING.TYPE, DatatypeConstants.IPADDRESS.TYPE, + new RegexpMatchCallFactoryBuilder()), + // + new NonEqualTypeMatchFunction<>(NAME_DNSNAME_REGEXP_MATCH, DatatypeConstants.STRING.TYPE, DatatypeConstants.DNSNAME.TYPE, + new RegexpMatchCallFactoryBuilder()), + // + new NonEqualTypeMatchFunction<>(NAME_RFC822NAME_REGEXP_MATCH, DatatypeConstants.STRING.TYPE, DatatypeConstants.RFC822NAME.TYPE, + new RegexpMatchCallFactoryBuilder()), + // + new NonEqualTypeMatchFunction<>(NAME_X500NAME_REGEXP_MATCH, DatatypeConstants.STRING.TYPE, DatatypeConstants.X500NAME.TYPE, + new RegexpMatchCallFactoryBuilder())); + +} diff --git a/src/main/java/org/ow2/authzforce/core/pdp/impl/func/NumericArithmeticFunction.java b/src/main/java/org/ow2/authzforce/core/pdp/impl/func/NumericArithmeticFunction.java index 106ddbc7..f2a033f9 100644 --- a/src/main/java/org/ow2/authzforce/core/pdp/impl/func/NumericArithmeticFunction.java +++ b/src/main/java/org/ow2/authzforce/core/pdp/impl/func/NumericArithmeticFunction.java @@ -1,315 +1,317 @@ -/** - * Copyright (C) 2011-2015 Thales Services SAS. - * - * This file is part of AuthZForce. - * - * AuthZForce is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software - * Foundation, either version 3 of the License, or (at your option) any later version. - * - * AuthZForce 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with AuthZForce. If not, see . - */ -package org.ow2.authzforce.core.pdp.impl.func; - -import java.util.Arrays; -import java.util.Deque; -import java.util.List; - -import org.ow2.authzforce.core.pdp.api.Datatype; -import org.ow2.authzforce.core.pdp.api.Expression; -import org.ow2.authzforce.core.pdp.api.FirstOrderFunction; -import org.ow2.authzforce.core.pdp.api.FirstOrderFunctionCall; -import org.ow2.authzforce.core.pdp.api.FirstOrderFunctionCall.EagerSinglePrimitiveTypeEval; -import org.ow2.authzforce.core.pdp.api.FunctionSet; -import org.ow2.authzforce.core.pdp.api.FunctionSignature; -import org.ow2.authzforce.core.pdp.api.IndeterminateEvaluationException; -import org.ow2.authzforce.core.pdp.api.StatusHelper; -import org.ow2.authzforce.core.pdp.api.Value; -import org.ow2.authzforce.core.pdp.impl.value.DatatypeConstants; -import org.ow2.authzforce.core.pdp.impl.value.DoubleValue; -import org.ow2.authzforce.core.pdp.impl.value.IntegerValue; -import org.ow2.authzforce.core.pdp.impl.value.NumericValue; - -/** - * A class that implements all the numeric *-add functions (as opposed to date/time *-add-* functions). - * - * @param - * return and parameter type - * - */ -public final class NumericArithmeticFunction> extends FirstOrderFunction.SingleParameterTyped -{ - /** - * Standard integer-abs function URI - */ - public static final String NAME_INTEGER_ABS = XACML_NS_1_0 + "integer-abs"; - - /** - * Standard double-abs function URI - */ - public static final String NAME_DOUBLE_ABS = XACML_NS_1_0 + "double-abs"; - - /** - * Standard URI of function integer-add - */ - public static final String NAME_INTEGER_ADD = XACML_NS_1_0 + "integer-add"; - - /** - * Standard URI of function double-add - */ - public static final String NAME_DOUBLE_ADD = XACML_NS_1_0 + "double-add"; - - /** - * Standard URI for the integer-multiply function. - */ - public static final String NAME_INTEGER_MULTIPLY = XACML_NS_1_0 + "integer-multiply"; - - /** - * Standard URI for the double-multiply function. - */ - public static final String NAME_DOUBLE_MULTIPLY = XACML_NS_1_0 + "double-multiply"; - - /** - * Standard URI for the integer-subtract function. - */ - public static final String NAME_INTEGER_SUBTRACT = XACML_NS_1_0 + "integer-subtract"; - - /** - * Standard URI for the integer-subtract function. - */ - public static final String NAME_DOUBLE_SUBTRACT = XACML_NS_1_0 + "double-subtract"; - - /** - * Standard URI for the integer-divide function. - */ - public static final String NAME_INTEGER_DIVIDE = XACML_NS_1_0 + "integer-divide"; - - /** - * Standard URI for the double-divide function. - */ - public static final String NAME_DOUBLE_DIVIDE = XACML_NS_1_0 + "double-divide"; - - /** - * Standard URI for the integer-mod function. - */ - public static final String NAME_INTEGER_MOD = XACML_NS_1_0 + "integer-mod"; - - /** - * Standard URI for the round function. - */ - public static final String NAME_ROUND = XACML_NS_1_0 + "round"; - - /** - * Standard URI for the floor function. - */ - public static final String NAME_FLOOR = XACML_NS_1_0 + "floor"; - - private static final IllegalArgumentException UNDEF_PARAMETER_TYPES_EXCEPTION = new IllegalArgumentException("Undefined function parameter types"); - - private static List> validate(List> paramTypes) - { - if (paramTypes == null || paramTypes.isEmpty()) - { - throw UNDEF_PARAMETER_TYPES_EXCEPTION; - } - - return paramTypes; - } - - private interface StaticOperation> - { - V eval(Deque args) throws IllegalArgumentException, ArithmeticException; - } - - private static final class Call> extends EagerSinglePrimitiveTypeEval - { - private final String invalidArgsErrMsg; - private final StaticOperation op; - - private Call(FunctionSignature.SingleParameterTyped functionSig, StaticOperation op, List> args, Datatype[] remainingArgTypes) - throws IllegalArgumentException - { - super(functionSig, args, remainingArgTypes); - this.op = op; - this.invalidArgsErrMsg = "Function " + this.functionId + ": invalid argument(s)"; - } - - @Override - protected V evaluate(Deque args) throws IndeterminateEvaluationException - { - try - { - return op.eval(args); - } catch (IllegalArgumentException | ArithmeticException e) - { - throw new IndeterminateEvaluationException(invalidArgsErrMsg, StatusHelper.STATUS_PROCESSING_ERROR, e); - } - } - } - - private final StaticOperation op; - - /** - * Creates a new Numeric Arithmetic function. - * - * @param funcURI - * function URI - * - * @param paramTypes - * parameter/return types (all the same) - * @param varArgs - * whether this is a varargs function (like Java varargs method), i.e. last arg has variable-length - * - */ - private NumericArithmeticFunction(String funcURI, boolean varArgs, List> paramTypes, StaticOperation op) throws IllegalArgumentException - { - super(funcURI, validate(paramTypes).get(0), varArgs, paramTypes); - this.op = op; - } - - @Override - public FirstOrderFunctionCall newCall(List> argExpressions, Datatype... remainingArgTypes) throws IllegalArgumentException - { - /** - * TODO: optimize call to "add" (resp. "multiply") function call by checking all static/constant arguments and if there are more than one, pre-compute - * their sum (resp. product) and replace these arguments with one argument that is this sum (resp. product) in the function call. Indeed, 'add' function - * is commutative and (constant in upper case, variables in lower case): add(C1, C2, x, y...) = add(C1+C2, x, y...). Similarly, multiply(C1, C2, x, - * y...) = multiply(C1*C2, x, y...) - * - */ - - return new Call<>(functionSignature, op, argExpressions, remainingArgTypes); - } - - private static final class AbsOperation