Added support for using the default relying party in cases where no metadata is avail...
[java-idp.git] / src / edu / internet2 / middleware / shibboleth / common / ServiceProviderMapper.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
6  * above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other
7  * materials provided with the distribution, if any, must include the following acknowledgment: "This product includes
8  * software developed by the University Corporation for Advanced Internet Development <http://www.ucaid.edu> Internet2
9  * Project. 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,
11  * nor 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
13  * contact shibboleth@shibboleth.org Products derived from this software may not be called Shibboleth, Internet2,
14  * UCAID, or the University Corporation for Advanced Internet Development, nor may Shibboleth appear in their name,
15  * without prior written permission of the University Corporation for Advanced Internet Development. THIS SOFTWARE IS
16  * PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES,
17  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
18  * NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS
19  * WITH LICENSEE. IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY CORPORATION FOR ADVANCED
20  * INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
23  * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24  * POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 package edu.internet2.middleware.shibboleth.common;
28
29 import java.net.URI;
30 import java.net.URL;
31 import java.util.HashMap;
32 import java.util.Map;
33
34 import org.apache.log4j.Logger;
35 import org.w3c.dom.Element;
36
37 import edu.internet2.middleware.shibboleth.aa.AARelyingParty;
38 import edu.internet2.middleware.shibboleth.hs.HSRelyingParty;
39 import edu.internet2.middleware.shibboleth.metadata.Metadata;
40 import edu.internet2.middleware.shibboleth.metadata.Provider;
41
42 /**
43  * Base class for determining the effective relying party from the unique id of the service provider. Checks first for
44  * an exact match on the service provider, then for membership in a federation. Uses the default relying party if
45  * neither is found.
46  * 
47  * @author Walter Hoehn
48  */
49 public abstract class ServiceProviderMapper {
50
51         private static Logger   log                             = Logger.getLogger(ServiceProviderMapper.class.getName());
52         protected Map                   relyingParties  = new HashMap();
53         private Metadata                metaData;
54
55         /**
56          * @param metaData
57          */
58         public ServiceProviderMapper(Metadata metaData) {
59                 this.metaData = metaData;
60         }
61
62         protected abstract ShibbolethOriginConfig getOriginConfig();
63
64         protected void verifyDefaultParty(ShibbolethOriginConfig configuration) throws ServiceProviderMapperException {
65                 //Verify we have a proper default party
66                 String defaultParty = configuration.getDefaultRelyingPartyName();
67                 if (defaultParty == null || defaultParty.equals("")) {
68                         if (relyingParties.size() != 1) {
69                                 log
70                                                 .error("Default Relying Party not specified.  Add a (defaultRelyingParty) attribute to <ShibbolethOriginConfig>.");
71                                 throw new ServiceProviderMapperException("Required configuration not specified.");
72                         } else {
73                                 log.debug("Only one Relying Party loaded.  Using this as the default.");
74                         }
75                 }
76                 log.debug("Default Relying Party set to: (" + defaultParty + ").");
77                 if (!relyingParties.containsKey(defaultParty)) {
78                         log.error("Default Relying Party refers to a Relying Party that has not been loaded.");
79                         throw new ServiceProviderMapperException("Invalid configuration (Default Relying Party).");
80                 }
81         }
82
83         protected RelyingParty getRelyingPartyImpl(String providerIdFromTarget) {
84
85                 //Null request, send the default
86                 if (providerIdFromTarget == null) {
87                         RelyingParty relyingParty = getDefaultRelyingParty();
88                         log.info("Using default Relying Party: (" + relyingParty.getName() + ").");
89                         return new UnknownProviderWrapper(relyingParty, providerIdFromTarget);
90                 }
91
92                 //Look for a configuration for the specific relying party
93                 if (relyingParties.containsKey(providerIdFromTarget)) {
94                         log.info("Found Relying Party for (" + providerIdFromTarget + ").");
95                         return (RelyingParty) relyingParties.get(providerIdFromTarget);
96                 }
97
98                 //Next, check to see if the relying party is in any groups
99                 RelyingParty groupParty = findRelyingPartyByGroup(providerIdFromTarget);
100                 if (groupParty != null) {
101                         log.info("Provider is a member of Relying Party (" + groupParty.getName() + ").");
102                         return new RelyingPartyGroupWrapper(groupParty, providerIdFromTarget);
103                 }
104
105                 //OK, we can't find it... just send the default
106                 RelyingParty relyingParty = getDefaultRelyingParty();
107                 log.info("Could not locate Relying Party configuration for (" + providerIdFromTarget
108                                 + ").  Using default Relying Party: (" + relyingParty.getName() + ").");
109                 return new UnknownProviderWrapper(relyingParty, providerIdFromTarget);
110         }
111
112         private RelyingParty findRelyingPartyByGroup(String providerIdFromTarget) {
113
114                 Provider provider = metaData.lookup(providerIdFromTarget);
115                 if (provider != null) {
116                         String[] groups = provider.getGroups();
117                         for (int i = 0; groups.length > i; i++) {
118                                 //We need to iterate backward because the groups go from least to most specific
119                                 String group = groups[groups.length - 1 - i];
120                                 if (relyingParties.containsKey(group)) {
121                                         log.info("Found matching Relying Party for group (" + group + ").");
122                                         return (RelyingParty) relyingParties.get(group);
123                                 } else {
124                                         log
125                                                         .debug("Provider is a member of group (" + group
126                                                                         + "), but no matching Relying Party was found.");
127                                 }
128                         }
129                 }
130                 return null;
131         }
132
133         public RelyingParty getDefaultRelyingParty() {
134
135                 //If there is no explicit default, pick the single configured Relying
136                 // Party
137                 String defaultParty = getOriginConfig().getDefaultRelyingPartyName();
138                 if (defaultParty == null || defaultParty.equals("")) {
139                         return (RelyingParty) relyingParties.values().iterator().next();
140                 }
141
142                 //If we do have a default specified, use it...
143                 return (RelyingParty) relyingParties.get(defaultParty);
144         }
145
146         /**
147          * Base relying party implementation.
148          * 
149          * @author Walter Hoehn
150          */
151         protected abstract class BaseRelyingPartyImpl implements RelyingParty {
152
153                 protected RelyingPartyIdentityProvider  identityProvider;
154                 protected String                                                name;
155                 protected String                                                overridenOriginProviderId;
156
157                 public BaseRelyingPartyImpl(Element partyConfig) throws ServiceProviderMapperException {
158
159                         //Get party name
160                         name = ((Element) partyConfig).getAttribute("name");
161                         if (name == null || name.equals("")) {
162                                 log.error("Relying Party name not set.  Add a (name) attribute to <RelyingParty>.");
163                                 throw new ServiceProviderMapperException("Required configuration not specified.");
164                         }
165                         log.debug("Loading Relying Party: (" + name + ").");
166
167                         //Process overrides for global data
168                         String attribute = ((Element) partyConfig).getAttribute("providerId");
169                         if (attribute != null && !attribute.equals("")) {
170                                 log.debug("Overriding providerId for Relying Pary (" + name + ") with (" + attribute + ").");
171                                 overridenOriginProviderId = attribute;
172                         }
173
174                 }
175
176                 public String getProviderId() {
177                         return name;
178                 }
179
180                 public String getName() {
181                         return name;
182                 }
183
184                 public IdentityProvider getIdentityProvider() {
185                         return identityProvider;
186                 }
187
188                 /**
189                  * Default identity provider implementation.
190                  * 
191                  * @author Walter Hoehn
192                  */
193                 protected class RelyingPartyIdentityProvider implements IdentityProvider {
194
195                         private String          providerId;
196                         private Credential      responseSigningCredential;
197                         private Credential      assertionSigningCredential;
198
199                         public RelyingPartyIdentityProvider(String providerId, Credential responseSigningCred) {
200                                 this.providerId = providerId;
201                                 this.responseSigningCredential = responseSigningCred;
202                         }
203
204                         public RelyingPartyIdentityProvider(String providerId, Credential responseSigningCred,
205                                         Credential assertionSigningCred) {
206                                 this.providerId = providerId;
207                                 this.responseSigningCredential = responseSigningCred;
208                                 this.assertionSigningCredential = assertionSigningCred;
209                         }
210
211                         public String getProviderId() {
212                                 return providerId;
213                         }
214
215                         public Credential getResponseSigningCredential() {
216                                 return responseSigningCredential;
217                         }
218
219                         public Credential getAssertionSigningCredential() {
220                                 return assertionSigningCredential;
221                         }
222
223                 }
224         }
225
226         /**
227          * Relying party implementation wrapper for relying parties that are federations.
228          * 
229          * @author Walter Hoehn
230          */
231         class RelyingPartyGroupWrapper implements RelyingParty, HSRelyingParty, AARelyingParty {
232
233                 private RelyingParty    wrapped;
234                 private String                  providerId;
235
236                 RelyingPartyGroupWrapper(RelyingParty wrapped, String providerId) {
237                         this.wrapped = wrapped;
238                         this.providerId = providerId;
239                 }
240
241                 public String getName() {
242                         return wrapped.getName();
243                 }
244
245                 public boolean isLegacyProvider() {
246                         return false;
247                 }
248
249                 public IdentityProvider getIdentityProvider() {
250                         return wrapped.getIdentityProvider();
251                 }
252
253                 public String getProviderId() {
254                         return providerId;
255                 }
256
257                 public String getHSNameFormatId() {
258                         if (!(wrapped instanceof HSRelyingParty)) {
259                                 return null;
260                         }
261                         return ((HSRelyingParty) wrapped).getHSNameFormatId();
262                 }
263
264                 public URL getAAUrl() {
265                         if (!(wrapped instanceof HSRelyingParty)) {
266                                 return null;
267                         }
268                         return ((HSRelyingParty) wrapped).getAAUrl();
269                 }
270
271                 public URI getDefaultAuthMethod() {
272                         if (!(wrapped instanceof HSRelyingParty)) {
273                                 return null;
274                         }
275                         return ((HSRelyingParty) wrapped).getDefaultAuthMethod();
276                 }
277
278                 public boolean passThruErrors() {
279                         if (!(wrapped instanceof AARelyingParty)) {
280                                 return false;
281                         }
282                         return ((AARelyingParty) wrapped).passThruErrors();
283                 }
284         }
285
286         /**
287          * Relying party implementation wrapper for anonymous service providers.
288          * 
289          * @author Walter Hoehn
290          */
291         protected class UnknownProviderWrapper implements RelyingParty, HSRelyingParty, AARelyingParty {
292
293                 protected RelyingParty  wrapped;
294                 protected String                providerId;
295
296                 protected UnknownProviderWrapper(RelyingParty wrapped, String providerId) {
297                         this.wrapped = wrapped;
298                         this.providerId = providerId;
299                 }
300
301                 public String getName() {
302                         return wrapped.getName();
303                 }
304
305                 public IdentityProvider getIdentityProvider() {
306                         return wrapped.getIdentityProvider();
307                 }
308
309                 public String getProviderId() {
310                         return providerId;
311                 }
312
313                 public String getHSNameFormatId() {
314                         if (!(wrapped instanceof HSRelyingParty)) {
315                                 return null;
316                         }
317                         return ((HSRelyingParty) wrapped).getHSNameFormatId();
318                 }
319
320                 public boolean isLegacyProvider() {
321                         if (!(wrapped instanceof HSRelyingParty)) {
322                                 return false;
323                         }
324                         return ((HSRelyingParty) wrapped).isLegacyProvider();
325                 }
326
327                 public URL getAAUrl() {
328                         if (!(wrapped instanceof HSRelyingParty)) {
329                                 return null;
330                         }
331                         return ((HSRelyingParty) wrapped).getAAUrl();
332                 }
333
334                 public URI getDefaultAuthMethod() {
335                         if (!(wrapped instanceof HSRelyingParty)) {
336                                 return null;
337                         }
338                         return ((HSRelyingParty) wrapped).getDefaultAuthMethod();
339                 }
340
341                 public boolean passThruErrors() {
342                         if (!(wrapped instanceof AARelyingParty)) {
343                                 return false;
344                         }
345                         return ((AARelyingParty) wrapped).passThruErrors();
346                 }
347         }
348
349 }