Profile defaults for attribute push can now be overidden per Relying Party.
[java-idp.git] / src / edu / internet2 / middleware / shibboleth / idp / provider / ShibbolethV1SSOHandler.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 above
6  * copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials
7  * provided with the distribution, if any, must include the following acknowledgment: "This product includes software
8  * developed by the University Corporation for Advanced Internet Development <http://www.ucaid.edu> Internet2 Project.
9  * 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, nor
11  * 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 contact
13  * shibboleth@shibboleth.org Products derived from this software may not be called Shibboleth, Internet2, UCAID, or the
14  * University Corporation for Advanced Internet Development, nor may Shibboleth appear in their name, without prior
15  * written permission of the University Corporation for Advanced Internet Development. THIS SOFTWARE IS PROVIDED BY THE
16  * COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE
18  * DISCLAIMED AND THE ENTIRE RISK OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE. IN NO
19  * EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC.
20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
23  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 package edu.internet2.middleware.shibboleth.idp.provider;
27
28 import java.io.IOException;
29 import java.io.UnsupportedEncodingException;
30 import java.net.URLEncoder;
31 import java.util.ArrayList;
32 import java.util.Arrays;
33 import java.util.Collections;
34 import java.util.Date;
35 import java.util.Iterator;
36 import java.util.Vector;
37
38 import javax.servlet.RequestDispatcher;
39 import javax.servlet.ServletException;
40 import javax.servlet.http.HttpServletRequest;
41 import javax.servlet.http.HttpServletResponse;
42 import javax.xml.namespace.QName;
43
44 import org.apache.log4j.Logger;
45 import org.opensaml.SAMLAssertion;
46 import org.opensaml.SAMLAttribute;
47 import org.opensaml.SAMLAttributeStatement;
48 import org.opensaml.SAMLAudienceRestrictionCondition;
49 import org.opensaml.SAMLAuthenticationStatement;
50 import org.opensaml.SAMLAuthorityBinding;
51 import org.opensaml.SAMLBinding;
52 import org.opensaml.SAMLBrowserProfile;
53 import org.opensaml.SAMLCondition;
54 import org.opensaml.SAMLException;
55 import org.opensaml.SAMLNameIdentifier;
56 import org.opensaml.SAMLRequest;
57 import org.opensaml.SAMLResponse;
58 import org.opensaml.SAMLStatement;
59 import org.opensaml.SAMLSubject;
60 import org.opensaml.artifact.Artifact;
61 import org.w3c.dom.Document;
62 import org.w3c.dom.Element;
63
64 import sun.misc.BASE64Decoder;
65 import edu.internet2.middleware.shibboleth.aa.AAException;
66 import edu.internet2.middleware.shibboleth.common.AuthNPrincipal;
67 import edu.internet2.middleware.shibboleth.common.NameIdentifierMappingException;
68 import edu.internet2.middleware.shibboleth.common.RelyingParty;
69 import edu.internet2.middleware.shibboleth.common.ShibBrowserProfile;
70 import edu.internet2.middleware.shibboleth.common.ShibbolethConfigurationException;
71 import edu.internet2.middleware.shibboleth.idp.IdPProtocolHandler;
72 import edu.internet2.middleware.shibboleth.idp.IdPProtocolSupport;
73 import edu.internet2.middleware.shibboleth.idp.InvalidClientDataException;
74 import edu.internet2.middleware.shibboleth.metadata.Endpoint;
75 import edu.internet2.middleware.shibboleth.metadata.EntityDescriptor;
76 import edu.internet2.middleware.shibboleth.metadata.SPSSODescriptor;
77
78 /**
79  * <code>ProtocolHandler</code> implementation that responds to SSO flows as specified in "Shibboleth Architecture:
80  * Protocols and Profiles". Includes a compatibility mode for dealing with Shibboleth v1.1 SPs.
81  * 
82  * @author Walter Hoehn
83  */
84 public class ShibbolethV1SSOHandler extends BaseHandler implements IdPProtocolHandler {
85
86         private static Logger log = Logger.getLogger(ShibbolethV1SSOHandler.class.getName());
87
88         /**
89          * Required DOM-based constructor.
90          */
91         public ShibbolethV1SSOHandler(Element config) throws ShibbolethConfigurationException {
92
93                 super(config);
94         }
95
96         /*
97          * @see edu.internet2.middleware.shibboleth.idp.IdPResponder.ProtocolHandler#processRequest(javax.servlet.http.HttpServletRequest,
98          *      javax.servlet.http.HttpServletResponse)
99          */
100         public SAMLResponse processRequest(HttpServletRequest request, HttpServletResponse response,
101                         SAMLRequest samlRequest, IdPProtocolSupport support) throws SAMLException, ServletException, IOException {
102
103                 if (request == null) {
104                         log.error("Protocol Handler received a SAML Request, but is unable to handle it.");
105                         throw new SAMLException(SAMLException.RESPONDER, "General error processing request.");
106                 }
107
108                 // Set attributes that are needed by the jsp
109                 request.setAttribute("shire", request.getParameter("shire"));
110                 request.setAttribute("target", request.getParameter("target"));
111
112                 try {
113                         // Ensure that we have the required data from the servlet container
114                         IdPProtocolSupport.validateEngineData(request);
115                         validateShibSpecificData(request);
116
117                         // Get the authN info
118                         String username = support.getIdPConfig().getAuthHeaderName().equalsIgnoreCase("REMOTE_USER") ? request
119                                         .getRemoteUser() : request.getHeader(support.getIdPConfig().getAuthHeaderName());
120                         if ((username == null) || (username.equals(""))) { throw new InvalidClientDataException(
121                                         "Unable to authenticate remote user"); }
122                         AuthNPrincipal principal = new AuthNPrincipal(username);
123
124                         // Select the appropriate Relying Party configuration for the request
125                         RelyingParty relyingParty = null;
126                         String remoteProviderId = request.getParameter("providerId");
127                         // If the target did not send a Provider Id, then assume it is a Shib
128                         // 1.1 or older target
129                         if (remoteProviderId == null) {
130                                 relyingParty = support.getServiceProviderMapper().getLegacyRelyingParty();
131                         } else if (remoteProviderId.equals("")) {
132                                 throw new InvalidClientDataException("Invalid service provider id.");
133                         } else {
134                                 log.debug("Remote provider has identified itself as: (" + remoteProviderId + ").");
135                                 relyingParty = support.getServiceProviderMapper().getRelyingParty(remoteProviderId);
136                         }
137
138                         // Grab the metadata for the provider
139                         EntityDescriptor provider = support.lookup(relyingParty.getProviderId());
140
141                         // Make sure that the selected relying party configuration is appropriate for this
142                         // acceptance URL
143                         String acceptanceURL = request.getParameter("shire");
144                         if (!relyingParty.isLegacyProvider()) {
145
146                                 if (provider == null) {
147                                         log.info("No metadata found for provider: (" + relyingParty.getProviderId() + ").");
148                                         relyingParty = support.getServiceProviderMapper().getRelyingParty(null);
149
150                                 } else {
151                                         if (isValidAssertionConsumerURL(provider, acceptanceURL)) {
152                                                 log.info("Supplied consumer URL validated for this provider.");
153                                         } else {
154                                                 log.error("Assertion consumer service URL (" + acceptanceURL + ") is NOT valid for provider ("
155                                                                 + relyingParty.getProviderId() + ").");
156                                                 throw new InvalidClientDataException("Invalid assertion consumer service URL.");
157                                         }
158                                 }
159                         }
160
161                         // Create SAML Name Identifier & Subject
162                         SAMLNameIdentifier nameId;
163                         try {
164                                 nameId = support.getNameMapper().getNameIdentifierName(relyingParty.getHSNameFormatId(), principal,
165                                                 relyingParty, relyingParty.getIdentityProvider());
166                         } catch (NameIdentifierMappingException e) {
167                                 log.error("Error converting principal to SAML Name Identifier: " + e);
168                                 throw new SAMLException("Error converting principal to SAML Name Identifier.", e);
169                         }
170
171                         String authenticationMethod = request.getHeader("SAMLAuthenticationMethod");
172                         if (authenticationMethod == null || authenticationMethod.equals("")) {
173                                 authenticationMethod = relyingParty.getDefaultAuthMethod().toString();
174                                 log.debug("User was authenticated via the default method for this relying party ("
175                                                 + authenticationMethod + ").");
176                         } else {
177                                 log.debug("User was authenticated via the method (" + authenticationMethod + ").");
178                         }
179
180                         // TODO Provide a mechanism for the authenticator to specify the auth time
181
182                         SAMLSubject authNSubject = new SAMLSubject(nameId, null, null, null);
183
184                         ArrayList assertions = new ArrayList();
185
186                         // Is this artifact or POST?
187                         boolean artifactProfile = useArtifactProfile(provider, acceptanceURL);
188
189                         // TODO make sure we support adding signatures to attribute assertion
190
191                         // Package attributes for push, if necessary - don't attempt this for legacy providers (they don't support
192                         // it)
193                         if (!relyingParty.isLegacyProvider() && pushAttributes(artifactProfile, relyingParty)) {
194                                 log.info("Resolving attributes for push.");
195                                 SAMLAssertion attrAssertion = generateAttributeAssertion(support, principal, relyingParty, authNSubject);
196                                 if (attrAssertion != null) {
197                                         assertions.add(attrAssertion);
198                                 } else {
199                                         log.info("No attributes resolved.");
200                                 }
201                         }
202
203                         // TODO do assertion signing for artifact stuff
204
205                         // SAML Artifact profile - don't even attempt this for legacy providers (they don't support it)
206                         if (!relyingParty.isLegacyProvider() && artifactProfile) {
207                                 respondWithArtifact(request, response, support, principal, relyingParty, provider, acceptanceURL,
208                                                 nameId, authenticationMethod, authNSubject, assertions);
209
210                                 // SAML POST profile
211                         } else {
212                                 respondWithPOST(request, response, support, principal, relyingParty, provider, acceptanceURL, nameId,
213                                                 authenticationMethod, authNSubject, assertions);
214                         }
215                 } catch (InvalidClientDataException e) {
216                         throw new SAMLException(SAMLException.RESPONDER, e.getMessage());
217                 }
218                 return null;
219         }
220
221         private void respondWithArtifact(HttpServletRequest request, HttpServletResponse response,
222                         IdPProtocolSupport support, AuthNPrincipal principal, RelyingParty relyingParty, EntityDescriptor provider,
223                         String acceptanceURL, SAMLNameIdentifier nameId, String authenticationMethod, SAMLSubject authNSubject,
224                         ArrayList assertions) throws SAMLException, IOException, UnsupportedEncodingException {
225
226                 log.debug("Responding with Artifact profile.");
227
228                 authNSubject.addConfirmationMethod(SAMLSubject.CONF_ARTIFACT);
229                 assertions.add(generateAuthNAssertion(request, relyingParty, provider, nameId, authenticationMethod, new Date(
230                                 System.currentTimeMillis()), authNSubject));
231
232                 // Create artifacts for each assertion
233                 ArrayList artifacts = new ArrayList();
234                 for (int i = 0; i < assertions.size(); i++) {
235                         artifacts
236                                         .add(support.getArtifactMapper().generateArtifact((SAMLAssertion) assertions.get(i), relyingParty));
237                 }
238
239                 // Assemble the query string
240                 StringBuffer destination = new StringBuffer(acceptanceURL);
241                 destination.append("?TARGET=");
242                 destination.append(URLEncoder.encode(request.getParameter("target"), "UTF-8"));
243                 Iterator iterator = artifacts.iterator();
244                 StringBuffer artifactBuffer = new StringBuffer(); // Buffer for the transaction log
245
246                 // Construct the artifact query parameter
247                 while (iterator.hasNext()) {
248                         Artifact artifact = (Artifact) iterator.next();
249                         artifactBuffer.append("(" + artifact + ")");
250                         destination.append("&SAMLart=");
251                         destination.append(URLEncoder.encode(artifact.encode(), "UTF-8"));
252                 }
253
254                 log.debug("Redirecting to (" + destination.toString() + ").");
255                 response.sendRedirect(destination.toString()); // Redirect to the artifact receiver
256                 support.getTransactionLog().info(
257                                 "Assertion artifact(s) (" + artifactBuffer.toString() + ") issued to provider ("
258                                                 + relyingParty.getIdentityProvider().getProviderId() + ") on behalf of principal ("
259                                                 + principal.getName() + "). Name Identifier: (" + nameId.getName()
260                                                 + "). Name Identifier Format: (" + nameId.getFormat() + ").");
261         }
262
263         private void respondWithPOST(HttpServletRequest request, HttpServletResponse response, IdPProtocolSupport support,
264                         AuthNPrincipal principal, RelyingParty relyingParty, EntityDescriptor provider, String acceptanceURL,
265                         SAMLNameIdentifier nameId, String authenticationMethod, SAMLSubject authNSubject, ArrayList assertions)
266                         throws SAMLException, IOException, ServletException {
267
268                 log.debug("Responding with POST profile.");
269
270                 authNSubject.addConfirmationMethod(SAMLSubject.CONF_BEARER);
271                 assertions.add(generateAuthNAssertion(request, relyingParty, provider, nameId, authenticationMethod, new Date(
272                                 System.currentTimeMillis()), authNSubject));
273
274                 // Set attributes needed by form
275                 request.setAttribute("acceptanceURL", acceptanceURL);
276                 request.setAttribute("target", request.getParameter("target"));
277
278                 SAMLResponse samlResponse = new SAMLResponse(null, acceptanceURL, assertions, null);
279
280                 IdPProtocolSupport.addSignatures(samlResponse, relyingParty, provider, true);
281
282                 createPOSTForm(request, response, samlResponse.toBase64());
283
284                 // Make transaction log entry
285                 if (relyingParty.isLegacyProvider()) {
286                         support.getTransactionLog().info(
287                                         "Authentication assertion issued to legacy provider (SHIRE: " + request.getParameter("shire")
288                                                         + ") on behalf of principal (" + principal.getName() + ") for resource ("
289                                                         + request.getParameter("target") + "). Name Identifier: (" + nameId.getName()
290                                                         + "). Name Identifier Format: (" + nameId.getFormat() + ").");
291
292                 } else {
293                         support.getTransactionLog().info(
294                                         "Authentication assertion issued to provider ("
295                                                         + relyingParty.getIdentityProvider().getProviderId() + ") on behalf of principal ("
296                                                         + principal.getName() + "). Name Identifier: (" + nameId.getName()
297                                                         + "). Name Identifier Format: (" + nameId.getFormat() + ").");
298                 }
299         }
300
301         private SAMLAssertion generateAttributeAssertion(IdPProtocolSupport support, AuthNPrincipal principal,
302                         RelyingParty relyingParty, SAMLSubject authNSubject) throws SAMLException {
303
304                 try {
305                         SAMLAttribute[] attributes = support.getReleaseAttributes(principal, relyingParty.getProviderId(), null);
306                         log.info("Found " + attributes.length + " attribute(s) for " + principal.getName());
307
308                         // Bail if we didn't get any attributes
309                         if (attributes == null || attributes.length < 1) {
310                                 return null;
311
312                         } else {
313                                 // Reference requested subject
314                                 SAMLSubject attrSubject = (SAMLSubject) authNSubject.clone();
315
316                                 ArrayList audiences = new ArrayList();
317                                 if (relyingParty.getProviderId() != null) {
318                                         audiences.add(relyingParty.getProviderId());
319                                 }
320                                 if (relyingParty.getName() != null && !relyingParty.getName().equals(relyingParty.getProviderId())) {
321                                         audiences.add(relyingParty.getName());
322                                 }
323                                 SAMLCondition condition = new SAMLAudienceRestrictionCondition(audiences);
324
325                                 // Put all attributes into an assertion
326                                 SAMLStatement statement = new SAMLAttributeStatement(attrSubject, Arrays.asList(attributes));
327
328                                 // Set assertion expiration to longest attribute expiration
329                                 long max = 0;
330                                 for (int i = 0; i < attributes.length; i++) {
331                                         if (max < attributes[i].getLifetime()) {
332                                                 max = attributes[i].getLifetime();
333                                         }
334                                 }
335                                 Date now = new Date();
336                                 Date then = new Date(now.getTime() + (max * 1000)); // max is in seconds
337
338                                 SAMLAssertion attrAssertion = new SAMLAssertion(relyingParty.getIdentityProvider().getProviderId(),
339                                                 now, then, Collections.singleton(condition), null, Collections.singleton(statement));
340
341                                 if (log.isDebugEnabled()) {
342                                         try {
343                                                 log.debug("Dumping generated Attribute Assertion:"
344                                                                 + System.getProperty("line.separator")
345                                                                 + new String(new BASE64Decoder().decodeBuffer(new String(attrAssertion.toBase64(),
346                                                                                 "ASCII")), "UTF8"));
347
348                                         } catch (Exception e) {
349                                                 log.error("Unable to dump assertion to debug log: " + e);
350                                         }
351                                 }
352                                 return attrAssertion;
353                         }
354                 } catch (AAException e) {
355                         log.error("An error was encountered while generating assertion for attribute push: " + e);
356                         throw new SAMLException(SAMLException.RESPONDER, "General error processing request.");
357                 } catch (CloneNotSupportedException e) {
358                         log.error("An error was encountered while generating assertion for attribute push: " + e);
359                         throw new SAMLException(SAMLException.RESPONDER, "General error processing request.");
360                 }
361         }
362
363         private SAMLAssertion generateAuthNAssertion(HttpServletRequest request, RelyingParty relyingParty,
364                         EntityDescriptor provider, SAMLNameIdentifier nameId, String authenticationMethod, Date authTime,
365                         SAMLSubject subject) throws SAMLException, IOException {
366
367                 Document doc = org.opensaml.XML.parserPool.newDocument();
368
369                 // Determine the correct audiences
370                 ArrayList audiences = new ArrayList();
371                 if (relyingParty.getProviderId() != null) {
372                         audiences.add(relyingParty.getProviderId());
373                 }
374                 if (relyingParty.getName() != null && !relyingParty.getName().equals(relyingParty.getProviderId())) {
375                         audiences.add(relyingParty.getName());
376                 }
377
378                 // Determine the correct issuer
379                 String issuer = null;
380                 if (relyingParty.isLegacyProvider()) {
381
382                         log.debug("Service Provider is running Shibboleth <= 1.1. Using old style issuer.");
383                         if (relyingParty.getIdentityProvider().getSigningCredential() == null
384                                         || relyingParty.getIdentityProvider().getSigningCredential().getX509Certificate() == null) { throw new SAMLException(
385                                         "Cannot serve legacy style assertions without an X509 certificate"); }
386                         issuer = ShibBrowserProfile.getHostNameFromDN(relyingParty.getIdentityProvider().getSigningCredential()
387                                         .getX509Certificate().getSubjectX500Principal());
388                         if (issuer == null || issuer.equals("")) { throw new SAMLException(
389                                         "Error parsing certificate DN while determining legacy issuer name."); }
390
391                 } else {
392                         issuer = relyingParty.getIdentityProvider().getProviderId();
393                 }
394
395                 // For compatibility with pre-1.2 shibboleth targets, include a pointer to the AA
396                 ArrayList bindings = new ArrayList();
397                 if (relyingParty.isLegacyProvider()) {
398
399                         SAMLAuthorityBinding binding = new SAMLAuthorityBinding(SAMLBinding.SOAP, relyingParty.getAAUrl()
400                                         .toString(), new QName(org.opensaml.XML.SAMLP_NS, "AttributeQuery"));
401                         bindings.add(binding);
402                 }
403
404                 // Create the assertion
405                 Vector conditions = new Vector(1);
406                 if (audiences != null && audiences.size() > 0) conditions.add(new SAMLAudienceRestrictionCondition(audiences));
407
408                 SAMLStatement[] statements = {new SAMLAuthenticationStatement(subject, authenticationMethod, authTime, request
409                                 .getRemoteAddr(), null, bindings)};
410
411                 SAMLAssertion assertion = new SAMLAssertion(issuer, new Date(System.currentTimeMillis()), new Date(System
412                                 .currentTimeMillis() + 300000), conditions, null, Arrays.asList(statements));
413
414                 if (log.isDebugEnabled()) {
415                         log.debug("Dumping generated AuthN Assertion:" + System.getProperty("line.separator")
416                                         + new String(new BASE64Decoder().decodeBuffer(new String(assertion.toBase64(), "ASCII")), "UTF8"));
417                 }
418
419                 return assertion;
420         }
421
422         /*
423          * @see edu.internet2.middleware.shibboleth.idp.IdPResponder.ProtocolHandler#getHandlerName()
424          */
425         public String getHandlerName() {
426
427                 return "Shibboleth v1.x SSO";
428         }
429
430         private void validateShibSpecificData(HttpServletRequest request) throws InvalidClientDataException {
431
432                 if (request.getParameter("target") == null || request.getParameter("target").equals("")) { throw new InvalidClientDataException(
433                                 "Invalid data from Service Provider: no target URL received."); }
434                 if ((request.getParameter("shire") == null) || (request.getParameter("shire").equals(""))) { throw new InvalidClientDataException(
435                                 "Invalid data from Service Provider: No acceptance URL received."); }
436         }
437
438         private static void createPOSTForm(HttpServletRequest req, HttpServletResponse res, byte[] buf) throws IOException,
439                         ServletException {
440
441                 // Hardcoded to ASCII to ensure Base64 encoding compatibility
442                 req.setAttribute("assertion", new String(buf, "ASCII"));
443
444                 if (log.isDebugEnabled()) {
445                         try {
446                                 log.debug("Dumping generated SAML Response:" + System.getProperty("line.separator")
447                                                 + new String(new BASE64Decoder().decodeBuffer(new String(buf, "ASCII")), "UTF8"));
448                         } catch (IOException e) {
449                                 log.error("Encountered an error while decoding SAMLReponse for logging purposes.");
450                         }
451                 }
452
453                 RequestDispatcher rd = req.getRequestDispatcher("/IdP.jsp");
454                 rd.forward(req, res);
455         }
456
457         /**
458          * Boolean indication of which browser profile is in effect. "true" indicates Artifact and "false" indicates POST.
459          */
460         private static boolean useArtifactProfile(EntityDescriptor provider, String acceptanceURL) {
461
462                 // TODO this logic needs to be updated
463
464                 // Default to POST if we have no metadata
465                 if (provider == null) { return false; }
466
467                 // Default to POST if we have incomplete metadata
468                 SPSSODescriptor sp = provider.getSPSSODescriptor(org.opensaml.XML.SAML11_PROTOCOL_ENUM);
469                 if (sp == null) { return false; }
470
471                 // Look at the bindings.. prefer POST if we have multiple
472                 Iterator endpoints = sp.getAssertionConsumerServiceManager().getEndpoints();
473                 while (endpoints.hasNext()) {
474                         Endpoint ep = (Endpoint) endpoints.next();
475                         if (acceptanceURL.equals(ep.getLocation()) && SAMLBrowserProfile.PROFILE_POST_URI.equals(ep.getBinding())) { return false; }
476                         if (acceptanceURL.equals(ep.getLocation())
477                                         && SAMLBrowserProfile.PROFILE_ARTIFACT_URI.equals(ep.getBinding())) { return true; }
478                 }
479
480                 // Default to POST if we have incomplete metadata
481                 return false;
482         }
483
484         /**
485          * Boolean indication of whether an assertion containing an attribute statement should be bundled in the response
486          * with the assertion containing the AuthN statement.
487          */
488         private static boolean pushAttributes(boolean artifactProfile, RelyingParty relyingParty) {
489
490                 // By default push for Artifact and don't push for POST
491                 // This can be overriden at the level of the relying party
492                 if (relyingParty.forceAttributePush()) {
493                         return true;
494                 } else if (relyingParty.forceAttributeNoPush()) {
495                         return false;
496                 } else if (artifactProfile) {
497                         return true;
498                 } else {
499                         return false;
500                 }
501         }
502
503         /**
504          * Boolean indication of whethere or not a given assertion consumer URL is valid for a given SP.
505          */
506         private static boolean isValidAssertionConsumerURL(EntityDescriptor provider, String shireURL)
507                         throws InvalidClientDataException {
508
509                 SPSSODescriptor sp = provider.getSPSSODescriptor(org.opensaml.XML.SAML11_PROTOCOL_ENUM);
510                 if (sp == null) {
511                         log.info("Inappropriate metadata for provider.");
512                         return false;
513                 }
514
515                 Iterator endpoints = sp.getAssertionConsumerServiceManager().getEndpoints();
516                 while (endpoints.hasNext()) {
517                         if (shireURL.equals(((Endpoint) endpoints.next()).getLocation())) { return true; }
518                 }
519                 log.info("Supplied consumer URL not found in metadata.");
520                 return false;
521         }
522 }