Failsafe in case folks have problems validating without xsi:type on attribute values.
[java-idp.git] / src / edu / internet2 / middleware / shibboleth / aa / AAAttribute.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;
51
52
53 import java.util.ArrayList;
54 import java.util.Arrays;
55 import java.util.Collection;
56 import java.util.Iterator;
57 import java.util.List;
58
59 import org.apache.log4j.Logger;
60 import org.opensaml.QName;
61 import org.opensaml.SAMLAttribute;
62 import org.opensaml.SAMLException;
63 import org.opensaml.XML;
64 import org.w3c.dom.Document;
65 import org.w3c.dom.Element;
66 import org.w3c.dom.Node;
67
68 import edu.internet2.middleware.shibboleth.aa.arp.ArpAttribute;
69 import edu.internet2.middleware.shibboleth.aa.attrresolv.ResolverAttribute;
70 import edu.internet2.middleware.shibboleth.aa.attrresolv.provider.ValueHandler;
71 import edu.internet2.middleware.shibboleth.aa.attrresolv.provider.ValueHandlerException;
72
73 /**
74  * An attribute for which the Shibboleth Attribute Authority has been asked
75  * to provide an assertion.
76  * 
77  * @author Walter Hoehn (wassa@columbia.edu)
78  */
79 public class AAAttribute extends SAMLAttribute implements ResolverAttribute, ArpAttribute {
80
81         private static Logger log = Logger.getLogger(AAAttribute.class.getName());
82         private boolean resolved = false;
83         /** A hedge in case we are wrong about no longer needing xsi:type.  Remove this, if possible, in 1.3. **/
84         static boolean typeHack = false;
85         
86         public final static String SHIB_ATTRIBUTE_NAMESPACE_URI = "urn:mace:shibboleth:1.0:attributeNamespace:uri";
87         
88         /** Default lifetime, in seconds **/
89         private static long defaultLifetime = 1800; // 30 minutes
90         private ValueHandler valueHandler = new StringValueHandler();
91
92         public AAAttribute(String name) throws SAMLException {
93                 super(
94                         name,
95                         SHIB_ATTRIBUTE_NAMESPACE_URI,
96                         !typeHack? null: new QName("urn:mace:shibboleth:1.0", "AttributeValueType"),
97                         defaultLifetime,
98                         null);
99         }
100
101         public AAAttribute(String name, Object[] values) throws SAMLException {
102                 this(name);
103                 setValues(values);
104         }
105         
106         public AAAttribute(String name, Object[] values, ValueHandler handler) throws SAMLException {
107                         this(name);
108                         setValues(values);
109                         registerValueHandler(handler);
110                 }
111
112         public boolean hasValues() {
113                 if (values.isEmpty()) {
114                         return false;
115                 }
116                 return true;
117         }
118
119         public Iterator getValues() {
120                 return valueHandler.getValues(values);
121         }
122
123         public void setValues(Object[] values) {
124                 if (!this.values.isEmpty()) {
125                         this.values.clear();
126                 }
127                 List newList = Arrays.asList(values);
128                 if (newList.contains(null)) {
129                         newList.remove(null);
130                 }
131                 this.values.addAll(newList);
132         }
133
134         /**
135         * @see java.lang.Object#hashCode()
136         */
137         public int hashCode() {
138                 int code = 0;
139                 if (values != null) {
140                         Iterator iterator = values.iterator();
141                         while (iterator.hasNext()) {
142                                 code += iterator.next().hashCode();
143                         }
144                 }
145                 return name.hashCode() + code;
146         }
147
148         /**
149          * @see edu.internet2.middleware.shibboleth.aa.attrresolv.ArpAttribute#resolved()
150          */
151         public boolean resolved() {
152                 return resolved;
153         }
154
155         /**
156          * @see edu.internet2.middleware.shibboleth.aa.attrresolv.ArpAttribute#setResolved()
157          */
158         public void setResolved() {
159                 resolved = true;
160         }
161
162         /**
163          * @see edu.internet2.middleware.shibboleth.aa.attrresolv.ArpAttribute#resolveFromCached(edu.internet2.middleware.shibboleth.aa.attrresolv.ArpAttribute)
164          */
165         public void resolveFromCached(ResolverAttribute attribute) {
166                 resolved = true;
167                 setLifetime(attribute.getLifetime());
168
169                 if (!this.values.isEmpty()) {
170                         this.values.clear();
171                 }
172                 for (Iterator iterator = attribute.getValues(); iterator.hasNext();) {
173                         values.add(iterator.next());
174                 }
175
176                 registerValueHandler(attribute.getRegisteredValueHandler());
177         }
178
179         public void setLifetime(long lifetime) {
180                 this.lifetime = lifetime;
181
182         }
183
184         public void addValue(Object value) {
185                 if (value != null) {
186                         values.add(value);
187                 }
188         }
189
190         /**
191          * @see org.opensaml.SAMLObject#toDOM(org.w3c.dom.Document)
192          */
193         public Node toDOM(Document doc, boolean xmlns) {
194
195                 Element attributeElement = doc.createElementNS(XML.SAML_NS, "Attribute");
196                 attributeElement.setAttributeNS(null, "AttributeName", name);
197                 attributeElement.setAttributeNS(null, "AttributeNamespace", namespace);
198
199                 for (int i = 0; i < values.size(); i++) {
200
201                         if (type != null) {
202                                 attributeElement.setAttributeNS(XML.XMLNS_NS, "xmlns:typens", type.getNamespaceURI());
203                         }
204                         Element valueElement = doc.createElementNS(XML.SAML_NS, "AttributeValue");
205                         if (type != null) {
206                                 valueElement.setAttributeNS(XML.XSI_NS, "xsi:type", "typens:" + type.getLocalName());
207                         }
208                         
209                         try {
210                                 valueHandler.toDOM(valueElement, values.get(i), doc);
211                                 attributeElement.appendChild(valueElement);
212
213                         } catch (ValueHandlerException e) {
214                                 log.error("Value Handler unable to convert value to DOM Node: " + e);
215                         }
216                 }
217                 return attributeElement;
218         }
219
220         /**
221          * @see edu.internet2.middleware.shibboleth.aa.attrresolv.ArpAttribute#registerValueHandler(edu.internet2.middleware.shibboleth.aa.attrresolv.provider.ValueHandler)
222          */
223         public void registerValueHandler(ValueHandler handler) {
224                 valueHandler = handler;
225         }
226
227         /**
228          * @see edu.internet2.middleware.shibboleth.aa.attrresolv.ArpAttribute#getRegisteredValueHandler()
229          */
230         public ValueHandler getRegisteredValueHandler() {
231                 return valueHandler;
232         }
233
234         /**
235          * @see java.lang.Object#equals(java.lang.Object)
236          */
237         public boolean equals(Object object) {
238
239                 if (!(object instanceof AAAttribute)) {
240                         return false;
241                 }
242                 if (lifetime != ((AAAttribute) object).lifetime) {
243                         return false;
244                 }
245                 if (name != ((AAAttribute) object).name) {
246                         return false;
247                 }
248                 if (!valueHandler.equals(((AAAttribute) object).valueHandler)) {
249                         return false;
250                 }
251                 
252                 ArrayList localValues = new ArrayList();
253                 for (Iterator iterator = getValues();iterator.hasNext();) {
254                         localValues.add(iterator.next());
255                 }
256                 
257                 ArrayList objectValues = new ArrayList();
258                 for (Iterator iterator = ((AAAttribute) object).getValues();iterator.hasNext();) {
259                         objectValues.add(iterator.next());
260                 }
261                 
262                 return localValues.equals(objectValues);
263         }
264
265 }
266
267 /**
268  *  Default <code>ValueHandler</code> implementation.  Expects all values to be String objects.
269  *
270  * @author Walter Hoehn (wassa@columbia.edu)
271  */
272 class StringValueHandler implements ValueHandler {
273
274         public void toDOM(Element valueElement, Object value, Document document) {
275                 valueElement.appendChild(document.createTextNode(value.toString()));
276         }
277
278         public Iterator getValues(Collection internalValues) {
279                 return internalValues.iterator();
280         }
281
282         /**
283          * @see java.lang.Object#equals(java.lang.Object)
284          */
285         public boolean equals(Object object) {
286                 if (object instanceof StringValueHandler) {
287                         return true;
288                 }
289                 return false;
290         }
291
292 }
293
294