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