2 * The Shibboleth License, Version 1. Copyright (c) 2002 University Corporation
3 * for Advanced Internet Development, Inc. All rights reserved
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
9 * Redistributions of source code must retain the above copyright notice, this
10 * list of conditions and the following disclaimer.
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.
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
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.
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.
47 package edu.internet2.middleware.shibboleth.common;
49 import java.util.HashMap;
51 import java.util.Properties;
53 import org.apache.log4j.Logger;
54 import org.w3c.dom.Element;
55 import org.w3c.dom.NodeList;
57 import edu.internet2.middleware.shibboleth.hs.HSNameMapper;
60 * @author Walter Hoehn
63 public class ServiceProviderMapper {
65 private static Logger log = Logger.getLogger(ShibbolethOriginConfig.class.getName());
66 private ShibbolethOriginConfig configuration;
67 private Credentials credentials;
68 private HSNameMapper nameMapper;
69 private Map relyingParties = new HashMap();
71 public ServiceProviderMapper(
73 ShibbolethOriginConfig configuration,
74 Credentials credentials,
75 HSNameMapper nameMapper)
76 throws ServiceProviderMapperException {
78 this.configuration = configuration;
79 this.credentials = credentials;
80 this.nameMapper = nameMapper;
82 NodeList itemElements =
83 rawConfig.getElementsByTagNameNS(ShibbolethOriginConfig.originConfigNamespace, "RelyingParty");
85 for (int i = 0; i < itemElements.getLength(); i++) {
86 addHSRelyingParty((Element) itemElements.item(i));
89 verifyDefaultParty(configuration);
92 public ServiceProviderMapper(Element rawConfig, ShibbolethOriginConfig configuration)
93 throws ServiceProviderMapperException {
95 this.configuration = configuration;
97 NodeList itemElements =
98 rawConfig.getElementsByTagNameNS(ShibbolethOriginConfig.originConfigNamespace, "RelyingParty");
100 for (int i = 0; i < itemElements.getLength(); i++) {
101 addAARelyingParty((Element) itemElements.item(i));
104 verifyDefaultParty(configuration);
107 private void verifyDefaultParty(ShibbolethOriginConfig configuration) throws ServiceProviderMapperException {
108 //Verify we have a proper default party
109 String defaultParty =
110 configuration.getConfigProperty(
111 "edu.internet2.middleware.shibboleth.common.RelyingParty.defaultRelyingParty");
112 if (defaultParty == null || defaultParty.equals("")) {
113 if (relyingParties.size() != 1) {
115 "Default Relying Party not specified. Add a (defaultRelyingParty) attribute to <ShibbolethOriginConfig>.");
116 throw new ServiceProviderMapperException("Required configuration not specified.");
118 log.debug("Only one Relying Party loaded. Using this as the default.");
121 log.debug("Default Relying Party set to: (" + defaultParty + ").");
122 if (!relyingParties.containsKey(defaultParty)) {
123 log.error("Default Relying Party refers to a Relying Party that has not been loaded.");
124 throw new ServiceProviderMapperException("Invalid configuration (Default Relying Party).");
128 private void addHSRelyingParty(Element e) throws ServiceProviderMapperException {
130 log.debug("Found a Relying Party.");
132 if (e.getLocalName().equals("RelyingParty")) {
133 RelyingParty party = new RelyingPartyImpl(e, configuration, credentials, nameMapper);
134 log.debug("Relying Party (" + party.getName() + ") loaded.");
135 relyingParties.put(party.getName(), party);
137 } catch (ServiceProviderMapperException exc) {
138 log.error("Encountered an error while attempting to load Relying Party configuration. Skipping...");
142 private void addAARelyingParty(Element e) throws ServiceProviderMapperException {
144 log.debug("Found a Relying Party.");
146 if (e.getLocalName().equals("RelyingParty")) {
147 RelyingParty party = new RelyingPartyImpl(e, configuration);
148 log.debug("Relying Party (" + party.getName() + ") loaded.");
149 relyingParties.put(party.getName(), party);
151 } catch (ServiceProviderMapperException exc) {
152 log.error("Encountered an error while attempting to load Relying Party configuration. Skipping...");
155 public RelyingParty getRelyingParty(String providerIdFromTarget) {
157 //If the target did not send a Provider Id, then assume it is a Shib
158 // 1.1 or older target
159 if (providerIdFromTarget == null || providerIdFromTarget.equals("")) {
160 log.info("Request is from legacy shib target. Selecting default Relying Party.");
161 return new LegacyWrapper(getDefaultRelyingPatry());
164 //Look for a configuration for the specific relying party
165 if (relyingParties.containsKey(providerIdFromTarget)) {
166 log.info("Found Relying Party for (" + providerIdFromTarget + ").");
167 return (RelyingParty) relyingParties.get(providerIdFromTarget);
170 //Next, check to see if the relying party is in any groups
171 RelyingParty groupParty = findRelyingPartyByGroup(providerIdFromTarget);
172 if (groupParty != null) {
173 log.info("Provider is a member of Relying Party (" + groupParty.getName() + ").");
174 return new RelyingPartyGroupWrapper(groupParty, providerIdFromTarget);
177 //OK, just send the default
179 "Could not locate Relying Party configuration for ("
180 + providerIdFromTarget
181 + "). Using default Relying Party.");
182 return new UnknownProviderWrapper(getDefaultRelyingPatry());
185 private RelyingParty findRelyingPartyByGroup(String providerIdFromTarget) {
187 // TODO This is totally a stub and needs to be based on target metadata
189 if (providerIdFromTarget.startsWith("urn:mace:inqueue:")) {
190 if (relyingParties.containsKey("urn:mace:inqueue")) {
191 return (RelyingParty) relyingParties.get("urn:mace:inqueue");
197 private RelyingParty getDefaultRelyingPatry() {
199 //If there is no explicit default, pick the single configured Relying
201 String defaultParty =
202 configuration.getConfigProperty(
203 "edu.internet2.middleware.shibboleth.common.RelyingParty.defaultRelyingParty");
204 if (defaultParty == null || defaultParty.equals("")) {
205 return (RelyingParty) relyingParties.values().iterator().next();
208 //If we do have a default specified, use it...
209 return (RelyingParty) relyingParties.get(defaultParty);
212 class RelyingPartyImpl implements RelyingParty {
214 protected ShibbolethOriginConfig originConfig;
215 protected Properties partyOverrides = new Properties();
216 protected RelyingPartyIdentityProvider identityProvider;
217 protected String name;
218 protected String hsNameFormatId;
220 public RelyingPartyImpl(
222 ShibbolethOriginConfig globalConfig,
223 Credentials credentials,
224 HSNameMapper nameMapper)
225 throws ServiceProviderMapperException {
227 this(partyConfig, globalConfig);
229 //Load a credential for signing
230 String credentialName = ((Element) partyConfig).getAttribute("signingCredential");
231 Credential credential = credentials.getCredential(credentialName);
233 if (credential == null) {
234 if (credentialName == null || credentialName.equals("")) {
236 "Relying Party credential not set. Add a (signingCredential) attribute to <RelyingParty>.");
237 throw new ServiceProviderMapperException("Required configuration not specified.");
240 "Relying Party credential not set. Add a (signingCredential) attribute to <RelyingParty>.");
241 throw new ServiceProviderMapperException("Required configuration not specified.");
245 //Load and verify the name format that the HS should use in
246 //assertions for this RelyingParty
247 NodeList hsNameFormats =
248 ((Element) partyConfig).getElementsByTagNameNS(
249 ShibbolethOriginConfig.originConfigNamespace,
251 //If no specification. Make sure we have a default mapping
252 if (hsNameFormats.getLength() < 1) {
253 if (nameMapper.getNameIdentifierMappingById(null) == null) {
254 log.error("Relying Party HS Name Format not set. Add a <HSNameFormat> element to <RelyingParty>.");
255 throw new ServiceProviderMapperException("Required configuration not specified.");
259 //We do have a specification, so make sure it points to a
260 // valid Name Mapping
261 if (hsNameFormats.getLength() > 1) {
263 "Found multiple HSNameFormat specifications for Relying Party ("
265 + "). Ignoring all but the first.");
268 String hsNameFormatId = ((Element) hsNameFormats.item(0)).getAttribute("nameMapping");
269 if (hsNameFormatId == null || hsNameFormatId.equals("")) {
270 log.error("HS Name Format mapping not set. Add a (nameMapping) attribute to <HSNameFormat>.");
271 throw new ServiceProviderMapperException("Required configuration not specified.");
274 if (nameMapper.getNameIdentifierMappingById(hsNameFormatId) == null) {
275 log.error("Relying Party HS Name Format refers to a name mapping that is not loaded.");
276 throw new ServiceProviderMapperException("Required configuration not specified.");
280 new RelyingPartyIdentityProvider(
281 getConfigProperty("edu.internet2.middleware.shibboleth.hs.HandleServlet.providerId"),
285 public RelyingPartyImpl(Element partyConfig, ShibbolethOriginConfig globalConfig)
286 throws ServiceProviderMapperException {
288 //Use global config for defaults
289 this.originConfig = globalConfig;
292 name = ((Element) partyConfig).getAttribute("name");
293 if (name == null || name.equals("")) {
294 log.error("Relying Party name not set. Add a (name) attribute to <RelyingParty>.");
295 throw new ServiceProviderMapperException("Required configuration not specified.");
297 log.debug("Loading Relying Party: (" + name + ").");
299 //Process overrides for global data
300 String attribute = ((Element) partyConfig).getAttribute("providerId");
301 if (attribute != null && !attribute.equals("")) {
302 log.debug("Overriding providerId for Relying Pary (" + name + ") with (" + attribute + ").");
303 partyOverrides.setProperty(
304 "edu.internet2.middleware.shibboleth.hs.HandleServlet.providerId",
308 attribute = ((Element) partyConfig).getAttribute("AAUrl");
309 if (attribute != null && !attribute.equals("")) {
310 log.debug("Overriding AAUrl for Relying Pary (" + name + ") with (" + attribute + ").");
311 partyOverrides.setProperty("edu.internet2.middleware.shibboleth.hs.HandleServlet.AAUrl", attribute);
314 attribute = ((Element) partyConfig).getAttribute("defaultAuthMethod");
315 if (attribute != null && !attribute.equals("")) {
316 log.debug("Overriding defaultAuthMethod for Relying Pary (" + name + ") with (" + attribute + ").");
317 partyOverrides.setProperty(
318 "edu.internet2.middleware.shibboleth.hs.HandleServlet.defaultAuthMethod",
323 new RelyingPartyIdentityProvider(
324 getConfigProperty("edu.internet2.middleware.shibboleth.hs.HandleServlet.providerId"),
329 public String getProviderId() {
333 public String getName() {
337 public boolean isLegacyProvider() {
341 public String getConfigProperty(String key) {
342 if (partyOverrides.containsKey(key)) {
343 return partyOverrides.getProperty(key);
345 return originConfig.getConfigProperty(key);
348 public String getHSNameFormatId() {
352 public IdentityProvider getIdentityProvider() {
353 return identityProvider;
356 class RelyingPartyIdentityProvider implements IdentityProvider {
358 private String providerId;
359 private Credential responseSigningCredential;
361 RelyingPartyIdentityProvider(String providerId, Credential responseSigningCred) {
362 this.providerId = providerId;
363 this.responseSigningCredential = responseSigningCred;
366 public String getProviderId() {
370 public Credential getResponseSigningCredential() {
371 return responseSigningCredential;
374 public Credential getAssertionSigningCredential() {
381 class RelyingPartyGroupWrapper implements RelyingParty {
383 private RelyingParty wrapped;
384 private String providerId;
386 RelyingPartyGroupWrapper(RelyingParty wrapped, String providerId) {
387 this.wrapped = wrapped;
388 this.providerId = providerId;
391 public String getName() {
392 return wrapped.getName();
395 public String getConfigProperty(String key) {
396 return wrapped.getConfigProperty(key);
399 public boolean isLegacyProvider() {
403 public String getHSNameFormatId() {
404 return wrapped.getHSNameFormatId();
407 public IdentityProvider getIdentityProvider() {
408 return wrapped.getIdentityProvider();
411 public String getProviderId() {
416 class UnknownProviderWrapper implements RelyingParty {
417 private RelyingParty wrapped;
419 UnknownProviderWrapper(RelyingParty wrapped) {
420 this.wrapped = wrapped;
423 public String getName() {
424 return wrapped.getName();
427 public String getConfigProperty(String key) {
428 return wrapped.getConfigProperty(key);
431 public boolean isLegacyProvider() {
432 return wrapped.isLegacyProvider();
435 public String getHSNameFormatId() {
436 return wrapped.getHSNameFormatId();
439 public IdentityProvider getIdentityProvider() {
440 return wrapped.getIdentityProvider();
443 public String getProviderId() {
448 class LegacyWrapper extends UnknownProviderWrapper {
450 LegacyWrapper(RelyingParty wrapped) {
453 public boolean isLegacyProvider() {