Added option to Simple Attribute Definition PlugIn to force values to lower case...
[java-idp.git] / src / edu / internet2 / middleware / shibboleth / aa / attrresolv / provider / SimpleAttributeDefinition.java
1 /*
2  * The Shibboleth License, Version 1. Copyright (c) 2002 University Corporation for Advanced Internet Development, Inc.
3  * All rights reserved Redistribution and use in source and binary forms, with or without modification, are permitted
4  * provided that the following conditions are met: Redistributions of source code must retain the above copyright
5  * notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above
6  * copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials
7  * provided with the distribution, if any, must include the following acknowledgment: "This product includes software
8  * developed by the University Corporation for Advanced Internet Development <http://www.ucaid.edu>Internet2 Project.
9  * Alternately, this acknowledegement may appear in the software itself, if and wherever such third-party
10  * acknowledgments normally appear. Neither the name of Shibboleth nor the names of its contributors, nor Internet2, nor
11  * the University Corporation for Advanced Internet Development, Inc., nor UCAID may be used to endorse or promote
12  * products derived from this software without specific prior written permission. For written permission, please contact
13  * shibboleth@shibboleth.org Products derived from this software may not be called Shibboleth, Internet2, UCAID, or the
14  * University Corporation for Advanced Internet Development, nor may Shibboleth appear in their name, without prior
15  * written permission of the University Corporation for Advanced Internet Development. THIS SOFTWARE IS PROVIDED BY THE
16  * COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE
18  * DISCLAIMED AND THE ENTIRE RISK OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE. IN NO
19  * EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC.
20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
23  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 package edu.internet2.middleware.shibboleth.aa.attrresolv.provider;
27
28 import java.security.Principal;
29 import java.util.Arrays;
30 import java.util.Iterator;
31 import java.util.LinkedHashSet;
32 import java.util.Set;
33
34 import javax.naming.NamingEnumeration;
35 import javax.naming.NamingException;
36 import javax.naming.directory.Attribute;
37 import javax.naming.directory.Attributes;
38
39 import org.apache.log4j.Logger;
40 import org.w3c.dom.Element;
41
42 import edu.internet2.middleware.shibboleth.aa.attrresolv.AttributeDefinitionPlugIn;
43 import edu.internet2.middleware.shibboleth.aa.attrresolv.Dependencies;
44 import edu.internet2.middleware.shibboleth.aa.attrresolv.ResolutionPlugInException;
45 import edu.internet2.middleware.shibboleth.aa.attrresolv.ResolverAttribute;
46
47 /**
48  * Basic <code>AttributeDefinitionPlugIn</code> implementation. Operates as a proxy for attributes gathered by
49  * Connectors.
50  * 
51  * @author Walter Hoehn (wassa@columbia.edu)
52  */
53 public class SimpleAttributeDefinition extends BaseAttributeDefinition implements AttributeDefinitionPlugIn {
54
55         private static Logger log = Logger.getLogger(SimpleAttributeDefinition.class.getName());
56         private String connectorMapping;
57         private String smartScope;
58         private ValueHandler valueHandler;
59         private boolean allowEmpty = false;
60         private boolean downCase = false;
61
62         /**
63          * Constructor for SimpleAttributeDefinition. Creates a PlugIn based on configuration information presented in a DOM
64          * Element.
65          */
66         public SimpleAttributeDefinition(Element e) throws ResolutionPlugInException {
67
68                 super(e);
69
70                 //Parse source name
71                 String sourceName = e.getAttribute("sourceName");
72                 if (sourceName == null || sourceName.equals("")) {
73                         int index = getId().lastIndexOf("#");
74                         if (index < 0) {
75                                 index = getId().lastIndexOf(":");
76                                 int slashIndex = getId().lastIndexOf("/");
77                                 if (slashIndex > index) {
78                                         index = slashIndex;
79                                 }
80                         }
81                         connectorMapping = getId().substring(index + 1);
82                 } else {
83                         connectorMapping = sourceName;
84                 }
85
86                 log.debug("Mapping attribute to name (" + connectorMapping + ") in connector.");
87
88                 //Configure smart scoping
89                 String smartScopingSpec = e.getAttribute("smartScope");
90                 if (smartScopingSpec != null && !smartScopingSpec.equals("")) {
91                         smartScope = smartScopingSpec;
92                 }
93                 if (smartScope != null) {
94                         log.debug("Smart Scope (" + smartScope + ") enabled for attribute (" + getId() + ").");
95                 } else {
96                         log.debug("Smart Scoping disabled for attribute (" + getId() + ").");
97                 }
98
99                 //Load a value handler
100                 String valueHandlerSpec = e.getAttribute("valueHandler");
101
102                 if (valueHandlerSpec != null && !valueHandlerSpec.equals("")) {
103                         if (smartScope == null) {
104                                 try {
105                                         Class handlerClass = Class.forName(valueHandlerSpec);
106                                         valueHandler = (ValueHandler) handlerClass.newInstance();
107                                 } catch (ClassNotFoundException cnfe) {
108                                         log.error("Value Handler implementation specified for attribute (" + getId()
109                                                         + ") cannot be found: " + cnfe);
110                                         throw new ResolutionPlugInException("Value Handler implementation specified for attribute ("
111                                                         + getId() + ") cannot be found.");
112                                 } catch (Exception oe) {
113                                         log.error("Value Handler implementation specified for attribute (" + getId()
114                                                         + ") coudl not be loaded: " + oe);
115                                         throw new ResolutionPlugInException("Value Handler implementation specified for attribute ("
116                                                         + getId() + ") could not be loaded.");
117                                 }
118                         } else {
119                                 log
120                                                 .error("Specification of \"valueHandler\' cannot be used in combination with \"smartScope\".  Ignoring Value Handler for attribute ("
121                                                                 + getId() + ").");
122                         }
123                 }
124
125                 if (valueHandler != null) {
126                         log.debug("Custom Value Handler enabled for attribute (" + getId() + ").");
127                 }
128
129                 //Decide whether or not to allow empty string values
130                 String rawAllowEmpty = e.getAttribute("allowEmpty");
131                 if (rawAllowEmpty != null) {
132                         if (rawAllowEmpty.equalsIgnoreCase("TRUE")) {
133                                 allowEmpty = true;
134                         }
135                 }
136
137                 log.debug("Allowal of empty string values is set to (" + allowEmpty + ") for attribute (" + getId() + ").");
138
139                 //Decide whether or not to force values to lower case
140                 String rawDownCase = e.getAttribute("downCase");
141                 if (rawDownCase != null) {
142                         if (rawDownCase.equalsIgnoreCase("TRUE")) {
143                                 downCase = true;
144                                 log.debug("Forcing values to lower case for attribute (" + getId() + ").");
145                         }
146                 }
147
148         }
149
150         /**
151          * @see edu.internet2.middleware.shibboleth.aa.attrresolv.AttributeDefinitionPlugIn#resolve(edu.internet2.middleware.shibboleth.aa.attrresolv.ArpAttribute,
152          *      java.security.Principal, java.lang.String, edu.internet2.middleware.shibboleth.aa.attrresolv.Dependencies)
153          */
154         public void resolve(ResolverAttribute attribute, Principal principal, String requester, Dependencies depends)
155                         throws ResolutionPlugInException {
156
157                 log.debug("Resolving attribute: (" + getId() + ")");
158                 Set results = new LinkedHashSet();
159                 if (!connectorDependencyIds.isEmpty()) {
160                         results.addAll(Arrays.asList(getValuesFromConnectors(depends)));
161                 }
162
163                 if (!attributeDependencyIds.isEmpty()) {
164                         results.addAll(Arrays.asList(getValuesFromAttributes(depends)));
165                 }
166
167                 if (lifeTime != -1) {
168                         attribute.setLifetime(lifeTime);
169                 }
170
171                 if (smartScope != null) {
172                         attribute.registerValueHandler(new ScopedStringValueHandler(smartScope));
173                 }
174                 if (smartScope == null && valueHandler != null) {
175                         attribute.registerValueHandler(valueHandler);
176                 }
177
178                 Iterator resultsIt = results.iterator();
179                 while (resultsIt.hasNext()) {
180                         Object value = resultsIt.next();
181                         if (!allowEmpty && value.equals("")) {
182                                 log.debug("Skipping empty string value.");
183                                 continue;
184                         }
185
186                         if (downCase && value instanceof String) {
187                                 value = ((String) value).toLowerCase();
188                         }
189
190                         attribute.addValue(value);
191                 }
192                 attribute.setResolved();
193         }
194
195         protected Object[] getValuesFromAttributes(Dependencies depends) {
196
197                 Set results = new LinkedHashSet();
198
199                 Iterator attrDependIt = attributeDependencyIds.iterator();
200                 while (attrDependIt.hasNext()) {
201                         ResolverAttribute attribute = depends.getAttributeResolution((String) attrDependIt.next());
202                         if (attribute != null) {
203                                 log.debug("Found value(s) for attribute (" + getId() + ").");
204                                 for (Iterator iterator = attribute.getValues(); iterator.hasNext();) {
205                                         results.add(iterator.next());
206                                 }
207                         } else {
208                                 log.error("An attribute dependency of attribute (" + getId()
209                                                 + ") was not included in the dependency chain.");
210                         }
211                 }
212
213                 if (results.isEmpty()) {
214                         log.debug("An attribute dependency of attribute (" + getId() + ") supplied no values.");
215                 }
216                 return results.toArray();
217         }
218
219         protected Object[] getValuesFromConnectors(Dependencies depends) {
220
221                 Set results = new LinkedHashSet();
222
223                 Iterator connectorDependIt = connectorDependencyIds.iterator();
224                 while (connectorDependIt.hasNext()) {
225                         Attributes attrs = depends.getConnectorResolution((String) connectorDependIt.next());
226                         if (attrs != null) {
227                                 Attribute attr = attrs.get(connectorMapping);
228                                 if (attr != null) {
229                                         log.debug("Found value(s) for attribute (" + getId() + ").");
230                                         try {
231                                                 NamingEnumeration valuesEnum = attr.getAll();
232                                                 while (valuesEnum.hasMore()) {
233                                                         results.add(valuesEnum.next());
234                                                 }
235                                         } catch (NamingException e) {
236                                                 log.error("An problem was encountered resolving the dependencies of attribute (" + getId()
237                                                                 + "): " + e);
238                                         }
239                                 }
240                         }
241                 }
242
243                 if (results.isEmpty()) {
244                         log.debug("A connector dependency of attribute (" + getId() + ") supplied no values.");
245                 }
246                 return results.toArray();
247         }
248 }