Unifed NameMapper and HSNameMapper interfaces for use by the new IdPResponder servlet.
[java-idp.git] / src / edu / internet2 / middleware / shibboleth / hs / HSServiceProviderMapper.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.hs;
27
28 import java.net.MalformedURLException;
29 import java.net.URI;
30 import java.net.URISyntaxException;
31 import java.net.URL;
32
33 import org.apache.log4j.Logger;
34 import org.w3c.dom.Element;
35 import org.w3c.dom.NodeList;
36
37 import edu.internet2.middleware.shibboleth.common.Credential;
38 import edu.internet2.middleware.shibboleth.common.Credentials;
39 import edu.internet2.middleware.shibboleth.common.NameMapper;
40 import edu.internet2.middleware.shibboleth.common.RelyingParty;
41 import edu.internet2.middleware.shibboleth.common.ServiceProviderMapper;
42 import edu.internet2.middleware.shibboleth.common.ServiceProviderMapperException;
43 import edu.internet2.middleware.shibboleth.common.ShibbolethOriginConfig;
44 import edu.internet2.middleware.shibboleth.metadata.Metadata;
45
46 /**
47  * Class for determining the effective relying party for the Shibboleth handle service from the unique id of the service
48  * provider.
49  * 
50  * @author Walter Hoehn
51  */
52 public class HSServiceProviderMapper extends ServiceProviderMapper {
53
54         private static Logger log = Logger.getLogger(HSServiceProviderMapper.class.getName());
55         private HSConfig configuration;
56         private Credentials credentials;
57         private NameMapper nameMapper;
58
59         /**
60          * Constructs a new service provider mapper for the handle service.
61          * 
62          * @param rawConfig
63          *            DOM representation of the handle service configuration
64          * @param configuration
65          *            global handle service configuration
66          * @param credentials
67          *            credentials for the handle service using this provider mapper
68          * @param nameMapper
69          *            name mapper for the handle service using this provider mapper
70          * @throws ServiceProviderMapperException
71          *             if the configuration is invalid
72          */
73         public HSServiceProviderMapper(Element rawConfig, HSConfig configuration, Credentials credentials,
74                         NameMapper nameMapper, Metadata metaData) throws ServiceProviderMapperException {
75
76                 super(metaData);
77                 this.configuration = configuration;
78                 this.credentials = credentials;
79                 this.nameMapper = nameMapper;
80
81                 NodeList itemElements = rawConfig.getElementsByTagNameNS(ShibbolethOriginConfig.originConfigNamespace,
82                                 "RelyingParty");
83
84                 for (int i = 0; i < itemElements.getLength(); i++) {
85                         addRelyingParty((Element) itemElements.item(i));
86                 }
87
88                 verifyDefaultParty(configuration);
89         }
90
91         private void addRelyingParty(Element e) throws ServiceProviderMapperException {
92
93                 log.debug("Found a Relying Party.");
94                 try {
95                         if (e.getLocalName().equals("RelyingParty")) {
96                                 RelyingParty party = new HSRelyingPartyImpl(e, configuration, credentials, nameMapper);
97                                 log.debug("Relying Party (" + party.getName() + ") loaded.");
98                                 relyingParties.put(party.getName(), party);
99                         }
100                 } catch (ServiceProviderMapperException exc) {
101                         log.error("Encountered an error while attempting to load Relying Party configuration.  Skipping...");
102                 }
103         }
104
105         /**
106          * Returns the appropriate relying party for the supplied service provider id.
107          */
108         public HSRelyingParty getRelyingParty(String providerIdFromTarget) {
109
110                 if (providerIdFromTarget == null || providerIdFromTarget.equals("")) {
111                         RelyingParty relyingParty = getDefaultRelyingParty();
112                         log.info("Selecting default Relying Party: (" + relyingParty.getName() + ").");
113                         return new NoMetadataWrapper((HSRelyingParty) relyingParty);
114                 }
115
116                 return (HSRelyingParty) getRelyingPartyImpl(providerIdFromTarget);
117         }
118
119         /**
120          * Returns the relying party for a legacy provider(the default)
121          */
122         public HSRelyingParty getLegacyRelyingParty() {
123
124                 RelyingParty relyingParty = getDefaultRelyingParty();
125                 log.info("Request is from legacy shib target.  Selecting default Relying Party: (" + relyingParty.getName()
126                                 + ").");
127                 return new LegacyWrapper((HSRelyingParty) relyingParty);
128
129         }
130
131         protected ShibbolethOriginConfig getOriginConfig() {
132
133                 return configuration;
134         }
135
136         /**
137          * HS-specific relying party implementation.
138          * 
139          * @author Walter Hoehn
140          */
141         class HSRelyingPartyImpl extends BaseRelyingPartyImpl implements HSRelyingParty {
142
143                 private URL overridenAAUrl;
144                 private URI overridenDefaultAuthMethod;
145                 protected String hsNameFormatId;
146                 private HSConfig configuration;
147
148                 HSRelyingPartyImpl(Element partyConfig, HSConfig globalConfig, Credentials credentials, NameMapper nameMapper)
149                                 throws ServiceProviderMapperException {
150
151                         super(partyConfig);
152
153                         configuration = globalConfig;
154
155                         //Load a credential for signing
156                         String credentialName = ((Element) partyConfig).getAttribute("signingCredential");
157                         boolean signAuthResponses = new Boolean(((Element) partyConfig).getAttribute("signAuthResponses"))
158                                         .booleanValue();
159                         boolean signAuthAssertions = new Boolean(((Element) partyConfig).getAttribute("signAuthAssertions"))
160                                         .booleanValue();
161                         Credential credential = credentials.getCredential(credentialName);
162
163                         if ((credential == null) && (signAuthResponses || signAuthAssertions)) {
164                                 if (credentialName == null || credentialName.equals("")) {
165                                         log
166                                                         .error("Relying Party credential not set.  Add a (signingCredential) attribute to <RelyingParty>.");
167                                         throw new ServiceProviderMapperException("Required configuration not specified.");
168                                 } else {
169                                         log
170                                                         .error("Relying Party credential invalid.  Fix the (signingCredential) attribute on <RelyingParty>.");
171                                         throw new ServiceProviderMapperException("Required configuration is invalid.");
172                                 }
173                         }
174
175                         //Load and verify the name format that the HS should use in
176                         //assertions for this RelyingParty
177                         NodeList hsNameFormats = ((Element) partyConfig).getElementsByTagNameNS(
178                                         ShibbolethOriginConfig.originConfigNamespace, "HSNameFormat");
179                         //If no specification. Make sure we have a default mapping
180                         if (hsNameFormats.getLength() < 1) {
181                                 if (nameMapper.getNameIdentifierMappingById(null) == null) {
182                                         log.error("Relying Party HS Name Format not set.  Add a <HSNameFormat> element to <RelyingParty>.");
183                                         throw new ServiceProviderMapperException("Required configuration not specified.");
184                                 }
185
186                         } else {
187                                 //We do have a specification, so make sure it points to a
188                                 // valid Name Mapping
189                                 if (hsNameFormats.getLength() > 1) {
190                                         log.warn("Found multiple HSNameFormat specifications for Relying Party (" + name
191                                                         + ").  Ignoring all but the first.");
192                                 }
193
194                                 hsNameFormatId = ((Element) hsNameFormats.item(0)).getAttribute("nameMapping");
195                                 if (hsNameFormatId == null || hsNameFormatId.equals("")) {
196                                         log.error("HS Name Format mapping not set.  Add a (nameMapping) attribute to <HSNameFormat>.");
197                                         throw new ServiceProviderMapperException("Required configuration not specified.");
198                                 }
199
200                                 if (nameMapper.getNameIdentifierMappingById(hsNameFormatId) == null) {
201                                         log.error("Relying Party HS Name Format refers to a name mapping that is not loaded.");
202                                         throw new ServiceProviderMapperException("Required configuration not specified.");
203                                 }
204                         }
205
206                         //Global overrides
207                         String attribute = ((Element) partyConfig).getAttribute("AAUrl");
208                         if (attribute != null && !attribute.equals("")) {
209                                 log.debug("Overriding AAUrl for Relying Pary (" + name + ") with (" + attribute + ").");
210                                 try {
211                                         overridenAAUrl = new URL(attribute);
212                                 } catch (MalformedURLException e) {
213                                         log.error("(AAUrl) attribute to is not a valid URL.");
214                                         throw new ServiceProviderMapperException("Configuration is invalid.");
215                                 }
216                         }
217
218                         attribute = ((Element) partyConfig).getAttribute("defaultAuthMethod");
219                         if (attribute != null && !attribute.equals("")) {
220                                 log.debug("Overriding defaultAuthMethod for Relying Pary (" + name + ") with (" + attribute + ").");
221                                 try {
222                                         overridenDefaultAuthMethod = new URI(attribute);
223                                 } catch (URISyntaxException e1) {
224                                         log.error("(defaultAuthMethod) attribute to is not a valid URI.");
225                                         throw new ServiceProviderMapperException("Configuration is invalid.");
226                                 }
227                         }
228
229                         identityProvider = new RelyingPartyIdentityProvider(overridenOriginProviderId != null
230                                         ? overridenOriginProviderId
231                                         : configuration.getProviderId(), signAuthResponses ? credential : null, signAuthAssertions
232                                         ? credential
233                                         : null);
234                 }
235
236                 public boolean isLegacyProvider() {
237
238                         return false;
239                 }
240
241                 public String getHSNameFormatId() {
242
243                         return hsNameFormatId;
244                 }
245
246                 public URI getDefaultAuthMethod() {
247
248                         if (overridenDefaultAuthMethod != null) {
249                                 return overridenDefaultAuthMethod;
250                         } else {
251                                 return configuration.getDefaultAuthMethod();
252                         }
253                 }
254
255                 public URL getAAUrl() {
256
257                         if (overridenAAUrl != null) {
258                                 return overridenAAUrl;
259                         } else {
260                                 return configuration.getAAUrl();
261                         }
262                 }
263         }
264
265         /**
266          * Relying party wrapper for Shibboleth &lt;=1.1 service providers.
267          * 
268          * @author Walter Hoehn
269          */
270         class LegacyWrapper extends UnknownProviderWrapper implements HSRelyingParty {
271
272                 LegacyWrapper(HSRelyingParty wrapped) {
273
274                         super(wrapped, null);
275                 }
276
277                 public boolean isLegacyProvider() {
278
279                         return true;
280                 }
281
282                 public String getHSNameFormatId() {
283
284                         return ((HSRelyingParty) wrapped).getHSNameFormatId();
285                 }
286
287                 public URL getAAUrl() {
288
289                         return ((HSRelyingParty) wrapped).getAAUrl();
290                 }
291
292                 public URI getDefaultAuthMethod() {
293
294                         return ((HSRelyingParty) wrapped).getDefaultAuthMethod();
295                 }
296         }
297
298         /**
299          * Relying party wrapper for providers for which we have no metadata
300          * 
301          * @author Walter Hoehn
302          */
303         class NoMetadataWrapper extends UnknownProviderWrapper implements HSRelyingParty {
304
305                 NoMetadataWrapper(HSRelyingParty wrapped) {
306
307                         super(wrapped, null);
308                 }
309
310                 public String getHSNameFormatId() {
311
312                         return ((HSRelyingParty) wrapped).getHSNameFormatId();
313                 }
314
315                 public URL getAAUrl() {
316
317                         return ((HSRelyingParty) wrapped).getAAUrl();
318                 }
319
320                 public URI getDefaultAuthMethod() {
321
322                         return ((HSRelyingParty) wrapped).getDefaultAuthMethod();
323                 }
324         }
325
326 }