Push SAML Attribute namespace configuration into the resolver. (Needed for proper...
[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 org.apache.log4j.Logger;
35 import org.w3c.dom.Element;
36
37 import edu.internet2.middleware.shibboleth.aa.attrresolv.AttributeDefinitionPlugIn;
38 import edu.internet2.middleware.shibboleth.aa.attrresolv.Dependencies;
39 import edu.internet2.middleware.shibboleth.aa.attrresolv.ResolutionPlugInException;
40 import edu.internet2.middleware.shibboleth.aa.attrresolv.ResolverAttribute;
41
42 /**
43  * Basic <code>AttributeDefinitionPlugIn</code> implementation. Operates as a proxy for attributes gathered by
44  * Connectors.
45  * 
46  * @author Walter Hoehn (wassa@columbia.edu)
47  */
48 public class SimpleAttributeDefinition extends SimpleBaseAttributeDefinition implements AttributeDefinitionPlugIn {
49
50         private static Logger log = Logger.getLogger(SimpleAttributeDefinition.class.getName());
51         private String connectorMapping;
52         private String smartScope;
53         private ValueHandler valueHandler;
54         private boolean allowEmpty = false;
55         private boolean downCase = false;
56
57         /**
58          * Constructor for SimpleAttributeDefinition. Creates a PlugIn based on configuration information presented in a DOM
59          * Element.
60          */
61         public SimpleAttributeDefinition(Element e) throws ResolutionPlugInException {
62
63                 super(e);
64
65                 // Parse source name
66                 String sourceName = e.getAttribute("sourceName");
67                 if (sourceName == null || sourceName.equals("")) {
68                         int index = getId().lastIndexOf("#");
69                         if (index < 0) {
70                                 index = getId().lastIndexOf(":");
71                                 int slashIndex = getId().lastIndexOf("/");
72                                 if (slashIndex > index) {
73                                         index = slashIndex;
74                                 }
75                         }
76                         connectorMapping = getId().substring(index + 1);
77                 } else {
78                         connectorMapping = sourceName;
79                 }
80
81                 log.debug("Mapping attribute to name (" + connectorMapping + ") in connector.");
82
83                 // Configure smart scoping
84                 String smartScopingSpec = e.getAttribute("smartScope");
85                 if (smartScopingSpec != null && !smartScopingSpec.equals("")) {
86                         smartScope = smartScopingSpec;
87                 }
88                 if (smartScope != null) {
89                         log.debug("Smart Scope (" + smartScope + ") enabled for attribute (" + getId() + ").");
90                 } else {
91                         log.debug("Smart Scoping disabled for attribute (" + getId() + ").");
92                 }
93
94                 // Load a value handler
95                 String valueHandlerSpec = e.getAttribute("valueHandler");
96
97                 if (valueHandlerSpec != null && !valueHandlerSpec.equals("")) {
98                         if (smartScope == null) {
99                                 try {
100                                         Class handlerClass = Class.forName(valueHandlerSpec);
101                                         valueHandler = (ValueHandler) handlerClass.newInstance();
102                                 } catch (ClassNotFoundException cnfe) {
103                                         log.error("Value Handler implementation specified for attribute (" + getId()
104                                                         + ") cannot be found: " + cnfe);
105                                         throw new ResolutionPlugInException("Value Handler implementation specified for attribute ("
106                                                         + getId() + ") cannot be found.");
107                                 } catch (Exception oe) {
108                                         log.error("Value Handler implementation specified for attribute (" + getId()
109                                                         + ") coudl not be loaded: " + oe);
110                                         throw new ResolutionPlugInException("Value Handler implementation specified for attribute ("
111                                                         + getId() + ") could not be loaded.");
112                                 }
113                         } else {
114                                 log.error("Specification of \"valueHandler\' cannot be used in combination with \"smartScope\". "
115                                                 + " Ignoring Value Handler for attribute (" + getId() + ").");
116                         }
117                 }
118
119                 if (valueHandler != null) {
120                         log.debug("Custom Value Handler enabled for attribute (" + getId() + ").");
121                 }
122
123                 // Decide whether or not to allow empty string values
124                 String rawAllowEmpty = e.getAttribute("allowEmpty");
125                 if (rawAllowEmpty != null) {
126                         if (rawAllowEmpty.equalsIgnoreCase("TRUE")) {
127                                 allowEmpty = true;
128                         }
129                 }
130
131                 log.debug("Allowal of empty string values is set to (" + allowEmpty + ") for attribute (" + getId() + ").");
132
133                 // Decide whether or not to force values to lower case
134                 String rawDownCase = e.getAttribute("downCase");
135                 if (rawDownCase != null) {
136                         if (rawDownCase.equalsIgnoreCase("TRUE")) {
137                                 downCase = true;
138                                 log.debug("Forcing values to lower case for attribute (" + getId() + ").");
139                         }
140                 }
141
142         }
143
144         /**
145          * @see edu.internet2.middleware.shibboleth.aa.attrresolv.AttributeDefinitionPlugIn#resolve(edu.internet2.middleware.shibboleth.aa.attrresolv.ResolverAttribute,
146          *      java.security.Principal, java.lang.String, java.lang.String,
147          *      edu.internet2.middleware.shibboleth.aa.attrresolv.Dependencies)
148          */
149         public void resolve(ResolverAttribute attribute, Principal principal, String requester, String responder,
150                         Dependencies depends) throws ResolutionPlugInException {
151
152                 log.debug("Resolving attribute: (" + getId() + ")");
153                 Set results = new LinkedHashSet();
154                 if (!connectorDependencyIds.isEmpty()) {
155                         results.addAll(Arrays.asList(getValuesFromConnectors(depends)));
156                 }
157
158                 if (!attributeDependencyIds.isEmpty()) {
159                         results.addAll(Arrays.asList(getValuesFromAttributes(depends)));
160                 }
161
162                 standardProcessing(attribute);
163
164                 if (smartScope != null) {
165                         attribute.registerValueHandler(new ScopedStringValueHandler(smartScope));
166                 }
167                 if (smartScope == null && valueHandler != null) {
168                         attribute.registerValueHandler(valueHandler);
169                 }
170
171                 Iterator resultsIt = results.iterator();
172                 while (resultsIt.hasNext()) {
173                         Object value = resultsIt.next();
174                         if (!allowEmpty && value.equals("")) {
175                                 log.debug("Skipping empty string value.");
176                                 continue;
177                         }
178
179                         if (downCase && value instanceof String) {
180                                 value = ((String) value).toLowerCase();
181                         }
182
183                         attribute.addValue(value);
184                 }
185                 attribute.setResolved();
186         }
187 }