ee0b004c1275b8ec721fe351604b281a71f1e73e
[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.Set;
58
59 import org.apache.log4j.Logger;
60 import org.apache.xerces.parsers.DOMParser;
61 import org.w3c.dom.CharacterData;
62 import org.w3c.dom.Document;
63 import org.w3c.dom.Element;
64 import org.w3c.dom.Node;
65 import org.w3c.dom.NodeList;
66
67 /**
68  *  An Attribute Release Policy Rule.
69  *
70  * @author Walter Hoehn (wassa@columbia.edu)
71  */
72
73 public class Rule {
74
75         private String description;
76         private Target target;
77         private static Logger log = Logger.getLogger(Rule.class.getName());
78         private ArrayList attributes = new ArrayList();
79
80         /**
81          * Returns the description for this <code>Rule</code>.
82          * @return String
83          */
84
85         public String getDescription() {
86                 return description;
87         }
88
89         /**
90          * Sets the description for this <code>Rule</code>.
91          * @param description The description to set
92          */
93
94         public void setDescription(String description) {
95                 this.description = description;
96         }
97
98         /**
99          * Unmarshalls the <code>Rule</code> into an xml <code>Element</code>.
100          * @return the xml <code>Element</code>
101          */
102         
103         public Attribute[] getAttributes() {
104                 return (Attribute[]) attributes.toArray(new Attribute[0]);      
105         }
106
107         public Element unmarshall() {
108
109                 DOMParser parser = new DOMParser();
110                 Document placeHolder = parser.getDocument();
111                 Element ruleNode = placeHolder.createElement("Rule");
112
113                 if (description != null) {
114                         Element descriptionNode = placeHolder.createElement("Description");
115                         descriptionNode.appendChild(placeHolder.createTextNode(description));
116                         ruleNode.appendChild(descriptionNode);
117                 }
118
119                 return ruleNode;
120         }
121
122         /**
123          * Creates an ARP Rule from an xml representation.
124          * @param the xml <code>Element</code> containing the ARP Rule.
125          */
126
127         public void marshall(Element element) throws ArpMarshallingException {
128
129                 //Make sure we are dealing with a Rule
130                 if (!element.getTagName().equals("Rule")) {
131                         log.error("Element data does not represent an ARP Rule.");
132                         throw new ArpMarshallingException("Element data does not represent an ARP Rule.");
133                 }
134
135                 //Grab the description
136                 NodeList descriptionNodes = element.getElementsByTagName("Description");
137                 if (descriptionNodes.getLength() > 0) {
138                         Element descriptionNode = (Element) descriptionNodes.item(0);
139                         if (descriptionNode.hasChildNodes()
140                                 && descriptionNode.getFirstChild().getNodeType() == Node.TEXT_NODE) {
141                                 description = ((CharacterData) descriptionNode.getFirstChild()).getData();
142                         }
143                 }
144
145                 //Create the Target
146                 NodeList targetNodes = element.getElementsByTagName("Target");
147                 if (targetNodes.getLength() != 1) {
148                         log.error(
149                                 "Element data does not represent an ARP Rule.  An ARP Rule must contain 1 and "
150                                         + "only 1 Target definition.");
151                         throw new ArpMarshallingException(
152                                 "Element data does not represent an ARP Rule.  An"
153                                         + " ARP Rule must contain 1 and only 1 Target definition.");
154                 }
155                 target = new Target();
156                 target.marshall((Element) targetNodes.item(0));
157
158                 //Create the Attributes
159                 NodeList attributeNodes = element.getElementsByTagName("Attribute");
160                 for (int i = 0; attributeNodes.getLength() > i; i++) {
161                         Attribute attribute = new Attribute();
162                         attribute.marshall((Element) attributeNodes.item(i));
163                         attributes.add(attribute);
164                 }
165         }
166
167         /**
168          * Method matchesRequest.
169          * @param requester
170          * @param resource
171          * @return boolean
172          */
173         public boolean matchesRequest(String requester, URL resource) {
174                 if (target.matchesAny()) {
175                         return true;
176                 }
177                 try {
178                         MatchFunction requesterFunction =
179                                 ArpEngine.lookupMatchFunction(target.getRequester().getMatchFunctionIdentifier());
180                         if (!requesterFunction.match(target.getRequester().getValue(), requester)) {
181                                 return false;
182                         }
183                         if (target.getResource().matchesAny()) {
184                                 return true;
185                         }
186                         MatchFunction resourceFunction =
187                                 ArpEngine.lookupMatchFunction(target.getResource().getMatchFunctionIdentifier());
188                         if (resourceFunction.match(target.getResource().getValue(), resource)) {
189                                 return true;
190                         }
191                         return false;
192                 } catch (ArpException e) {
193                         log.warn("Encountered a problem while trying to find matching ARP rules: " + e);
194                         return false;
195                 }
196         }
197
198         class Target {
199                 private Requester requester = null;
200                 private Resource resource = null;
201                 private boolean matchesAny = false;
202
203                 void marshall(Element element) throws ArpMarshallingException {
204
205                         //Make sure we are dealing with a Target
206                         if (!element.getTagName().equals("Target")) {
207                                 log.error("Element data does not represent an ARP Rule Target.");
208                                 throw new ArpMarshallingException("Element data does not represent an ARP Rule target.");
209                         }
210
211                         //Handle <AnyTarget/> definitions
212                         NodeList anyTargetNodeList = element.getElementsByTagName("AnyTarget");
213                         if (anyTargetNodeList.getLength() == 1) {
214                                 matchesAny = true;
215                                 return;
216                         }
217
218                         //Create Requester
219                         NodeList requesterNodeList = element.getElementsByTagName("Requester");
220                         if (requesterNodeList.getLength() == 1) {
221                                 requester = new Requester();
222                                 requester.marshall((Element) requesterNodeList.item(0));
223                         } else {
224                                 log.error("ARP Rule Target contains invalid data: incorrectly specified <Requester>.");
225                                 throw new ArpMarshallingException("ARP Rule Target contains invalid data: incorrectly specified <Requester>.");
226                         }
227
228                         //Handle <AnyResource/>
229                         NodeList anyResourceNodeList = element.getElementsByTagName("AnyResource");
230                         if (anyResourceNodeList.getLength() == 1) {
231                                 resource = new Resource();
232                                 return;
233                         }
234
235                         //Create Resource
236                         NodeList resourceNodeList = element.getElementsByTagName("Resource");
237                         if (resourceNodeList.getLength() == 1) {
238                                 resource = new Resource();
239                                 resource.marshall((Element) resourceNodeList.item(0));
240                         } else {
241                                 log.error("ARP Rule Target contains invalid data: incorrectly specified <Resource>.");
242                                 throw new ArpMarshallingException("ARP Rule Target contains invalid data: incorrectly specified <Resource>.");
243                         }
244                 }
245
246                 boolean matchesAny() {
247                         return matchesAny;
248                 }
249                 Requester getRequester() {
250                         return requester;
251                 }
252                 Resource getResource() {
253                         return resource;
254                 }
255         }
256
257         class Resource {
258                 private String value;
259                 private URI matchFunctionIdentifier;
260                 private boolean matchesAny;
261                 Resource() {
262                         matchesAny = true;
263                 }
264                 boolean matchesAny() {
265                         return matchesAny;
266                 }
267                 URI getMatchFunctionIdentifier() {
268                         return matchFunctionIdentifier;
269                 }
270                 String getValue() {
271                         return value;
272                 }
273                 void marshall(Element element) throws ArpMarshallingException {
274                         //Make sure we are deling with a Resource
275                         if (!element.getTagName().equals("Resource")) {
276                                 log.error("Element data does not represent an ARP Rule Target.");
277                                 throw new ArpMarshallingException("Element data does not represent an ARP Rule target.");
278                         }
279
280                         //Grab the value
281                         if (element.hasChildNodes() && element.getFirstChild().getNodeType() == Node.TEXT_NODE) {
282                                 value = ((CharacterData) element.getFirstChild()).getData();
283                         } else {
284                                 log.error("Element data does not represent an ARP Rule Target.");
285                                 throw new ArpMarshallingException("Element data does not represent an ARP Rule target.");
286                         }
287
288                         //Grab the match function
289                         try {
290                                 if (element.hasAttribute("matchFunction")) {
291                                         matchFunctionIdentifier = new URI(element.getAttribute("matchFunction"));
292                                 } else {
293                                         matchFunctionIdentifier = new URI("urn:mace:shibboleth:arp:matchFunction:resourceTree");
294                                 }
295                         } catch (URISyntaxException e) {
296                                 log.error("ARP match function not identified by a proper URI.");
297                                 throw new ArpMarshallingException("ARP match function not identified by a proper URI.");
298                         }
299                 }
300         }
301
302         class Requester {
303                 private String value;
304                 private URI matchFunctionIdentifier;
305                 URI getMatchFunctionIdentifier() {
306                         return matchFunctionIdentifier;
307                 }
308                 String getValue() {
309                         return value;
310                 }
311                 void marshall(Element element) throws ArpMarshallingException {
312                         //Make sure we are deling with a Requester
313                         if (!element.getTagName().equals("Requester")) {
314                                 log.error("Element data does not represent an ARP Rule Target.");
315                                 throw new ArpMarshallingException("Element data does not represent an ARP Rule target.");
316                         }
317
318                         //Grab the value
319                         if (element.hasChildNodes() && element.getFirstChild().getNodeType() == Node.TEXT_NODE) {
320                                 value = ((CharacterData) element.getFirstChild()).getData();
321                         } else {
322                                 log.error("Element data does not represent an ARP Rule Target.");
323                                 throw new ArpMarshallingException("Element data does not represent an ARP Rule target.");
324                         }
325
326                         //Grab the match function
327                         try {
328                                 if (element.hasAttribute("matchFunction")) {
329                                         matchFunctionIdentifier = new URI(element.getAttribute("matchFunction"));
330                                 } else {
331                                         matchFunctionIdentifier = new URI("urn:mace:shibboleth:arp:matchFunction:exactShar");
332                                 }
333                         } catch (URISyntaxException e) {
334                                 log.error("ARP match function not identified by a proper URI.");
335                                 throw new ArpMarshallingException("ARP match function not identified by a proper URI.");
336                         }
337                 }
338         }
339
340         class Attribute {
341                 private URI name;
342                 private boolean anyValue = false;
343                 private String anyValueRelease = "permit";
344                 private Set values = new HashSet();
345
346                 boolean releaseAnyValue() {
347                         if (anyValueRelease.equals("permit")) {
348                                 return anyValue;
349                         }
350                         return false;
351                 }
352                 
353                 boolean denyAnyValue() {
354                         if (anyValueRelease.equals("deny")) {
355                                 return anyValue;
356                         }
357                         return false;
358                 }
359                 
360                 URI getName() {
361                         return name;    
362                 }
363                 AttributeValue[] getValues() {
364                         return (AttributeValue[]) values.toArray(new AttributeValue[0]);        
365                 }
366
367                 void marshall(Element element) throws ArpMarshallingException {
368                         //Make sure we are dealing with an Attribute
369                         if (!element.getTagName().equals("Attribute")) {
370                                 log.error("Element data does not represent an ARP Rule Target.");
371                                 throw new ArpMarshallingException("Element data does not represent an ARP Rule target.");
372                         }
373
374                         //Get the attribute name
375                         try {
376                                 if (element.hasAttribute("name")) {
377                                         name = new URI(element.getAttribute("name"));
378                                 } else {
379                                         log.error("Attribute name not specified.");
380                                         throw new ArpMarshallingException("Attribute name not specified.");
381                                 }
382                         } catch (URISyntaxException e) {
383                                 log.error("Attribute name not identified by a proper URI: " + e);
384                                 throw new ArpMarshallingException("Attribute name not identified by a proper URI.");
385                         }
386
387                                 //Handle <AnyValue/> definitions
388                                 NodeList anyValueNodeList = element.getElementsByTagName("AnyValue");
389                                 if (anyValueNodeList.getLength() == 1) {
390                                         anyValue = true;
391                                         if (((Element) anyValueNodeList.item(0)).hasAttribute("release")) {
392                                                 anyValueRelease = ((Element) anyValueNodeList.item(0)).getAttribute("release");
393                                         }
394                                 }
395
396                                 //Handle Value definitions
397                                 NodeList valueNodeList = element.getElementsByTagName("Value");
398                                 for (int i = 0; valueNodeList.getLength() > i; i++) {
399                                         String release = null;
400                                         String value = null;
401                                         if (((Element) valueNodeList.item(i)).hasAttribute("release")) {
402                                                 release = ((Element) valueNodeList.item(i)).getAttribute("release");
403                                         }
404                                         if (((Element) valueNodeList.item(i)).hasChildNodes()
405                                                 && ((Element) valueNodeList.item(i)).getFirstChild().getNodeType() == Node.TEXT_NODE) {
406                                                 value = ((CharacterData) ((Element) valueNodeList.item(i)).getFirstChild()).getData();
407                                         }
408                                         AttributeValue aValue = new AttributeValue(release, value);
409                                         values.add(aValue);
410                                 }
411
412                         }
413         }
414         class AttributeValue {
415                 private String release = "permit";
416                 private String value;
417
418                 AttributeValue(String release, String value) {
419                         setRelease(release);
420                         this.value = value;
421                 }
422
423                 String getRelease() {
424                         return release;
425                 }
426
427                 String getValue() {
428                         return value;
429                 }
430
431                 void setRelease(String release) {
432                         if (release == null) {
433                                 return;
434                         }
435                         if (release.equals("permit") || release.equals("deny")) {
436                                 this.release = release;
437                         }
438                 }
439
440                 void setValue(String value) {
441                         this.value = value;
442                 }
443         }
444
445 }