Fleshed out support for configuration based on Relying Party lookup.
[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 /**
58  * @author Walter Hoehn
59  *  
60  */
61 public class ServiceProviderMapper {
62
63         private static Logger log = Logger.getLogger(ShibbolethOriginConfig.class.getName());
64         private ShibbolethOriginConfig configuration;
65         private Credentials credentials;
66         private Map relyingParties = new HashMap();
67
68         public ServiceProviderMapper(Element rawConfig, ShibbolethOriginConfig configuration, Credentials credentials)
69                 throws ServiceProviderMapperException {
70
71                 this.configuration = configuration;
72                 this.credentials = credentials;
73
74                 NodeList itemElements =
75                         rawConfig.getElementsByTagNameNS(ShibbolethOriginConfig.originConfigNamespace, "RelyingParty");
76
77                 for (int i = 0; i < itemElements.getLength(); i++) {
78                         addRelyingParty((Element) itemElements.item(i));
79                 }
80
81                 //Verify we have a proper default party
82                 String defaultParty =
83                         configuration.getConfigProperty(
84                                 "edu.internet2.middleware.shibboleth.common.RelyingParty.defaultRelyingParty");
85                 if (defaultParty == null || defaultParty.equals("")) {
86                         if (relyingParties.size() != 1) {
87                                 log.error(
88                                         "Default Relying Party not specified.  Add a (defaultRelyingParty) attribute to <ShibbolethOriginConfig>.");
89                                 throw new ServiceProviderMapperException("Required configuration not specified.");
90                         } else {
91                                 log.debug("Only one Relying Party loaded.  Using this as the default.");
92                         }
93                 }
94                 log.debug("Default Relying Party set to: (" + defaultParty + ").");
95                 if (!relyingParties.containsKey(defaultParty)) {
96                         log.error("Default Relying Party refers to a Relying Party that has not been loaded.");
97                         throw new ServiceProviderMapperException("Invalid configuration (Default Relying Party).");
98                 }
99         }
100
101         private void addRelyingParty(Element e) throws ServiceProviderMapperException {
102
103                 log.debug("Found a Relying Party.");
104                 //TODO catch these individually and don't totally fail for one bad
105                 if (e.getLocalName().equals("RelyingParty")) {
106                         RelyingParty party = new RelyingPartyImpl(e, configuration, credentials);
107                         log.debug("Relying Party (" + party.getName() + ") loaded.");
108                         relyingParties.put(party.getName(), party);
109                 }
110         }
111         public RelyingParty getRelyingParty(String providerIdFromTarget) {
112
113                 //If the target did not send a Provider Id, then assume it is a Shib
114                 // 1.1 or older target
115                 if (providerIdFromTarget == null || providerIdFromTarget.equals("")) {
116                         log.info("Request is from legacy shib target.  Selecting default Relying Party.");
117                         return new LegacyWrapper(getDefaultRelyingPatry());
118                 }
119
120                 //Look for a configuration for the specific relying party
121                 if (relyingParties.containsKey(providerIdFromTarget)) {
122                         log.info("Found Relying Party for (" + providerIdFromTarget + ").");
123                         return (RelyingParty) relyingParties.get(providerIdFromTarget);
124                 }
125
126                 //Next, check to see if the relying party is in any groups
127                 RelyingParty groupParty = findRelyingPartyByGroup(providerIdFromTarget);
128                 if (groupParty != null) {
129                         log.info("Provider is a member of Relying Party (" + groupParty.getName() + ").");
130                         return new RelyingPartyGroupWrapper(groupParty, providerIdFromTarget);
131                 }
132
133                 //OK, just send the default
134                 log.info(
135                         "Could not locate Relying Party configuration for ("
136                                 + providerIdFromTarget
137                                 + ").  Using default Relying Party.");
138                 return new UnknownProviderWrapper(getDefaultRelyingPatry());
139         }
140
141         private RelyingParty findRelyingPartyByGroup(String providerIdFromTarget) {
142
143                 // TODO This is totally a stub and needs to be based on target metadata lookup
144                 if (providerIdFromTarget.startsWith("urn:mace:inqueue:")) {
145                         if (relyingParties.containsKey("urn:mace:inqueue")) {
146                                 return (RelyingParty) relyingParties.get("urn:mace:inqueue");
147                         }
148                 }
149                 return null;
150         }
151
152         private RelyingParty getDefaultRelyingPatry() {
153
154                 //If there is no explicit default, pick the single configured Relying Party
155                 String defaultParty =
156                         configuration.getConfigProperty(
157                                 "edu.internet2.middleware.shibboleth.common.RelyingParty.defaultRelyingParty");
158                 if (defaultParty == null || defaultParty.equals("")) {
159                         return (RelyingParty) relyingParties.values().iterator().next();
160                 }
161
162                 //If we do have a default specified, use it...
163                 return (RelyingParty) relyingParties.get(defaultParty);
164         }
165
166         class RelyingPartyImpl implements RelyingParty {
167
168                 protected ShibbolethOriginConfig originConfig;
169                 protected Properties partyOverrides = new Properties();
170                 protected RelyingPartyIdentityProvider identityProvider;
171                 protected String name;
172
173                 public RelyingPartyImpl(Element partyConfig, ShibbolethOriginConfig globalConfig, Credentials credentials)
174                         throws ServiceProviderMapperException {
175
176                         //Use global config for defaults
177                         this.originConfig = globalConfig;
178
179                         //Get party name
180                         name = ((Element) partyConfig).getAttribute("name");
181                         if (name == null || name.equals("")) {
182                                 log.error("Relying Party name not set.  Add a (name) attribute to <RelyingParty>.");
183                                 throw new ServiceProviderMapperException("Required configuration not specified.");
184                         }
185                         log.debug("Loading Relying Party: (" + name + ").");
186
187                         //Load an credential for signing
188                         String credentialName = ((Element) partyConfig).getAttribute("signingCredential");
189                         Credential credential = credentials.getCredential(credentialName);
190
191                         if (credential == null) {
192                                 if (credentialName == null || credentialName.equals("")) {
193                                         log.error(
194                                                 "Relying Party credential not set.  Add a (signingCredential) attribute to <RelyingParty>.");
195                                         throw new ServiceProviderMapperException("Required configuration not specified.");
196                                 } else {
197                                         log.error(
198                                                 "Relying Party credential not set.  Add a (signingCredential) attribute to <RelyingParty>.");
199                                         throw new ServiceProviderMapperException("Required configuration not specified.");
200                                 }
201                         }
202
203                         //TODO HSNameFormat
204
205                         String attribute = ((Element) partyConfig).getAttribute("providerId");
206                         if (attribute != null && !attribute.equals("")) {
207                                 log.debug("Overriding providerId for Relying Pary (" + name + ") with (" + attribute + ").");
208                                 partyOverrides.setProperty(
209                                         "edu.internet2.middleware.shibboleth.hs.HandleServlet.providerId",
210                                         attribute);
211                         }
212
213                         attribute = ((Element) partyConfig).getAttribute("AAUrl");
214                         if (attribute != null && !attribute.equals("")) {
215                                 log.debug("Overriding AAUrl for Relying Pary (" + name + ") with (" + attribute + ").");
216                                 partyOverrides.setProperty("edu.internet2.middleware.shibboleth.hs.HandleServlet.AAUrl", attribute);
217                         }
218
219                         identityProvider =
220                                 new RelyingPartyIdentityProvider(
221                                         getConfigProperty("edu.internet2.middleware.shibboleth.hs.HandleServlet.providerId"),
222                                         credential);
223                 }
224
225                 public String getProviderId() {
226                         return name;
227                 }
228
229                 public String getName() {
230                         return name;
231                 }
232
233                 public boolean isLegacyProvider() {
234                         return false;
235                 }
236
237                 public String getConfigProperty(String key) {
238                         if (partyOverrides.containsKey(key)) {
239                                 return partyOverrides.getProperty(key);
240                         }
241                         return originConfig.getConfigProperty(key);
242                 }
243
244                 public String getHSNameFormatId() {
245                         return null;
246                 }
247
248                 public IdentityProvider getIdentityProvider() {
249                         return identityProvider;
250                 }
251
252                 class RelyingPartyIdentityProvider implements IdentityProvider {
253
254                         private String providerId;
255                         private Credential responseSigningCredential;
256
257                         RelyingPartyIdentityProvider(String providerId, Credential responseSigningCred) {
258                                 this.providerId = providerId;
259                                 this.responseSigningCredential = responseSigningCred;
260                         }
261
262                         public String getProviderId() {
263                                 return providerId;
264                         }
265
266                         public Credential getResponseSigningCredential() {
267                                 return responseSigningCredential;
268                         }
269
270                         public Credential getAssertionSigningCredential() {
271                                 return null;
272                         }
273
274                 }
275         }
276
277         class RelyingPartyGroupWrapper implements RelyingParty {
278
279                 private RelyingParty wrapped;
280                 private String providerId;
281
282                 RelyingPartyGroupWrapper(RelyingParty wrapped, String providerId) {
283                         this.wrapped = wrapped;
284                         this.providerId = providerId;
285                 }
286
287                 public String getName() {
288                         return wrapped.getName();
289                 }
290
291                 public String getConfigProperty(String key) {
292                         return wrapped.getConfigProperty(key);
293                 }
294
295                 public boolean isLegacyProvider() {
296                         return false;
297                 }
298
299                 public String getHSNameFormatId() {
300                         return wrapped.getHSNameFormatId();
301                 }
302
303                 public IdentityProvider getIdentityProvider() {
304                         return wrapped.getIdentityProvider();
305                 }
306
307                 public String getProviderId() {
308                         return providerId;
309                 }
310         }
311
312         class UnknownProviderWrapper implements RelyingParty {
313                 private RelyingParty wrapped;
314
315                 UnknownProviderWrapper(RelyingParty wrapped) {
316                         this.wrapped = wrapped;
317                 }
318
319                 public String getName() {
320                         return wrapped.getName();
321                 }
322
323                 public String getConfigProperty(String key) {
324                         return wrapped.getConfigProperty(key);
325                 }
326
327                 public boolean isLegacyProvider() {
328                         return wrapped.isLegacyProvider();
329                 }
330
331                 public String getHSNameFormatId() {
332                         return wrapped.getHSNameFormatId();
333                 }
334
335                 public IdentityProvider getIdentityProvider() {
336                         return wrapped.getIdentityProvider();
337                 }
338
339                 public String getProviderId() {
340                         return null;
341                 }
342         }
343
344         class LegacyWrapper extends UnknownProviderWrapper {
345
346                 LegacyWrapper(RelyingParty wrapped) {
347                         super(wrapped);
348                 }
349                 public boolean isLegacyProvider() {
350                         return true;
351                 }
352         }
353 }
354