Federation metadata is now used for Relying Party lookup when a specific entry does...
[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                 //Look for a configuration for the specific relying party
86                 if (relyingParties.containsKey(providerIdFromTarget)) {
87                         log.info("Found Relying Party for (" + providerIdFromTarget + ").");
88                         return (RelyingParty) relyingParties.get(providerIdFromTarget);
89                 }
90
91                 //Next, check to see if the relying party is in any groups
92                 RelyingParty groupParty = findRelyingPartyByGroup(providerIdFromTarget);
93                 if (groupParty != null) {
94                         log.info("Provider is a member of Relying Party (" + groupParty.getName() + ").");
95                         return new RelyingPartyGroupWrapper(groupParty, providerIdFromTarget);
96                 }
97
98                 //OK, just send the default
99                 log.info("Could not locate Relying Party configuration for (" + providerIdFromTarget
100                                 + ").  Using default Relying Party.");
101                 return new UnknownProviderWrapper(getDefaultRelyingPatry(), providerIdFromTarget);
102         }
103
104         private RelyingParty findRelyingPartyByGroup(String providerIdFromTarget) {
105
106                 Provider provider = metaData.lookup(providerIdFromTarget);
107                 if (provider != null) {
108                         String[] groups = provider.getGroups();
109                         for (int i = 0; groups.length > i; i++) {
110                                 //We need to iterate backward because the groups go from least to most specific
111                                 String group = groups[groups.length - 1 - i];
112                                 if (relyingParties.containsKey(group)) {
113                                         log.info("Found matching Relying Party for group (" + group + ").");
114                                         return (RelyingParty) relyingParties.get(group);
115                                 } else {
116                                         log
117                                                         .debug("Provider is a member of group (" + group
118                                                                         + "), but no matching Relying Party was found.");
119                                 }
120                         }
121                 }
122                 return null;
123         }
124
125         protected RelyingParty getDefaultRelyingPatry() {
126
127                 //If there is no explicit default, pick the single configured Relying
128                 // Party
129                 String defaultParty = getOriginConfig().getDefaultRelyingPartyName();
130                 if (defaultParty == null || defaultParty.equals("")) {
131                         return (RelyingParty) relyingParties.values().iterator().next();
132                 }
133
134                 //If we do have a default specified, use it...
135                 return (RelyingParty) relyingParties.get(defaultParty);
136         }
137
138         /**
139          * Base relying party implementation.
140          * 
141          * @author Walter Hoehn
142          */
143         protected abstract class BaseRelyingPartyImpl implements RelyingParty {
144
145                 protected RelyingPartyIdentityProvider  identityProvider;
146                 protected String                                                name;
147                 protected String                                                overridenOriginProviderId;
148
149                 public BaseRelyingPartyImpl(Element partyConfig) throws ServiceProviderMapperException {
150
151                         //Get party name
152                         name = ((Element) partyConfig).getAttribute("name");
153                         if (name == null || name.equals("")) {
154                                 log.error("Relying Party name not set.  Add a (name) attribute to <RelyingParty>.");
155                                 throw new ServiceProviderMapperException("Required configuration not specified.");
156                         }
157                         log.debug("Loading Relying Party: (" + name + ").");
158
159                         //Process overrides for global data
160                         String attribute = ((Element) partyConfig).getAttribute("providerId");
161                         if (attribute != null && !attribute.equals("")) {
162                                 log.debug("Overriding providerId for Relying Pary (" + name + ") with (" + attribute + ").");
163                                 overridenOriginProviderId = attribute;
164                         }
165
166                 }
167
168                 public String getProviderId() {
169                         return name;
170                 }
171
172                 public String getName() {
173                         return name;
174                 }
175
176                 public IdentityProvider getIdentityProvider() {
177                         return identityProvider;
178                 }
179
180                 /**
181                  * Default identity provider implementation.
182                  * 
183                  * @author Walter Hoehn
184                  */
185                 protected class RelyingPartyIdentityProvider implements IdentityProvider {
186
187                         private String          providerId;
188                         private Credential      responseSigningCredential;
189                         private Credential      assertionSigningCredential;
190
191                         public RelyingPartyIdentityProvider(String providerId, Credential responseSigningCred) {
192                                 this.providerId = providerId;
193                                 this.responseSigningCredential = responseSigningCred;
194                         }
195
196                         public RelyingPartyIdentityProvider(String providerId, Credential responseSigningCred,
197                                         Credential assertionSigningCred) {
198                                 this.providerId = providerId;
199                                 this.responseSigningCredential = responseSigningCred;
200                                 this.assertionSigningCredential = assertionSigningCred;
201                         }
202
203                         public String getProviderId() {
204                                 return providerId;
205                         }
206
207                         public Credential getResponseSigningCredential() {
208                                 return responseSigningCredential;
209                         }
210
211                         public Credential getAssertionSigningCredential() {
212                                 return assertionSigningCredential;
213                         }
214
215                 }
216         }
217
218         /**
219          * Relying party implementation wrapper for relying parties that are federations.
220          * 
221          * @author Walter Hoehn
222          */
223         class RelyingPartyGroupWrapper implements RelyingParty, HSRelyingParty, AARelyingParty {
224
225                 private RelyingParty    wrapped;
226                 private String                  providerId;
227
228                 RelyingPartyGroupWrapper(RelyingParty wrapped, String providerId) {
229                         this.wrapped = wrapped;
230                         this.providerId = providerId;
231                 }
232
233                 public String getName() {
234                         return wrapped.getName();
235                 }
236
237                 public boolean isLegacyProvider() {
238                         return false;
239                 }
240
241                 public IdentityProvider getIdentityProvider() {
242                         return wrapped.getIdentityProvider();
243                 }
244
245                 public String getProviderId() {
246                         return providerId;
247                 }
248
249                 public String getHSNameFormatId() {
250                         if (!(wrapped instanceof HSRelyingParty)) {
251                                 return null;
252                         }
253                         return ((HSRelyingParty) wrapped).getHSNameFormatId();
254                 }
255
256                 public URL getAAUrl() {
257                         if (!(wrapped instanceof HSRelyingParty)) {
258                                 return null;
259                         }
260                         return ((HSRelyingParty) wrapped).getAAUrl();
261                 }
262
263                 public URI getDefaultAuthMethod() {
264                         if (!(wrapped instanceof HSRelyingParty)) {
265                                 return null;
266                         }
267                         return ((HSRelyingParty) wrapped).getDefaultAuthMethod();
268                 }
269
270                 public boolean passThruErrors() {
271                         if (!(wrapped instanceof AARelyingParty)) {
272                                 return false;
273                         }
274                         return ((AARelyingParty) wrapped).passThruErrors();
275                 }
276         }
277
278         /**
279          * Relying party implementation wrapper for anonymous service providers.
280          * 
281          * @author Walter Hoehn
282          */
283         protected class UnknownProviderWrapper implements RelyingParty, HSRelyingParty, AARelyingParty {
284
285                 protected RelyingParty  wrapped;
286                 protected String                providerId;
287
288                 protected UnknownProviderWrapper(RelyingParty wrapped, String providerId) {
289                         this.wrapped = wrapped;
290                         this.providerId = providerId;
291                 }
292
293                 public String getName() {
294                         return wrapped.getName();
295                 }
296
297                 public IdentityProvider getIdentityProvider() {
298                         return wrapped.getIdentityProvider();
299                 }
300
301                 public String getProviderId() {
302                         return providerId;
303                 }
304
305                 public String getHSNameFormatId() {
306                         if (!(wrapped instanceof HSRelyingParty)) {
307                                 return null;
308                         }
309                         return ((HSRelyingParty) wrapped).getHSNameFormatId();
310                 }
311
312                 public boolean isLegacyProvider() {
313                         if (!(wrapped instanceof HSRelyingParty)) {
314                                 return false;
315                         }
316                         return ((HSRelyingParty) wrapped).isLegacyProvider();
317                 }
318
319                 public URL getAAUrl() {
320                         if (!(wrapped instanceof HSRelyingParty)) {
321                                 return null;
322                         }
323                         return ((HSRelyingParty) wrapped).getAAUrl();
324                 }
325
326                 public URI getDefaultAuthMethod() {
327                         if (!(wrapped instanceof HSRelyingParty)) {
328                                 return null;
329                         }
330                         return ((HSRelyingParty) wrapped).getDefaultAuthMethod();
331                 }
332
333                 public boolean passThruErrors() {
334                         if (!(wrapped instanceof AARelyingParty)) {
335                                 return false;
336                         }
337                         return ((AARelyingParty) wrapped).passThruErrors();
338                 }
339         }
340
341 }