Added ARP match functions for string and regular expressions that do not match. ...
[java-idp.git] / src / edu / internet2 / middleware / shibboleth / aa / arp / Rule.java
1 /* 
2  * The Shibboleth License, Version 1. 
3  * Copyright (c) 2002 
4  * University Corporation for Advanced Internet Development, Inc. 
5  * All rights reserved
6  * 
7  * 
8  * Redistribution and use in source and binary forms, with or without 
9  * modification, are permitted provided that the following conditions are met:
10  * 
11  * Redistributions of source code must retain the above copyright notice, this 
12  * list of conditions and the following disclaimer.
13  * 
14  * Redistributions in binary form must reproduce the above copyright notice, 
15  * this list of conditions and the following disclaimer in the documentation 
16  * and/or other materials provided with the distribution, if any, must include 
17  * the following acknowledgment: "This product includes software developed by 
18  * the University Corporation for Advanced Internet Development 
19  * <http://www.ucaid.edu>Internet2 Project. Alternately, this acknowledegement 
20  * may appear in the software itself, if and wherever such third-party 
21  * acknowledgments normally appear.
22  * 
23  * Neither the name of Shibboleth nor the names of its contributors, nor 
24  * Internet2, nor the University Corporation for Advanced Internet Development, 
25  * Inc., nor UCAID may be used to endorse or promote products derived from this 
26  * software without specific prior written permission. For written permission, 
27  * please contact shibboleth@shibboleth.org
28  * 
29  * Products derived from this software may not be called Shibboleth, Internet2, 
30  * UCAID, or the University Corporation for Advanced Internet Development, nor 
31  * may Shibboleth appear in their name, without prior written permission of the 
32  * University Corporation for Advanced Internet Development.
33  * 
34  * 
35  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
36  * AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
37  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
38  * PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK 
39  * OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE. 
40  * IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY 
41  * CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY DIRECT, 
42  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
43  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
44  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 
45  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
46  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
47  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
48  */
49
50 package edu.internet2.middleware.shibboleth.aa.arp;
51
52 import java.net.URI;
53 import java.net.URISyntaxException;
54 import java.net.URL;
55 import java.util.ArrayList;
56 import java.util.HashSet;
57 import java.util.Iterator;
58 import java.util.Set;
59
60 import javax.xml.parsers.DocumentBuilderFactory;
61 import javax.xml.parsers.ParserConfigurationException;
62
63 import org.apache.log4j.Logger;
64 import org.w3c.dom.CharacterData;
65 import org.w3c.dom.Document;
66 import org.w3c.dom.Element;
67 import org.w3c.dom.Node;
68 import org.w3c.dom.NodeList;
69 import org.w3c.dom.Text;
70
71 /**
72  *  An Attribute Release Policy Rule.
73  *
74  * @author Walter Hoehn (wassa@columbia.edu)
75  */
76
77 public class Rule {
78
79         private String description;
80         private Target target;
81         private static Logger log = Logger.getLogger(Rule.class.getName());
82         private ArrayList attributes = new ArrayList();
83         private NodeList attributeReferences;
84
85         private URI identifier;
86
87         /**
88          * Returns the description for this <code>Rule</code>.
89          * @return String
90          */
91
92         public String getDescription() {
93                 return description;
94         }
95
96         /**
97          * Sets the description for this <code>Rule</code>.
98          * @param description The description to set
99          */
100
101         public void setDescription(String description) {
102                 this.description = description;
103         }
104
105         /**
106          * Returns all of the attribute specifications associated with this Rule.
107          * @return the attributes
108          */
109
110         public Attribute[] getAttributes() {
111                 return (Attribute[]) attributes.toArray(new Attribute[0]);
112         }
113
114         /**
115          * Unmarshalls the <code>Rule</code> into an xml <code>Element</code>.
116          * @return the xml <code>Element</code>
117          */
118
119         public Element unmarshall() throws ArpMarshallingException {
120
121                 try {
122                         Document placeHolder = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
123                         Element ruleNode = placeHolder.createElementNS(Arp.arpNamespace, "Rule");
124                         
125                         if (identifier != null) {
126                                 ruleNode.setAttributeNS(Arp.arpNamespace, "identifier", identifier.toString());
127                         }
128
129                         if (description != null) {
130                                 Element descriptionNode = placeHolder.createElementNS(Arp.arpNamespace, "Description");
131                                 descriptionNode.appendChild(placeHolder.createTextNode(description));
132                                 ruleNode.appendChild(descriptionNode);
133                         }
134                         ruleNode.appendChild(placeHolder.importNode(target.unmarshall(), true));
135                         Iterator attrIterator = attributes.iterator();
136                         while (attrIterator.hasNext()) {
137                                 ruleNode.appendChild(
138                                         placeHolder.importNode(((Attribute) attrIterator.next()).unmarshall(), true));
139                         }
140                         
141                         if (attributeReferences != null) {
142                                 for (int i = 0;i < attributeReferences.getLength();i++) {
143                                         ruleNode.appendChild(
144                                         placeHolder.importNode(attributeReferences.item(i), true));
145                                 }       
146                         }
147                         return ruleNode;
148                 } catch (ParserConfigurationException e) {
149                         log.error("Encountered a problem unmarshalling an ARP Rule: " + e);
150                         throw new ArpMarshallingException("Encountered a problem unmarshalling an ARP Rule.");
151                 }
152         }
153
154         /**
155          * Creates an ARP Rule from an xml representation.
156          * @param element the xml <code>Element</code> containing the ARP Rule.
157          */
158
159         public void marshall(Element element) throws ArpMarshallingException {
160
161                 //Make sure we are dealing with a Rule
162                 if (!element.getTagName().equals("Rule")) {
163                         log.error("Element data does not represent an ARP Rule.");
164                         throw new ArpMarshallingException("Element data does not represent an ARP Rule.");
165                 }
166
167                 //Get the rule identifier
168                 try {
169                         if (element.hasAttribute("identifier")) {
170                                 identifier = new URI(element.getAttribute("identifier"));
171                         }
172                 } catch (URISyntaxException e) {
173                         log.error("Rule not identified by a proper URI: " + e);
174                         throw new ArpMarshallingException("Rule not identified by a proper URI.");
175                 }
176
177                 //Grab the description
178                 NodeList descriptionNodes = element.getElementsByTagNameNS(Arp.arpNamespace, "Description");
179                 if (descriptionNodes.getLength() > 0) {
180                         Element descriptionNode = (Element) descriptionNodes.item(0);
181                         if (descriptionNode.hasChildNodes()
182                                 && descriptionNode.getFirstChild().getNodeType() == Node.TEXT_NODE) {
183                                 description = ((CharacterData) descriptionNode.getFirstChild()).getData();
184                         }
185                 }
186
187                 //Create the Target
188                 NodeList targetNodes = element.getElementsByTagNameNS(Arp.arpNamespace, "Target");
189                 if (targetNodes.getLength() != 1) {
190                         log.error(
191                                 "Element data does not represent an ARP Rule.  An ARP Rule must contain 1 and "
192                                         + "only 1 Target definition.");
193                         throw new ArpMarshallingException(
194                                 "Element data does not represent an ARP Rule.  An"
195                                         + " ARP Rule must contain 1 and only 1 Target definition.");
196                 }
197                 target = new Target();
198                 target.marshall((Element) targetNodes.item(0));
199
200                 //Create the Attributes
201                 NodeList attributeNodes = element.getElementsByTagNameNS(Arp.arpNamespace, "Attribute");
202                 for (int i = 0; attributeNodes.getLength() > i; i++) {
203                         Attribute attribute = new Attribute();
204                         attribute.marshall((Element) attributeNodes.item(i));
205                         attributes.add(attribute);
206                 }
207
208                 //Retain Attribute references
209                 //Not enforced!
210                 NodeList attributeReferenceNodes =
211                         element.getElementsByTagNameNS(Arp.arpNamespace, "AttributeReference");
212                 if (attributeReferenceNodes.getLength() > 0) {
213                         log.warn(
214                                 "Encountered an Attribute Reference while marshalling an ARP.  "
215                                         + "References are currently unsupported by the ARP Engine.  Ignoring...");
216                         attributeReferences = attributeReferenceNodes;
217                 }
218         }
219
220         /**
221          * Returns a boolean indication of whether this rule is applicable to a given attribute request.
222          * @param requester the SHAR making the request
223          * @param resource the resource on behalf of which the request is being made
224          */
225
226         public boolean matchesRequest(String requester, URL resource) {
227                 if (target.matchesAny()) {
228                         return true;
229                 }
230                 
231                 if (requester == null) {
232                         return false;
233                 }
234                 
235                 try {
236                         MatchFunction requesterFunction =
237                                 ArpEngine.lookupMatchFunction(target.getRequester().getMatchFunctionIdentifier());
238                         if (!requesterFunction.match(target.getRequester().getValue(), requester)) {
239                                 return false;
240                         }
241                         
242                         if (target.getResource().matchesAny()) {
243                                 return true;
244                         }
245                         
246                         if (resource == null) {
247                                 return false;
248                         }
249                         
250                         MatchFunction resourceFunction =
251                                 ArpEngine.lookupMatchFunction(target.getResource().getMatchFunctionIdentifier());
252                         if (resourceFunction.match(target.getResource().getValue(), resource)) {
253                                 return true;
254                         }
255                         return false;
256                 } catch (ArpException e) {
257                         log.warn("Encountered a problem while trying to find matching ARP rules: " + e);
258                         return false;
259                 }
260         }
261
262         class Target {
263                 private Requester requester = null;
264                 private Resource resource = null;
265                 private boolean matchesAny = false;
266
267                 /**
268                  * Unmarshalls the <code>Rule.Target</code> into an xml <code>Element</code>.
269                  * @return the xml <code>Element</code>
270                  */
271
272                 Element unmarshall() throws ArpMarshallingException {
273
274                         try {
275                                 Document placeHolder =
276                                         DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
277                                 Element targetNode = placeHolder.createElementNS(Arp.arpNamespace, "Target");
278
279                                 if (matchesAny) {
280                                         Element anyTargetNode = placeHolder.createElementNS(Arp.arpNamespace, "AnyTarget");
281                                         targetNode.appendChild(anyTargetNode);
282                                         return targetNode;
283                                 }
284                                 targetNode.appendChild(placeHolder.importNode(requester.unmarshall(), true));
285                                 if (target.resource.matchesAny()) {
286                                         Element anyResourceNode = placeHolder.createElementNS(Arp.arpNamespace, "AnyResource");
287                                         targetNode.appendChild(anyResourceNode);
288                                         return targetNode;
289                                 }
290                                 targetNode.appendChild(placeHolder.importNode(resource.unmarshall(), true));
291                                 return targetNode;
292                         } catch (ParserConfigurationException e) {
293                                 log.error("Encountered a problem unmarshalling an ARP Rule: " + e);
294                                 throw new ArpMarshallingException("Encountered a problem unmarshalling an ARP Rule.");
295                         }
296                 }
297
298                 /**
299                  * Creates an ARP Rule Target from an xml representation.
300                  * @param element the xml <code>Element</code> containing the ARP Rule.
301                  */
302                 void marshall(Element element) throws ArpMarshallingException {
303
304                         //Make sure we are dealing with a Target
305                         if (!element.getTagName().equals("Target")) {
306                                 log.error("Element data does not represent an ARP Rule Target.");
307                                 throw new ArpMarshallingException("Element data does not represent an ARP Rule target.");
308                         }
309
310                         //Handle <AnyTarget/> definitions
311                         NodeList anyTargetNodeList = element.getElementsByTagNameNS(Arp.arpNamespace, "AnyTarget");
312                         if (anyTargetNodeList.getLength() == 1) {
313                                 matchesAny = true;
314                                 return;
315                         }
316
317                         //Create Requester
318                         NodeList requesterNodeList = element.getElementsByTagNameNS(Arp.arpNamespace, "Requester");
319                         if (requesterNodeList.getLength() == 1) {
320                                 requester = new Requester();
321                                 requester.marshall((Element) requesterNodeList.item(0));
322                         } else {
323                                 log.error("ARP Rule Target contains invalid data: incorrectly specified <Requester>.");
324                                 throw new ArpMarshallingException("ARP Rule Target contains invalid data: incorrectly specified <Requester>.");
325                         }
326
327                         //Handle <AnyResource/>
328                         NodeList anyResourceNodeList = element.getElementsByTagNameNS(Arp.arpNamespace, "AnyResource");
329                         if (anyResourceNodeList.getLength() == 1) {
330                                 resource = new Resource();
331                                 return;
332                         }
333
334                         //Create Resource
335                         NodeList resourceNodeList = element.getElementsByTagNameNS(Arp.arpNamespace, "Resource");
336                         if (resourceNodeList.getLength() == 1) {
337                                 resource = new Resource();
338                                 resource.marshall((Element) resourceNodeList.item(0));
339                         } else {
340                                 resource = new Resource();
341                         }
342                 }
343
344                 boolean matchesAny() {
345                         return matchesAny;
346                 }
347                 Requester getRequester() {
348                         return requester;
349                 }
350                 Resource getResource() {
351                         return resource;
352                 }
353         }
354
355         class Resource {
356                 private String value;
357                 private URI matchFunctionIdentifier;
358                 private boolean matchesAny;
359                 Resource() {
360                         matchesAny = true;
361                 }
362                 boolean matchesAny() {
363                         return matchesAny;
364                 }
365                 URI getMatchFunctionIdentifier() {
366                         return matchFunctionIdentifier;
367                 }
368                 String getValue() {
369                         return value;
370                 }
371
372                 /**
373                  * Unmarshalls the <code>Rule.Resource</code> into an xml <code>Element</code>.
374                  * @return the xml <code>Element</code>
375                  */
376
377                 Element unmarshall() throws ArpMarshallingException {
378
379                         try {
380                                 Document placeHolder =
381                                         DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
382                                 Element resourceNode = placeHolder.createElementNS(Arp.arpNamespace, "Resource");
383                                 if (!matchFunctionIdentifier
384                                         .equals(new URI("urn:mace:shibboleth:arp:matchFunction:resourceTree"))) {
385                                         resourceNode.setAttributeNS(Arp.arpNamespace, "matchFunction", matchFunctionIdentifier.toString());
386                                 }
387                                 Text valueNode = placeHolder.createTextNode(value);
388                                 resourceNode.appendChild(valueNode);
389                                 return resourceNode;
390
391                         } catch (URISyntaxException e) {
392                                 log.error("Encountered a problem unmarshalling an ARP Rule Resource: " + e);
393                                 throw new ArpMarshallingException("Encountered a problem unmarshalling an ARP Rule Resource.");
394                         } catch (ParserConfigurationException e) {
395                                 log.error("Encountered a problem unmarshalling an ARP Rule Resource: " + e);
396                                 throw new ArpMarshallingException("Encountered a problem unmarshalling an ARP Rule Resource.");
397                         }
398                 }
399
400                 /**
401                  * Creates an ARP Rule Target Resource from an xml representation.
402                  * @param element the xml <code>Element</code> containing the ARP Rule.
403                  */
404                 void marshall(Element element) throws ArpMarshallingException {
405
406                         matchesAny = false;
407
408                         //Make sure we are deling with a Resource
409                         if (!element.getTagName().equals("Resource")) {
410                                 log.error("Element data does not represent an ARP Rule Target.");
411                                 throw new ArpMarshallingException("Element data does not represent an ARP Rule target.");
412                         }
413
414                         //Grab the value
415                         if (element.hasChildNodes() && element.getFirstChild().getNodeType() == Node.TEXT_NODE) {
416                                 value = ((CharacterData) element.getFirstChild()).getData();
417                         } else {
418                                 log.error("Element data does not represent an ARP Rule Target.");
419                                 throw new ArpMarshallingException("Element data does not represent an ARP Rule target.");
420                         }
421
422                         //Grab the match function
423                         try {
424                                 if (element.hasAttribute("matchFunction")) {
425                                         matchFunctionIdentifier = new URI(element.getAttribute("matchFunction"));
426                                 } else {
427                                         matchFunctionIdentifier = new URI("urn:mace:shibboleth:arp:matchFunction:resourceTree");
428                                 }
429                         } catch (URISyntaxException e) {
430                                 log.error("ARP match function not identified by a proper URI.");
431                                 throw new ArpMarshallingException("ARP match function not identified by a proper URI.");
432                         }
433                 }
434         }
435
436         class Requester {
437                 private String value;
438                 private URI matchFunctionIdentifier;
439                 URI getMatchFunctionIdentifier() {
440                         return matchFunctionIdentifier;
441                 }
442                 String getValue() {
443                         return value;
444                 }
445
446                 /**
447                  * Unmarshalls the <code>Rule.Requester</code> into an xml <code>Element</code>.
448                  * @return the xml <code>Element</code>
449                  */
450
451                 Element unmarshall() throws ArpMarshallingException {
452
453                         try {
454                                 Document placeHolder =
455                                         DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
456                                 Element requesterNode = placeHolder.createElementNS(Arp.arpNamespace, "Requester");
457                                 if (!matchFunctionIdentifier
458                                         .equals(new URI("urn:mace:shibboleth:arp:matchFunction:stringMatch"))) {
459                                         requesterNode.setAttributeNS(Arp.arpNamespace, "matchFunction", matchFunctionIdentifier.toString());
460                                 }
461                                 Text valueNode = placeHolder.createTextNode(value);
462                                 requesterNode.appendChild(valueNode);
463                                 return requesterNode;
464
465                         } catch (URISyntaxException e) {
466                                 log.error("Encountered a problem unmarshalling an ARP Rule Requester: " + e);
467                                 throw new ArpMarshallingException("Encountered a problem unmarshalling an ARP Rule Requester.");
468                         } catch (ParserConfigurationException e) {
469                                 log.error("Encountered a problem unmarshalling an ARP Rule Requester: " + e);
470                                 throw new ArpMarshallingException("Encountered a problem unmarshalling an ARP Rule Requester.");
471                         }
472                 }
473
474                 /**
475                  * Creates an ARP Rule Target Requester from an xml representation.
476                  * @param element the xml <code>Element</code> containing the ARP Rule.
477                  */
478                 void marshall(Element element) throws ArpMarshallingException {
479                         //Make sure we are deling with a Requester
480                         if (!element.getTagName().equals("Requester")) {
481                                 log.error("Element data does not represent an ARP Rule Target.");
482                                 throw new ArpMarshallingException("Element data does not represent an ARP Rule target.");
483                         }
484
485                         //Grab the value
486                         if (element.hasChildNodes() && element.getFirstChild().getNodeType() == Node.TEXT_NODE) {
487                                 value = ((CharacterData) element.getFirstChild()).getData();
488                         } else {
489                                 log.error("Element data does not represent an ARP Rule Target.");
490                                 throw new ArpMarshallingException("Element data does not represent an ARP Rule target.");
491                         }
492
493                         //Grab the match function
494                         try {
495                                 if (element.hasAttribute("matchFunction")) {
496                                         matchFunctionIdentifier = new URI(element.getAttribute("matchFunction"));
497                                 } else {
498                                         matchFunctionIdentifier = new URI("urn:mace:shibboleth:arp:matchFunction:stringMatch");
499                                 }
500                         } catch (URISyntaxException e) {
501                                 log.error("ARP match function not identified by a proper URI.");
502                                 throw new ArpMarshallingException("ARP match function not identified by a proper URI.");
503                         }
504                 }
505         }
506
507         class Attribute {
508                 
509                 private URI name;
510                 private boolean anyValue = false;
511                 private String anyValueRelease = "permit";
512                 private Set values = new HashSet();
513                 private URI identifier;
514
515                 boolean releaseAnyValue() {
516                         if (anyValueRelease.equals("permit")) {
517                                 return anyValue;
518                         }
519                         return false;
520                 }
521
522                 boolean denyAnyValue() {
523                         if (anyValueRelease.equals("deny")) {
524                                 return anyValue;
525                         }
526                         return false;
527                 }
528
529                 void setAnyValueDeny(boolean b) {
530                         if (b) {
531                                 anyValue = true;
532                                 anyValueRelease = "deny";
533                                 values.clear();
534                         } else {
535                                 if (anyValueRelease.equals("deny") && anyValue) {
536                                         anyValue = false;
537                                 }
538                         }
539                 }
540
541                 boolean isValuePermitted(Object value) {
542                         //Handle Deny All
543                         if (denyAnyValue()) {
544                                 return false;
545                         }
546
547                         //Handle Permit All with no specific values
548                         if (releaseAnyValue() && getValues().length == 0) {
549                                 return true;
550                         }
551
552                         //Handle Deny Specific
553                         Iterator iterator = values.iterator();
554                         while (iterator.hasNext()) {
555                                 AttributeValue valueSpec = (AttributeValue) iterator.next();
556
557                                 MatchFunction resourceFunction;
558                                 try {
559                                         resourceFunction = ArpEngine.lookupMatchFunction(valueSpec.getMatchFunctionIdentifier());
560                                         // For safety, err on the side of caution
561                                         if (resourceFunction == null) {
562                                                 log.warn(
563                                                         "Could not locate matching function for ARP value. Function: "
564                                                                 + valueSpec.getMatchFunctionIdentifier().toString());
565                                                 return false;
566                                         }
567
568                                 } catch (ArpException e) {
569                                         log.error("Error while attempting to find referenced matching function for ARP values: " + e);
570                                         return false;
571                                 }
572
573                                 try {
574                                         if (valueSpec.getRelease().equals("deny") && resourceFunction.match(valueSpec.getValue(), value)) {
575                                                 return false;
576                                         }
577                                 } catch (MatchingException e) {
578                                         log.error("Could not apply referenced matching function to ARP value: " + e);
579                                         return false;
580                                 }
581                         }
582
583                         //Handle Permit All with no relevant specific denies
584                         if (releaseAnyValue()) {
585                                 return true;
586                         }
587
588                         //Handle Permit Specific
589                         iterator = values.iterator();
590                         while (iterator.hasNext()) {
591                                 AttributeValue valueSpec = (AttributeValue) iterator.next();
592
593                                 MatchFunction resourceFunction;
594                                 try {
595                                         resourceFunction = ArpEngine.lookupMatchFunction(valueSpec.getMatchFunctionIdentifier());
596                                         // Ignore non-functional permits
597                                         if (resourceFunction == null) {
598                                                 log.warn(
599                                                         "Could not locate matching function for ARP value. Function: "
600                                                                 + valueSpec.getMatchFunctionIdentifier().toString());
601                                                 continue;
602                                         }
603
604                                 } catch (ArpException e) {
605                                         log.error("Error while attempting to find referenced matching function for ARP values: " + e);
606                                         continue;
607                                 }
608
609                                 try {
610                                         if (valueSpec.getRelease().equals("permit")
611                                                 && resourceFunction.match(valueSpec.getValue(), value)) {
612                                                 return true;
613                                         }
614                                 } catch (MatchingException e) {
615                                         log.error("Could not apply referenced matching function to ARP value: " + e);
616                                         continue;
617                                 }
618                         }
619                         return false;
620                 }
621
622                 void setAnyValuePermit(boolean b) {
623                         if (b) {
624                                 anyValue = true;
625                                 anyValueRelease = "permit";
626                                 Iterator iterator = values.iterator();
627                                 HashSet permittedValues = new HashSet();
628                                 while (iterator.hasNext()) {
629                                         AttributeValue value = (AttributeValue) iterator.next();
630                                         if (value.getRelease().equals("permit")) {
631                                                 permittedValues.add(value);
632                                         }
633                                 }
634                                 values.removeAll(permittedValues);
635                         } else {
636                                 if (anyValueRelease.equals("permit") && anyValue) {
637                                         anyValue = false;
638                                 }
639                         }
640                 }
641
642                 URI getName() {
643                         return name;
644                 }
645                 AttributeValue[] getValues() {
646                         return (AttributeValue[]) values.toArray(new AttributeValue[0]);
647                 }
648
649                 void addValue(AttributeValue value) {
650                         if (denyAnyValue()) {
651                                 return;
652                         }
653                         if (releaseAnyValue() && value.getRelease().equals("permit")) {
654                                 return;
655                         }
656                         values.add(value);
657                 }
658
659                 /**
660                  * Unmarshalls an <code>Attribute</code> into an xml <code>Element</code>.
661                  * @return the xml <code>Element</code>
662                  */
663
664                 Element unmarshall() throws ArpMarshallingException {
665
666                         try {
667                                 Document placeHolder = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
668                                 Element attributeNode = placeHolder.createElementNS(Arp.arpNamespace, "Attribute");
669
670                                 attributeNode.setAttributeNS(Arp.arpNamespace, "name", name.toString());
671
672                                 if (identifier != null) {
673                                         attributeNode.setAttributeNS(Arp.arpNamespace, "identifier", identifier.toString());
674                                 }
675
676                                 if (anyValue) {
677                                         Element anyValueNode = placeHolder.createElementNS(Arp.arpNamespace, "AnyValue");
678                                         anyValueNode.setAttributeNS(Arp.arpNamespace, "release", anyValueRelease);
679                                         attributeNode.appendChild(anyValueNode);
680                                 }
681                                 Iterator valueIterator = values.iterator();
682                                 while (valueIterator.hasNext()) {
683                                         AttributeValue value = (AttributeValue) valueIterator.next();
684                                         Element valueNode = placeHolder.createElementNS(Arp.arpNamespace, "Value");
685                                         valueNode.setAttributeNS(Arp.arpNamespace, "release", value.getRelease());
686                                         if (!value
687                                                 .getMatchFunctionIdentifier()
688                                                 .equals(new URI("urn:mace:shibboleth:arp:matchFunction:stringMatch"))) {
689                                                 valueNode.setAttributeNS(
690                                                         Arp.arpNamespace,
691                                                         "matchFunction",
692                                                         value.getMatchFunctionIdentifier().toString());
693                                         }
694                                         Text valueTextNode = placeHolder.createTextNode(value.getValue());
695                                         valueNode.appendChild(valueTextNode);
696                                         attributeNode.appendChild(valueNode);
697                                 }
698                                 return attributeNode;
699
700                         } catch (URISyntaxException e) {
701                                 log.error("Encountered a problem unmarshalling an ARP Rule Resource: " + e);
702                                 throw new ArpMarshallingException("Encountered a problem unmarshalling an ARP Rule Resource.");
703                         } catch (ParserConfigurationException e) {
704                                 log.error("Encountered a problem unmarshalling an ARP Rule: " + e);
705                                 throw new ArpMarshallingException("Encountered a problem unmarshalling an ARP Rule.");
706                         }
707                 }
708                 
709                 /**
710                  * Creates an ARP Rule Attribute from an xml representation.
711                  * @param element the xml <code>Element</code> containing the ARP Rule.
712                  */
713                 void marshall(Element element) throws ArpMarshallingException {
714                         //Make sure we are dealing with an Attribute
715                         if (!element.getTagName().equals("Attribute")) {
716                                 log.error("Element data does not represent an ARP Rule Target.");
717                                 throw new ArpMarshallingException("Element data does not represent an ARP Rule target.");
718                         }
719
720                         //Get the attribute identifier
721                         try {
722                                 if (element.hasAttribute("identifier")) {
723                                         identifier = new URI(element.getAttribute("identifier"));
724                                 }
725                         } catch (URISyntaxException e) {
726                                 log.error("Attribute not identified by a proper URI: " + e);
727                                 throw new ArpMarshallingException("Attribute not identified by a proper URI.");
728                         }
729
730                         //Get the attribute name
731                         try {
732                                 if (element.hasAttribute("name")) {
733                                         name = new URI(element.getAttribute("name"));
734                                 } else {
735                                         log.error("Attribute name not specified.");
736                                         throw new ArpMarshallingException("Attribute name not specified.");
737                                 }
738                         } catch (URISyntaxException e) {
739                                 log.error("Attribute name not identified by a proper URI: " + e);
740                                 throw new ArpMarshallingException("Attribute name not identified by a proper URI.");
741                         }
742
743                         //Handle <AnyValue/> definitions
744                         NodeList anyValueNodeList = element.getElementsByTagNameNS(Arp.arpNamespace, "AnyValue");
745                         if (anyValueNodeList.getLength() == 1) {
746                                 anyValue = true;
747                                 if (((Element) anyValueNodeList.item(0)).hasAttribute("release")) {
748                                         anyValueRelease = ((Element) anyValueNodeList.item(0)).getAttribute("release");
749                                 }
750                         }
751
752                         //Handle Value definitions
753                         if (!denyAnyValue()) {
754                                 NodeList valueNodeList = element.getElementsByTagNameNS(Arp.arpNamespace, "Value");
755                                 for (int i = 0; valueNodeList.getLength() > i; i++) {
756                                         String release = null;
757                                         String value = null;
758                                         URI matchFunctionIdentifier = null;
759                                         if (((Element) valueNodeList.item(i)).hasAttribute("release")) {
760                                                 release = ((Element) valueNodeList.item(i)).getAttribute("release");
761                                         }
762
763                                         //Grab the match function
764                                         try {
765                                                 if (((Element) valueNodeList.item(i)).hasAttribute("matchFunction")) {
766                                                         matchFunctionIdentifier =
767                                                                 new URI(((Element) valueNodeList.item(i)).getAttribute("matchFunction"));
768                                                 }
769                                         } catch (URISyntaxException e) {
770                                                 log.error(
771                                                         "ARP match function not identified by a proper URI: "
772                                                                 + ((Element) valueNodeList.item(i)).getAttribute("matchFunction"));
773                                                 throw new ArpMarshallingException("ARP match function not identified by a proper URI.");
774                                         }
775
776                                         if (((Element) valueNodeList.item(i)).hasChildNodes()
777                                                 && ((Element) valueNodeList.item(i)).getFirstChild().getNodeType() == Node.TEXT_NODE) {
778                                                 value = ((CharacterData) ((Element) valueNodeList.item(i)).getFirstChild()).getData();
779                                         }
780                                         if (releaseAnyValue() && release.equals("permit")) {
781                                                 continue;
782                                         }
783                                         AttributeValue aValue = new AttributeValue(release, matchFunctionIdentifier, value);
784                                         values.add(aValue);
785                                 }
786                         }
787
788                 }
789         }
790         class AttributeValue {
791
792                 private String release = "permit";
793                 private String value;
794                 private URI matchFunctionIdentifier;
795
796                 AttributeValue(String release, URI matchFunctionIdentifier, String value) throws ArpMarshallingException {
797                         setRelease(release);
798                         this.value = value;
799                         if (matchFunctionIdentifier != null) {
800                                 this.matchFunctionIdentifier = matchFunctionIdentifier;
801                         } else {
802                                 try {
803                                         matchFunctionIdentifier = new URI("urn:mace:shibboleth:arp:matchFunction:stringMatch");
804                                 } catch (URISyntaxException e) {
805                                         throw new ArpMarshallingException("ARP Engine internal error: could not set default matching function for attribute value.");
806                                 }
807                         }
808                 }
809
810                 String getRelease() {
811                         return release;
812                 }
813
814                 String getValue() {
815                         return value;
816                 }
817
818                 URI getMatchFunctionIdentifier() {
819                         return matchFunctionIdentifier;
820                 }
821
822                 void setRelease(String release) {
823                         if (release == null) {
824                                 return;
825                         }
826                         if (release.equals("permit") || release.equals("deny")) {
827                                 this.release = release;
828                         }
829                 }
830
831                 void setValue(String value) {
832                         this.value = value;
833                 }
834         }
835
836 }