--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<AttributeReleasePolicy xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:mace:shibboleth:arp:1.0" xsi:schemaLocation="urn:mace:shibboleth:arp:1.0 shibboleth-arp-1.0.xsd" >
+ <Description>Rule with matching functions set for values.</Description>
+ <Rule>
+ <Target>
+ <AnyTarget/>
+ </Target>
+ <Attribute name="urn:mace:dir:attribute-def:eduPersonAffiliation">
+ <AnyValue release="permit"/>
+ <Value release="deny" matchFunction="urn:mace:shibboleth:arp:matchFunction:regexMatch">^.+@example.edu$</Value>
+ </Attribute>
+ <Attribute name="urn:mace:dir:attribute-def:eduPersonEntitlement">
+ <Value release="permit" matchFunction="urn:mace:shibboleth:arp:matchFunction:regexMatch">^urn:x:1:.+$</Value>
+ </Attribute>
+ </Rule>
+</AttributeReleasePolicy>
matchFunctions.put(
new URI("urn:mace:shibboleth:arp:matchFunction:regexMatch"),
"edu.internet2.middleware.shibboleth.aa.arp.provider.RegexMatchFunction");
+ matchFunctions.put(
+ new URI("urn:mace:shibboleth:arp:matchFunction:stringValue"),
+ "edu.internet2.middleware.shibboleth.aa.arp.provider.StringValueMatchFunction");
} catch (URISyntaxException e) {
log.error("Error mapping standard match functions: " + e);
}
}
class Attribute {
+
private URI name;
private boolean anyValue = false;
private String anyValueRelease = "permit";
Iterator iterator = values.iterator();
while (iterator.hasNext()) {
AttributeValue valueSpec = (AttributeValue) iterator.next();
- if (valueSpec.getValue().equals(value) && valueSpec.getRelease().equals("deny")) {
+
+ MatchFunction resourceFunction;
+ try {
+ resourceFunction = ArpEngine.lookupMatchFunction(valueSpec.getMatchFunctionIdentifier());
+ // For safety, err on the side of caution
+ if (resourceFunction == null) {
+ log.warn(
+ "Could not locate matching function for ARP value. Function: "
+ + valueSpec.getMatchFunctionIdentifier().toString());
+ return false;
+ }
+
+ } catch (ArpException e) {
+ log.error("Error while attempting to find referenced matching function for ARP values: " + e);
+ return false;
+ }
+
+ try {
+ if (valueSpec.getRelease().equals("deny") && resourceFunction.match(valueSpec.getValue(), value)) {
+ return false;
+ }
+ } catch (MatchingException e) {
+ log.error("Could not apply referenced matching function to ARP value: " + e);
return false;
}
}
+
//Handle Permit All with no relevant specific denies
if (releaseAnyValue()) {
return true;
iterator = values.iterator();
while (iterator.hasNext()) {
AttributeValue valueSpec = (AttributeValue) iterator.next();
- if (valueSpec.getValue().equals(value) && valueSpec.getRelease().equals("permit")) {
- return true;
+
+ MatchFunction resourceFunction;
+ try {
+ resourceFunction = ArpEngine.lookupMatchFunction(valueSpec.getMatchFunctionIdentifier());
+ // Ignore non-functional permits
+ if (resourceFunction == null) {
+ log.warn(
+ "Could not locate matching function for ARP value. Function: "
+ + valueSpec.getMatchFunctionIdentifier().toString());
+ continue;
+ }
+
+ } catch (ArpException e) {
+ log.error("Error while attempting to find referenced matching function for ARP values: " + e);
+ continue;
}
- }
+ try {
+ if (valueSpec.getRelease().equals("permit")
+ && resourceFunction.match(valueSpec.getValue(), value)) {
+ return true;
+ }
+ } catch (MatchingException e) {
+ log.error("Could not apply referenced matching function to ARP value: " + e);
+ continue;
+ }
+ }
return false;
}
Element unmarshall() throws ArpMarshallingException {
try {
- Document placeHolder =
- DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
+ Document placeHolder = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
Element attributeNode = placeHolder.createElementNS(Arp.arpNamespace, "Attribute");
attributeNode.setAttributeNS(Arp.arpNamespace, "name", name.toString());
if (identifier != null) {
- attributeNode.setAttributeNS(
- Arp.arpNamespace,
- "identifier",
- identifier.toString());
+ attributeNode.setAttributeNS(Arp.arpNamespace, "identifier", identifier.toString());
}
if (anyValue) {
- Element anyValueNode =
- placeHolder.createElementNS(Arp.arpNamespace, "AnyValue");
+ Element anyValueNode = placeHolder.createElementNS(Arp.arpNamespace, "AnyValue");
anyValueNode.setAttributeNS(Arp.arpNamespace, "release", anyValueRelease);
attributeNode.appendChild(anyValueNode);
}
AttributeValue value = (AttributeValue) valueIterator.next();
Element valueNode = placeHolder.createElementNS(Arp.arpNamespace, "Value");
valueNode.setAttributeNS(Arp.arpNamespace, "release", value.getRelease());
+ if (!value
+ .getMatchFunctionIdentifier()
+ .equals(new URI("urn:mace:shibboleth:arp:matchFunction:stringValue"))) {
+ valueNode.setAttributeNS(
+ Arp.arpNamespace,
+ "matchFunction",
+ value.getMatchFunctionIdentifier().toString());
+ }
Text valueTextNode = placeHolder.createTextNode(value.getValue());
valueNode.appendChild(valueTextNode);
attributeNode.appendChild(valueNode);
}
return attributeNode;
+ } catch (URISyntaxException e) {
+ log.error("Encountered a problem unmarshalling an ARP Rule Resource: " + e);
+ throw new ArpMarshallingException("Encountered a problem unmarshalling an ARP Rule Resource.");
} catch (ParserConfigurationException e) {
log.error("Encountered a problem unmarshalling an ARP Rule: " + e);
throw new ArpMarshallingException("Encountered a problem unmarshalling an ARP Rule.");
}
//Handle <AnyValue/> definitions
- NodeList anyValueNodeList =
- element.getElementsByTagNameNS(Arp.arpNamespace, "AnyValue");
+ NodeList anyValueNodeList = element.getElementsByTagNameNS(Arp.arpNamespace, "AnyValue");
if (anyValueNodeList.getLength() == 1) {
anyValue = true;
if (((Element) anyValueNodeList.item(0)).hasAttribute("release")) {
for (int i = 0; valueNodeList.getLength() > i; i++) {
String release = null;
String value = null;
+ URI matchFunctionIdentifier = null;
if (((Element) valueNodeList.item(i)).hasAttribute("release")) {
release = ((Element) valueNodeList.item(i)).getAttribute("release");
}
+
+ //Grab the match function
+ try {
+ if (((Element) valueNodeList.item(i)).hasAttribute("matchFunction")) {
+ matchFunctionIdentifier =
+ new URI(((Element) valueNodeList.item(i)).getAttribute("matchFunction"));
+ }
+ } catch (URISyntaxException e) {
+ log.error(
+ "ARP match function not identified by a proper URI: "
+ + ((Element) valueNodeList.item(i)).getAttribute("matchFunction"));
+ throw new ArpMarshallingException("ARP match function not identified by a proper URI.");
+ }
+
if (((Element) valueNodeList.item(i)).hasChildNodes()
- && ((Element) valueNodeList.item(i)).getFirstChild().getNodeType()
- == Node.TEXT_NODE) {
- value =
- ((CharacterData) ((Element) valueNodeList.item(i)).getFirstChild())
- .getData();
+ && ((Element) valueNodeList.item(i)).getFirstChild().getNodeType() == Node.TEXT_NODE) {
+ value = ((CharacterData) ((Element) valueNodeList.item(i)).getFirstChild()).getData();
}
if (releaseAnyValue() && release.equals("permit")) {
continue;
}
- AttributeValue aValue = new AttributeValue(release, value);
+ AttributeValue aValue = new AttributeValue(release, matchFunctionIdentifier, value);
values.add(aValue);
}
}
}
}
class AttributeValue {
+
private String release = "permit";
private String value;
+ private URI matchFunctionIdentifier;
- AttributeValue(String release, String value) {
+ AttributeValue(String release, URI matchFunctionIdentifier, String value) throws ArpMarshallingException {
setRelease(release);
this.value = value;
+ if (matchFunctionIdentifier != null) {
+ this.matchFunctionIdentifier = matchFunctionIdentifier;
+ } else {
+ try {
+ matchFunctionIdentifier = new URI("urn:mace:shibboleth:arp:matchFunction:stringValue");
+ } catch (URISyntaxException e) {
+ throw new ArpMarshallingException("ARP Engine internal error: could not set default matching function for attribute value.");
+ }
+ }
}
String getRelease() {
return value;
}
+ URI getMatchFunctionIdentifier() {
+ return matchFunctionIdentifier;
+ }
+
void setRelease(String release) {
if (release == null) {
return;
--- /dev/null
+/*
+ * The Shibboleth License, Version 1.
+ * Copyright (c) 2002
+ * University Corporation for Advanced Internet Development, 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:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution, if any, must include
+ * the following acknowledgment: "This product includes software developed by
+ * the University Corporation for Advanced Internet Development
+ * <http://www.ucaid.edu>Internet2 Project. Alternately, this acknowledegement
+ * may appear in the software itself, if and wherever such third-party
+ * acknowledgments normally appear.
+ *
+ * Neither the name of Shibboleth nor the names of its contributors, nor
+ * Internet2, nor the University Corporation for Advanced Internet Development,
+ * Inc., nor UCAID may be used to endorse or promote products derived from this
+ * software without specific prior written permission. For written permission,
+ * please contact shibboleth@shibboleth.org
+ *
+ * Products derived from this software may not be called Shibboleth, Internet2,
+ * UCAID, or the University Corporation for Advanced Internet Development, nor
+ * may Shibboleth appear in their name, without prior written permission of the
+ * University Corporation for Advanced Internet Development.
+ *
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK
+ * OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY
+ * CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package edu.internet2.middleware.shibboleth.aa.arp.provider;
+
+import org.apache.log4j.Logger;
+
+import edu.internet2.middleware.shibboleth.aa.arp.MatchFunction;
+import edu.internet2.middleware.shibboleth.aa.arp.MatchingException;
+
+/**
+ * Match function that does exact string matching.
+ *
+ * @author Walter Hoehn (wassa@columbia.edu)
+ */
+public class StringValueMatchFunction implements MatchFunction {
+ private static Logger log = Logger.getLogger(StringValueMatchFunction.class.getName());
+ /**
+ * @see edu.internet2.middleware.shibboleth.aa.arp.MatchFunction#match(Object,
+ * Object)
+ */
+ public boolean match(Object arpComponent, Object requestComponent)
+ throws MatchingException
+ {
+ if (!(arpComponent instanceof String) || !(requestComponent instanceof String)) {
+ log.error("Invalid use of ARP matching function (StringValueMatchFunction).");
+ throw new MatchingException("Invalid use of ARP matching function (StringValueMatchFunction).");
+ }
+ return arpComponent.equals(requestComponent);
+ }
+}
"data/example8.xml",
"data/example9.xml",
"data/example10.xml",
- "data/example11.xml" };
+ "data/example11.xml",
+ "data/example12.xml" };
public ArpTests(String name) {
super(name);
arpApplicationTest19(repository, props, parser);
arpApplicationTest20(repository, props, parser);
arpApplicationTest21(repository, props, parser);
+ arpApplicationTest22(repository, props, parser);
+ arpApplicationTest23(repository, props, parser);
} catch (Exception e) {
e.printStackTrace();
assertEquals("ARP application test 21: ARP not applied as expected.", inputSet, releaseSet);
}
+ /**
+ * ARPs: A site ARP only
+ * Target: Specific shar, Specific resource
+ * Attribute: Release values by regex
+ */
+ void arpApplicationTest22(ArpRepository repository, Properties props, DOMParser parser) throws Exception {
+
+ //Gather the Input
+ String rawArp =
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ + "<AttributeReleasePolicy xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"urn:mace:shibboleth:arp:1.0\" xsi:schemaLocation=\"urn:mace:shibboleth:arp:1.0 shibboleth-arp-1.0.xsd\">"
+ + " <Rule>"
+ + " <Target>"
+ + " <Requester>shar.example.edu</Requester>"
+ + " <Resource>http://www.example.edu/</Resource>"
+ + " </Target>"
+ + " <Attribute name=\"urn:mace:dir:attribute-def:eduPersonEntitlement\">"
+ + " <Value release=\"permit\" matchFunction=\"urn:mace:shibboleth:arp:matchFunction:regexMatch\">^urn:x:a.+$</Value>"
+ + " </Attribute>"
+ + " </Rule>"
+ + " </AttributeReleasePolicy>";
+
+ Principal principal1 = new AuthNPrincipal("Test2Principal");
+ URL url1 = new URL("http://www.example.edu/index.html");
+ AAAttributeSet inputSet =
+ new AAAttributeSet(
+ new AAAttribute(
+ "urn:mace:dir:attribute-def:eduPersonEntitlement",
+ new Object[] { "urn:x:a", "urn:x:foo", "urn:x:bar", "urn:x:adagio", "urn:x:awol" }));
+ AAAttributeSet releaseSet =
+ new AAAttributeSet(
+ new AAAttribute(
+ "urn:mace:dir:attribute-def:eduPersonEntitlement",
+ new Object[] { "urn:x:adagio", "urn:x:awol" }));
+
+ //Setup the engine
+ parser.parse(new InputSource(new StringReader(rawArp)));
+ Arp siteArp = new Arp();
+ siteArp.marshall(parser.getDocument().getDocumentElement());
+ repository.update(siteArp);
+ ArpEngine engine = new ArpEngine(repository, props);
+
+ //Apply the ARP
+ engine.filterAttributes(inputSet, principal1, "shar.example.edu", url1);
+
+ assertEquals("ARP application test 22: ARP not applied as expected.", inputSet, releaseSet);
+ }
+ /**
+ * ARPs: A site ARP only
+ * Target: Specific shar, Specific resource
+ * Attribute: Deny specific values by regex
+ */
+ void arpApplicationTest23(ArpRepository repository, Properties props, DOMParser parser) throws Exception {
+
+ //Gather the Input
+ String rawArp =
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ + "<AttributeReleasePolicy xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"urn:mace:shibboleth:arp:1.0\" xsi:schemaLocation=\"urn:mace:shibboleth:arp:1.0 shibboleth-arp-1.0.xsd\">"
+ + " <Rule>"
+ + " <Target>"
+ + " <Requester>shar.example.edu</Requester>"
+ + " <Resource>http://www.example.edu/</Resource>"
+ + " </Target>"
+ + " <Attribute name=\"urn:mace:dir:attribute-def:eduPersonEntitlement\">"
+ + " <AnyValue release=\"permit\" />"
+ + " <Value release=\"deny\" matchFunction=\"urn:mace:shibboleth:arp:matchFunction:regexMatch\">^urn:x:a.+$</Value>"
+ + " </Attribute>"
+ + " </Rule>"
+ + " </AttributeReleasePolicy>";
+
+ Principal principal1 = new AuthNPrincipal("Test2Principal");
+ URL url1 = new URL("http://www.example.edu/index.html");
+ AAAttributeSet inputSet =
+ new AAAttributeSet(
+ new AAAttribute(
+ "urn:mace:dir:attribute-def:eduPersonEntitlement",
+ new Object[] { "urn:x:a", "urn:x:foo", "urn:x:bar", "urn:x:adagio", "urn:x:awol" }));
+ AAAttributeSet releaseSet =
+ new AAAttributeSet(
+ new AAAttribute(
+ "urn:mace:dir:attribute-def:eduPersonEntitlement",
+ new Object[] { "urn:x:a", "urn:x:foo", "urn:x:bar" }));
+
+ //Setup the engine
+ parser.parse(new InputSource(new StringReader(rawArp)));
+ Arp siteArp = new Arp();
+ siteArp.marshall(parser.getDocument().getDocumentElement());
+ repository.update(siteArp);
+ ArpEngine engine = new ArpEngine(repository, props);
+
+ //Apply the ARP
+ engine.filterAttributes(inputSet, principal1, "shar.example.edu", url1);
+
+ assertEquals("ARP application test 23: ARP not applied as expected.", inputSet, releaseSet);
+ }
}