AA compiles again.
[java-idp.git] / src / edu / internet2 / middleware / shibboleth / common / ServiceProviderMapper.java
1 /*
2  * The Shibboleth License, Version 1. Copyright (c) 2002 University Corporation
3  * for Advanced Internet Development, Inc. All rights reserved
4  * 
5  * 
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  * 
9  * Redistributions of source code must retain the above copyright notice, this
10  * list of conditions and the following disclaimer.
11  * 
12  * Redistributions in binary form must reproduce the above copyright notice,
13  * this list of conditions and the following disclaimer in the documentation
14  * and/or other materials provided with the distribution, if any, must include
15  * the following acknowledgment: "This product includes software developed by
16  * the University Corporation for Advanced Internet Development
17  * <http://www.ucaid.edu> Internet2 Project. Alternately, this acknowledegement
18  * may appear in the software itself, if and wherever such third-party
19  * acknowledgments normally appear.
20  * 
21  * Neither the name of Shibboleth nor the names of its contributors, nor
22  * Internet2, nor the University Corporation for Advanced Internet Development,
23  * Inc., nor UCAID may be used to endorse or promote products derived from this
24  * software without specific prior written permission. For written permission,
25  * please contact shibboleth@shibboleth.org
26  * 
27  * Products derived from this software may not be called Shibboleth, Internet2,
28  * UCAID, or the University Corporation for Advanced Internet Development, nor
29  * may Shibboleth appear in their name, without prior written permission of the
30  * University Corporation for Advanced Internet Development.
31  * 
32  * 
33  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
34  * AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
35  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
36  * PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK
37  * OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE.
38  * IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY
39  * CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY
40  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
41  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
42  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
43  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
44  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
45  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
46  */
47 package edu.internet2.middleware.shibboleth.common;
48
49 import java.util.HashMap;
50 import java.util.Map;
51 import java.util.Properties;
52
53 import org.apache.log4j.Logger;
54 import org.w3c.dom.Element;
55 import org.w3c.dom.NodeList;
56
57 import edu.internet2.middleware.shibboleth.hs.HSNameMapper;
58
59 /**
60  * @author Walter Hoehn
61  *  
62  */
63 public class ServiceProviderMapper {
64
65         private static Logger log = Logger.getLogger(ShibbolethOriginConfig.class.getName());
66         private ShibbolethOriginConfig configuration;
67         private Credentials credentials;
68         private HSNameMapper nameMapper;
69         private Map relyingParties = new HashMap();
70
71         public ServiceProviderMapper(
72                 Element rawConfig,
73                 ShibbolethOriginConfig configuration,
74                 Credentials credentials,
75                 HSNameMapper nameMapper)
76                 throws ServiceProviderMapperException {
77
78                 this.configuration = configuration;
79                 this.credentials = credentials;
80                 this.nameMapper = nameMapper;
81
82                 NodeList itemElements =
83                         rawConfig.getElementsByTagNameNS(ShibbolethOriginConfig.originConfigNamespace, "RelyingParty");
84
85                 for (int i = 0; i < itemElements.getLength(); i++) {
86                         addHSRelyingParty((Element) itemElements.item(i));
87                 }
88
89                 verifyDefaultParty(configuration);
90         }
91
92         public ServiceProviderMapper(Element rawConfig, ShibbolethOriginConfig configuration)
93                 throws ServiceProviderMapperException {
94
95                 this.configuration = configuration;
96
97                 NodeList itemElements =
98                         rawConfig.getElementsByTagNameNS(ShibbolethOriginConfig.originConfigNamespace, "RelyingParty");
99
100                 for (int i = 0; i < itemElements.getLength(); i++) {
101                         addAARelyingParty((Element) itemElements.item(i));
102                 }
103
104                 verifyDefaultParty(configuration);
105         }
106
107         private void verifyDefaultParty(ShibbolethOriginConfig configuration) throws ServiceProviderMapperException {
108                 //Verify we have a proper default party
109                 String defaultParty =
110                         configuration.getConfigProperty(
111                                 "edu.internet2.middleware.shibboleth.common.RelyingParty.defaultRelyingParty");
112                 if (defaultParty == null || defaultParty.equals("")) {
113                         if (relyingParties.size() != 1) {
114                                 log.error(
115                                         "Default Relying Party not specified.  Add a (defaultRelyingParty) attribute to <ShibbolethOriginConfig>.");
116                                 throw new ServiceProviderMapperException("Required configuration not specified.");
117                         } else {
118                                 log.debug("Only one Relying Party loaded.  Using this as the default.");
119                         }
120                 }
121                 log.debug("Default Relying Party set to: (" + defaultParty + ").");
122                 if (!relyingParties.containsKey(defaultParty)) {
123                         log.error("Default Relying Party refers to a Relying Party that has not been loaded.");
124                         throw new ServiceProviderMapperException("Invalid configuration (Default Relying Party).");
125                 }
126         }
127
128         private void addHSRelyingParty(Element e) throws ServiceProviderMapperException {
129
130                 log.debug("Found a Relying Party.");
131                 try {
132                         if (e.getLocalName().equals("RelyingParty")) {
133                                 RelyingParty party = new RelyingPartyImpl(e, configuration, credentials, nameMapper);
134                                 log.debug("Relying Party (" + party.getName() + ") loaded.");
135                                 relyingParties.put(party.getName(), party);
136                         }
137                 } catch (ServiceProviderMapperException exc) {
138                         log.error("Encountered an error while attempting to load Relying Party configuration.  Skipping...");
139                 }
140         }
141
142         private void addAARelyingParty(Element e) throws ServiceProviderMapperException {
143
144                 log.debug("Found a Relying Party.");
145                 try {
146                         if (e.getLocalName().equals("RelyingParty")) {
147                                 RelyingParty party = new RelyingPartyImpl(e, configuration);
148                                 log.debug("Relying Party (" + party.getName() + ") loaded.");
149                                 relyingParties.put(party.getName(), party);
150                         }
151                 } catch (ServiceProviderMapperException exc) {
152                         log.error("Encountered an error while attempting to load Relying Party configuration.  Skipping...");
153                 }
154         }
155         public RelyingParty getRelyingParty(String providerIdFromTarget) {
156
157                 //If the target did not send a Provider Id, then assume it is a Shib
158                 // 1.1 or older target
159                 if (providerIdFromTarget == null || providerIdFromTarget.equals("")) {
160                         log.info("Request is from legacy shib target.  Selecting default Relying Party.");
161                         return new LegacyWrapper(getDefaultRelyingPatry());
162                 }
163
164                 //Look for a configuration for the specific relying party
165                 if (relyingParties.containsKey(providerIdFromTarget)) {
166                         log.info("Found Relying Party for (" + providerIdFromTarget + ").");
167                         return (RelyingParty) relyingParties.get(providerIdFromTarget);
168                 }
169
170                 //Next, check to see if the relying party is in any groups
171                 RelyingParty groupParty = findRelyingPartyByGroup(providerIdFromTarget);
172                 if (groupParty != null) {
173                         log.info("Provider is a member of Relying Party (" + groupParty.getName() + ").");
174                         return new RelyingPartyGroupWrapper(groupParty, providerIdFromTarget);
175                 }
176
177                 //OK, just send the default
178                 log.info(
179                         "Could not locate Relying Party configuration for ("
180                                 + providerIdFromTarget
181                                 + ").  Using default Relying Party.");
182                 return new UnknownProviderWrapper(getDefaultRelyingPatry());
183         }
184
185         private RelyingParty findRelyingPartyByGroup(String providerIdFromTarget) {
186
187                 // TODO This is totally a stub and needs to be based on target metadata
188                 // lookup
189                 if (providerIdFromTarget.startsWith("urn:mace:inqueue:")) {
190                         if (relyingParties.containsKey("urn:mace:inqueue")) {
191                                 return (RelyingParty) relyingParties.get("urn:mace:inqueue");
192                         }
193                 }
194                 return null;
195         }
196
197         private RelyingParty getDefaultRelyingPatry() {
198
199                 //If there is no explicit default, pick the single configured Relying
200                 // Party
201                 String defaultParty =
202                         configuration.getConfigProperty(
203                                 "edu.internet2.middleware.shibboleth.common.RelyingParty.defaultRelyingParty");
204                 if (defaultParty == null || defaultParty.equals("")) {
205                         return (RelyingParty) relyingParties.values().iterator().next();
206                 }
207
208                 //If we do have a default specified, use it...
209                 return (RelyingParty) relyingParties.get(defaultParty);
210         }
211
212         class RelyingPartyImpl implements RelyingParty {
213
214                 protected ShibbolethOriginConfig originConfig;
215                 protected Properties partyOverrides = new Properties();
216                 protected RelyingPartyIdentityProvider identityProvider;
217                 protected String name;
218                 protected String hsNameFormatId;
219
220                 public RelyingPartyImpl(
221                         Element partyConfig,
222                         ShibbolethOriginConfig globalConfig,
223                         Credentials credentials,
224                         HSNameMapper nameMapper)
225                         throws ServiceProviderMapperException {
226
227                         this(partyConfig, globalConfig);
228
229                         //Load a credential for signing
230                         String credentialName = ((Element) partyConfig).getAttribute("signingCredential");
231                         Credential credential = credentials.getCredential(credentialName);
232
233                         if (credential == null) {
234                                 if (credentialName == null || credentialName.equals("")) {
235                                         log.error(
236                                                 "Relying Party credential not set.  Add a (signingCredential) attribute to <RelyingParty>.");
237                                         throw new ServiceProviderMapperException("Required configuration not specified.");
238                                 } else {
239                                         log.error(
240                                                 "Relying Party credential not set.  Add a (signingCredential) attribute to <RelyingParty>.");
241                                         throw new ServiceProviderMapperException("Required configuration not specified.");
242                                 }
243                         }
244
245                         //Load and verify the name format that the HS should use in
246                         //assertions for this RelyingParty
247                         NodeList hsNameFormats =
248                                 ((Element) partyConfig).getElementsByTagNameNS(
249                                         ShibbolethOriginConfig.originConfigNamespace,
250                                         "HSNameFormat");
251                         //If no specification. Make sure we have a default mapping
252                         if (hsNameFormats.getLength() < 1) {
253                                 if (nameMapper.getNameIdentifierMappingById(null) == null) {
254                                         log.error("Relying Party HS Name Format not set.  Add a <HSNameFormat> element to <RelyingParty>.");
255                                         throw new ServiceProviderMapperException("Required configuration not specified.");
256                                 }
257
258                         } else {
259                                 //We do have a specification, so make sure it points to a
260                                 // valid Name Mapping
261                                 if (hsNameFormats.getLength() > 1) {
262                                         log.warn(
263                                                 "Found multiple HSNameFormat specifications for Relying Party ("
264                                                         + name
265                                                         + ").  Ignoring all but the first.");
266                                 }
267
268                                 String hsNameFormatId = ((Element) hsNameFormats.item(0)).getAttribute("nameMapping");
269                                 if (hsNameFormatId == null || hsNameFormatId.equals("")) {
270                                         log.error("HS Name Format mapping not set.  Add a (nameMapping) attribute to <HSNameFormat>.");
271                                         throw new ServiceProviderMapperException("Required configuration not specified.");
272                                 }
273
274                                 if (nameMapper.getNameIdentifierMappingById(hsNameFormatId) == null) {
275                                         log.error("Relying Party HS Name Format refers to a name mapping that is not loaded.");
276                                         throw new ServiceProviderMapperException("Required configuration not specified.");
277                                 }
278                         }
279                         identityProvider =
280                                 new RelyingPartyIdentityProvider(
281                                         getConfigProperty("edu.internet2.middleware.shibboleth.hs.HandleServlet.providerId"),
282                                         credential);
283                 }
284
285                 public RelyingPartyImpl(Element partyConfig, ShibbolethOriginConfig globalConfig)
286                         throws ServiceProviderMapperException {
287
288                         //Use global config for defaults
289                         this.originConfig = globalConfig;
290
291                         //Get party name
292                         name = ((Element) partyConfig).getAttribute("name");
293                         if (name == null || name.equals("")) {
294                                 log.error("Relying Party name not set.  Add a (name) attribute to <RelyingParty>.");
295                                 throw new ServiceProviderMapperException("Required configuration not specified.");
296                         }
297                         log.debug("Loading Relying Party: (" + name + ").");
298
299                         //Process overrides for global data
300                         String attribute = ((Element) partyConfig).getAttribute("providerId");
301                         if (attribute != null && !attribute.equals("")) {
302                                 log.debug("Overriding providerId for Relying Pary (" + name + ") with (" + attribute + ").");
303                                 partyOverrides.setProperty(
304                                         "edu.internet2.middleware.shibboleth.hs.HandleServlet.providerId",
305                                         attribute);
306                         }
307
308                         attribute = ((Element) partyConfig).getAttribute("AAUrl");
309                         if (attribute != null && !attribute.equals("")) {
310                                 log.debug("Overriding AAUrl for Relying Pary (" + name + ") with (" + attribute + ").");
311                                 partyOverrides.setProperty("edu.internet2.middleware.shibboleth.hs.HandleServlet.AAUrl", attribute);
312                         }
313
314                         attribute = ((Element) partyConfig).getAttribute("defaultAuthMethod");
315                         if (attribute != null && !attribute.equals("")) {
316                                 log.debug("Overriding defaultAuthMethod for Relying Pary (" + name + ") with (" + attribute + ").");
317                                 partyOverrides.setProperty(
318                                         "edu.internet2.middleware.shibboleth.hs.HandleServlet.defaultAuthMethod",
319                                         attribute);
320                         }
321
322                         identityProvider =
323                                 new RelyingPartyIdentityProvider(
324                                         getConfigProperty("edu.internet2.middleware.shibboleth.hs.HandleServlet.providerId"),
325                                         null);
326
327                 }
328
329                 public String getProviderId() {
330                         return name;
331                 }
332
333                 public String getName() {
334                         return name;
335                 }
336
337                 public boolean isLegacyProvider() {
338                         return false;
339                 }
340
341                 public String getConfigProperty(String key) {
342                         if (partyOverrides.containsKey(key)) {
343                                 return partyOverrides.getProperty(key);
344                         }
345                         return originConfig.getConfigProperty(key);
346                 }
347
348                 public String getHSNameFormatId() {
349                         return null;
350                 }
351
352                 public IdentityProvider getIdentityProvider() {
353                         return identityProvider;
354                 }
355
356                 class RelyingPartyIdentityProvider implements IdentityProvider {
357
358                         private String providerId;
359                         private Credential responseSigningCredential;
360
361                         RelyingPartyIdentityProvider(String providerId, Credential responseSigningCred) {
362                                 this.providerId = providerId;
363                                 this.responseSigningCredential = responseSigningCred;
364                         }
365
366                         public String getProviderId() {
367                                 return providerId;
368                         }
369
370                         public Credential getResponseSigningCredential() {
371                                 return responseSigningCredential;
372                         }
373
374                         public Credential getAssertionSigningCredential() {
375                                 return null;
376                         }
377
378                 }
379         }
380
381         class RelyingPartyGroupWrapper implements RelyingParty {
382
383                 private RelyingParty wrapped;
384                 private String providerId;
385
386                 RelyingPartyGroupWrapper(RelyingParty wrapped, String providerId) {
387                         this.wrapped = wrapped;
388                         this.providerId = providerId;
389                 }
390
391                 public String getName() {
392                         return wrapped.getName();
393                 }
394
395                 public String getConfigProperty(String key) {
396                         return wrapped.getConfigProperty(key);
397                 }
398
399                 public boolean isLegacyProvider() {
400                         return false;
401                 }
402
403                 public String getHSNameFormatId() {
404                         return wrapped.getHSNameFormatId();
405                 }
406
407                 public IdentityProvider getIdentityProvider() {
408                         return wrapped.getIdentityProvider();
409                 }
410
411                 public String getProviderId() {
412                         return providerId;
413                 }
414         }
415
416         class UnknownProviderWrapper implements RelyingParty {
417                 private RelyingParty wrapped;
418
419                 UnknownProviderWrapper(RelyingParty wrapped) {
420                         this.wrapped = wrapped;
421                 }
422
423                 public String getName() {
424                         return wrapped.getName();
425                 }
426
427                 public String getConfigProperty(String key) {
428                         return wrapped.getConfigProperty(key);
429                 }
430
431                 public boolean isLegacyProvider() {
432                         return wrapped.isLegacyProvider();
433                 }
434
435                 public String getHSNameFormatId() {
436                         return wrapped.getHSNameFormatId();
437                 }
438
439                 public IdentityProvider getIdentityProvider() {
440                         return wrapped.getIdentityProvider();
441                 }
442
443                 public String getProviderId() {
444                         return null;
445                 }
446         }
447
448         class LegacyWrapper extends UnknownProviderWrapper {
449
450                 LegacyWrapper(RelyingParty wrapped) {
451                         super(wrapped);
452                 }
453                 public boolean isLegacyProvider() {
454                         return true;
455                 }
456         }
457 }