Updated javadocs.
[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.net.URI;
50 import java.net.URL;
51 import java.util.HashMap;
52 import java.util.Map;
53
54 import org.apache.log4j.Logger;
55 import org.w3c.dom.Element;
56
57 import edu.internet2.middleware.shibboleth.aa.AARelyingParty;
58 import edu.internet2.middleware.shibboleth.hs.HSRelyingParty;
59
60 /**
61  * Base class for determining the effective relying party from the unique id of the service provider. Checks first for
62  * an exact match on the service provider, then for membership in a federation. Uses the default relying party if
63  * neither is found.
64  * 
65  * @author Walter Hoehn
66  */
67 public abstract class ServiceProviderMapper {
68
69         private static Logger log = Logger.getLogger(ServiceProviderMapper.class.getName());
70         protected Map relyingParties = new HashMap();
71
72         protected abstract ShibbolethOriginConfig getOriginConfig();
73
74         protected void verifyDefaultParty(ShibbolethOriginConfig configuration) throws ServiceProviderMapperException {
75                 //Verify we have a proper default party
76                 String defaultParty = configuration.getDefaultRelyingPartyName();
77                 if (defaultParty == null || defaultParty.equals("")) {
78                         if (relyingParties.size() != 1) {
79                                 log.error(
80                                         "Default Relying Party not specified.  Add a (defaultRelyingParty) attribute to <ShibbolethOriginConfig>.");
81                                 throw new ServiceProviderMapperException("Required configuration not specified.");
82                         } else {
83                                 log.debug("Only one Relying Party loaded.  Using this as the default.");
84                         }
85                 }
86                 log.debug("Default Relying Party set to: (" + defaultParty + ").");
87                 if (!relyingParties.containsKey(defaultParty)) {
88                         log.error("Default Relying Party refers to a Relying Party that has not been loaded.");
89                         throw new ServiceProviderMapperException("Invalid configuration (Default Relying Party).");
90                 }
91         }
92
93         protected RelyingParty getRelyingPartyImpl(String providerIdFromTarget) {
94
95                 //Look for a configuration for the specific relying party
96                 if (relyingParties.containsKey(providerIdFromTarget)) {
97                         log.info("Found Relying Party for (" + providerIdFromTarget + ").");
98                         return (RelyingParty) relyingParties.get(providerIdFromTarget);
99                 }
100
101                 //Next, check to see if the relying party is in any groups
102                 RelyingParty groupParty = findRelyingPartyByGroup(providerIdFromTarget);
103                 if (groupParty != null) {
104                         log.info("Provider is a member of Relying Party (" + groupParty.getName() + ").");
105                         return new RelyingPartyGroupWrapper(groupParty, providerIdFromTarget);
106                 }
107
108                 //OK, just send the default
109                 log.info(
110                         "Could not locate Relying Party configuration for ("
111                                 + providerIdFromTarget
112                                 + ").  Using default Relying Party.");
113                 return new UnknownProviderWrapper(getDefaultRelyingPatry());
114         }
115
116         private RelyingParty findRelyingPartyByGroup(String providerIdFromTarget) {
117
118                 // TODO This is totally a stub and needs to be based on target metadata
119                 // lookup
120                 if (providerIdFromTarget.startsWith("urn:mace:inqueue:")) {
121                         if (relyingParties.containsKey("urn:mace:inqueue")) {
122                                 return (RelyingParty) relyingParties.get("urn:mace:inqueue");
123                         }
124                 }
125                 return null;
126         }
127
128         protected RelyingParty getDefaultRelyingPatry() {
129
130                 //If there is no explicit default, pick the single configured Relying
131                 // Party
132                 String defaultParty = getOriginConfig().getDefaultRelyingPartyName();
133                 if (defaultParty == null || defaultParty.equals("")) {
134                         return (RelyingParty) relyingParties.values().iterator().next();
135                 }
136
137                 //If we do have a default specified, use it...
138                 return (RelyingParty) relyingParties.get(defaultParty);
139         }
140
141         /**
142          * Base relying party implementation.
143          *
144          * @author Walter Hoehn
145          */
146         protected abstract class BaseRelyingPartyImpl implements RelyingParty {
147
148                 protected RelyingPartyIdentityProvider identityProvider;
149                 protected String name;
150                 protected String overridenOriginProviderId;
151
152                 public BaseRelyingPartyImpl(Element partyConfig) throws ServiceProviderMapperException {
153
154                         //Get party name
155                         name = ((Element) partyConfig).getAttribute("name");
156                         if (name == null || name.equals("")) {
157                                 log.error("Relying Party name not set.  Add a (name) attribute to <RelyingParty>.");
158                                 throw new ServiceProviderMapperException("Required configuration not specified.");
159                         }
160                         log.debug("Loading Relying Party: (" + name + ").");
161
162                         //Process overrides for global data
163                         String attribute = ((Element) partyConfig).getAttribute("providerId");
164                         if (attribute != null && !attribute.equals("")) {
165                                 log.debug("Overriding providerId for Relying Pary (" + name + ") with (" + attribute + ").");
166                                 overridenOriginProviderId = attribute;
167                         }
168
169                 }
170
171                 public String getProviderId() {
172                         return name;
173                 }
174
175                 public String getName() {
176                         return name;
177                 }
178
179                 public IdentityProvider getIdentityProvider() {
180                         return identityProvider;
181                 }
182
183                 /**
184                  * Default identity provider implementation.
185                  * @author Walter Hoehn
186                  */
187                 protected class RelyingPartyIdentityProvider implements IdentityProvider {
188
189                         private String providerId;
190                         private Credential responseSigningCredential;
191
192                         public RelyingPartyIdentityProvider(String providerId, Credential responseSigningCred) {
193                                 this.providerId = providerId;
194                                 this.responseSigningCredential = responseSigningCred;
195                         }
196
197                         public String getProviderId() {
198                                 return providerId;
199                         }
200
201                         public Credential getResponseSigningCredential() {
202                                 return responseSigningCredential;
203                         }
204
205                         public Credential getAssertionSigningCredential() {
206                                 return null;
207                         }
208
209                 }
210         }
211
212         /**
213          * Relying party implementation wrapper for relying parties that are federations.
214          * 
215          * @author Walter Hoehn
216          */
217         class RelyingPartyGroupWrapper implements RelyingParty, HSRelyingParty, AARelyingParty {
218
219                 private RelyingParty wrapped;
220                 private String providerId;
221
222                 RelyingPartyGroupWrapper(RelyingParty wrapped, String providerId) {
223                         this.wrapped = wrapped;
224                         this.providerId = providerId;
225                 }
226
227                 public String getName() {
228                         return wrapped.getName();
229                 }
230
231                 public boolean isLegacyProvider() {
232                         return false;
233                 }
234
235                 public IdentityProvider getIdentityProvider() {
236                         return wrapped.getIdentityProvider();
237                 }
238
239                 public String getProviderId() {
240                         return providerId;
241                 }
242                 public String getHSNameFormatId() {
243                         if (!(wrapped instanceof HSRelyingParty)) {
244                                 return null;
245                         }
246                         return ((HSRelyingParty) wrapped).getHSNameFormatId();
247                 }
248
249                 public URL getAAUrl() {
250                         if (!(wrapped instanceof HSRelyingParty)) {
251                                 return null;
252                         }
253                         return ((HSRelyingParty) wrapped).getAAUrl();
254                 }
255
256                 public URI getDefaultAuthMethod() {
257                         if (!(wrapped instanceof HSRelyingParty)) {
258                                 return null;
259                         }
260                         return ((HSRelyingParty) wrapped).getDefaultAuthMethod();
261                 }
262
263                 public boolean passThruErrors() {
264                         if (!(wrapped instanceof AARelyingParty)) {
265                                 return false;
266                         }
267                         return ((AARelyingParty) wrapped).passThruErrors();
268                 }
269         }
270
271         /**
272          * Relying party implementation wrapper for anonymous service providers.
273          *
274          * @author Walter Hoehn
275          */
276         protected class UnknownProviderWrapper implements RelyingParty, HSRelyingParty, AARelyingParty {
277                 protected RelyingParty wrapped;
278
279                 protected UnknownProviderWrapper(RelyingParty wrapped) {
280                         this.wrapped = wrapped;
281                 }
282
283                 public String getName() {
284                         return wrapped.getName();
285                 }
286
287                 public IdentityProvider getIdentityProvider() {
288                         return wrapped.getIdentityProvider();
289                 }
290
291                 public String getProviderId() {
292                         return null;
293                 }
294
295                 public String getHSNameFormatId() {
296                         if (!(wrapped instanceof HSRelyingParty)) {
297                                 return null;
298                         }
299                         return ((HSRelyingParty) wrapped).getHSNameFormatId();
300                 }
301
302                 public boolean isLegacyProvider() {
303                         if (!(wrapped instanceof HSRelyingParty)) {
304                                 return false;
305                         }
306                         return ((HSRelyingParty) wrapped).isLegacyProvider();
307                 }
308
309                 public URL getAAUrl() {
310                         if (!(wrapped instanceof HSRelyingParty)) {
311                                 return null;
312                         }
313                         return ((HSRelyingParty) wrapped).getAAUrl();
314                 }
315
316                 public URI getDefaultAuthMethod() {
317                         if (!(wrapped instanceof HSRelyingParty)) {
318                                 return null;
319                         }
320                         return ((HSRelyingParty) wrapped).getDefaultAuthMethod();
321                 }
322
323                 public boolean passThruErrors() {
324                         if (!(wrapped instanceof AARelyingParty)) {
325                                 return false;
326                         }
327                         return ((AARelyingParty) wrapped).passThruErrors();
328                 }
329         }
330
331 }