Much more robust attribute push support.
[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  * @author Walter Hoehn
80  */
81 public class ShibbolethV1SSOHandler extends BaseHandler implements IdPProtocolHandler {
82
83         private static Logger log = Logger.getLogger(ShibbolethV1SSOHandler.class.getName());
84
85         /**
86          * Required DOM-based constructor.
87          */
88         public ShibbolethV1SSOHandler(Element config) throws ShibbolethConfigurationException {
89
90                 super(config);
91         }
92
93         /*
94          * @see edu.internet2.middleware.shibboleth.idp.IdPResponder.ProtocolHandler#processRequest(javax.servlet.http.HttpServletRequest,
95          *      javax.servlet.http.HttpServletResponse)
96          */
97         public SAMLResponse processRequest(HttpServletRequest request, HttpServletResponse response,
98                         SAMLRequest samlRequest, IdPProtocolSupport support) throws SAMLException, ServletException, IOException {
99
100                 if (request == null) {
101                         log.error("Protocol Handler received a SAML Request, but is unable to handle it.");
102                         throw new SAMLException(SAMLException.RESPONDER, "General error processing request.");
103                 }
104
105                 // Set attributes that are needed by the jsp
106                 request.setAttribute("shire", request.getParameter("shire"));
107                 request.setAttribute("target", request.getParameter("target"));
108
109                 try {
110                         // Ensure that we have the required data from the servlet container
111                         IdPProtocolSupport.validateEngineData(request);
112                         validateShibSpecificData(request);
113
114                         // Get the authN info
115                         String username = support.getIdPConfig().getAuthHeaderName().equalsIgnoreCase("REMOTE_USER") ? request
116                                         .getRemoteUser() : request.getHeader(support.getIdPConfig().getAuthHeaderName());
117                         if ((username == null) || (username.equals(""))) { throw new InvalidClientDataException(
118                                         "Unable to authenticate remote user"); }
119                         AuthNPrincipal principal = new AuthNPrincipal(username);
120
121                         // Select the appropriate Relying Party configuration for the request
122                         RelyingParty relyingParty = null;
123                         String remoteProviderId = request.getParameter("providerId");
124                         // If the target did not send a Provider Id, then assume it is a Shib
125                         // 1.1 or older target
126                         if (remoteProviderId == null) {
127                                 relyingParty = support.getServiceProviderMapper().getLegacyRelyingParty();
128                         } else if (remoteProviderId.equals("")) {
129                                 throw new InvalidClientDataException("Invalid service provider id.");
130                         } else {
131                                 log.debug("Remote provider has identified itself as: (" + remoteProviderId + ").");
132                                 relyingParty = support.getServiceProviderMapper().getRelyingParty(remoteProviderId);
133                         }
134
135                         // Grab the metadata for the provider
136                         EntityDescriptor provider = support.lookup(relyingParty.getProviderId());
137
138                         // Make sure that the selected relying party configuration is appropriate for this
139                         // acceptance URL
140                         String acceptanceURL = request.getParameter("shire");
141                         if (!relyingParty.isLegacyProvider()) {
142
143                                 if (provider == null) {
144                                         log.info("No metadata found for provider: (" + relyingParty.getProviderId() + ").");
145                                         relyingParty = support.getServiceProviderMapper().getRelyingParty(null);
146
147                                 } else {
148                                         if (isValidAssertionConsumerURL(provider, acceptanceURL)) {
149                                                 log.info("Supplied consumer URL validated for this provider.");
150                                         } else {
151                                                 log.error("Assertion consumer service URL (" + acceptanceURL + ") is NOT valid for provider ("
152                                                                 + relyingParty.getProviderId() + ").");
153                                                 throw new InvalidClientDataException("Invalid assertion consumer service URL.");
154                                         }
155                                 }
156                         }
157
158                         // Create SAML Name Identifier & Subject
159                         SAMLNameIdentifier nameId;
160                         try {
161                                 nameId = support.getNameMapper().getNameIdentifierName(relyingParty.getHSNameFormatId(), principal,
162                                                 relyingParty, relyingParty.getIdentityProvider());
163                         } catch (NameIdentifierMappingException e) {
164                                 log.error("Error converting principal to SAML Name Identifier: " + e);
165                                 throw new SAMLException("Error converting principal to SAML Name Identifier.", e);
166                         }
167
168                         String authenticationMethod = request.getHeader("SAMLAuthenticationMethod");
169                         if (authenticationMethod == null || authenticationMethod.equals("")) {
170                                 authenticationMethod = relyingParty.getDefaultAuthMethod().toString();
171                                 log.debug("User was authenticated via the default method for this relying party ("
172                                                 + authenticationMethod + ").");
173                         } else {
174                                 log.debug("User was authenticated via the method (" + authenticationMethod + ").");
175                         }
176
177                         // TODO Provide a mechanism for the authenticator to specify the auth time
178
179                         SAMLSubject authNSubject = new SAMLSubject(nameId, null, null, null);
180
181                         ArrayList assertions = new ArrayList();
182
183                         // Is this artifact or POST?
184                         boolean artifactProfile = useArtifactProfile(provider, acceptanceURL);
185
186                         // TODO make sure we support adding signatures to attribute assertion
187
188                         // Package attributes for push, if necessary - don't attempt this for legacy providers (they don't support
189                         // it)
190                         if (!relyingParty.isLegacyProvider() && pushAttributes(artifactProfile)) {
191                                 log.info("Resolving attributes for push.");
192                                 SAMLAssertion attrAssertion = generateAttributeAssertion(support, principal, relyingParty, authNSubject);
193                                 if (attrAssertion != null) {
194                                         assertions.add(attrAssertion);
195                                 } else {
196                                         log.info("No attributes resolved.");
197                                 }
198                         }
199
200                         // TODO do assertion signing for artifact stuff
201
202                         // SAML Artifact profile - don't even attempt this for legacy providers (they don't support it)
203                         if (!relyingParty.isLegacyProvider() && artifactProfile) {
204                                 respondWithArtifact(request, response, support, principal, relyingParty, provider, acceptanceURL,
205                                                 nameId, authenticationMethod, authNSubject, assertions);
206
207                                 // SAML POST profile
208                         } else {
209                                 respondWithPOST(request, response, support, principal, relyingParty, provider, acceptanceURL, nameId,
210                                                 authenticationMethod, authNSubject, assertions);
211                         }
212                 } catch (InvalidClientDataException e) {
213                         throw new SAMLException(SAMLException.RESPONDER, e.getMessage());
214                 }
215                 return null;
216         }
217
218         private void respondWithArtifact(HttpServletRequest request, HttpServletResponse response,
219                         IdPProtocolSupport support, AuthNPrincipal principal, RelyingParty relyingParty, EntityDescriptor provider,
220                         String acceptanceURL, SAMLNameIdentifier nameId, String authenticationMethod, SAMLSubject authNSubject,
221                         ArrayList assertions) throws SAMLException, IOException, UnsupportedEncodingException {
222
223                 log.debug("Responding with Artifact profile.");
224
225                 authNSubject.addConfirmationMethod(SAMLSubject.CONF_ARTIFACT);
226                 assertions.add(generateAuthNAssertion(request, relyingParty, provider, nameId, authenticationMethod, new Date(
227                                 System.currentTimeMillis()), authNSubject));
228
229                 // Create artifacts for each assertion
230                 ArrayList artifacts = new ArrayList();
231                 for (int i = 0; i < assertions.size(); i++) {
232                         artifacts
233                                         .add(support.getArtifactMapper().generateArtifact((SAMLAssertion) assertions.get(i), relyingParty));
234                 }
235
236                 // Assemble the query string
237                 StringBuffer destination = new StringBuffer(acceptanceURL);
238                 destination.append("?TARGET=");
239                 destination.append(URLEncoder.encode(request.getParameter("target"), "UTF-8"));
240                 Iterator iterator = artifacts.iterator();
241                 StringBuffer artifactBuffer = new StringBuffer(); // Buffer for the transaction log
242
243                 // Construct the artifact query parameter
244                 while (iterator.hasNext()) {
245                         Artifact artifact = (Artifact) iterator.next();
246                         artifactBuffer.append("(" + artifact + ")");
247                         destination.append("&SAMLart=");
248                         destination.append(URLEncoder.encode(artifact.encode(), "UTF-8"));
249                 }
250
251                 log.debug("Redirecting to (" + destination.toString() + ").");
252                 response.sendRedirect(destination.toString()); // Redirect to the artifact receiver
253                 support.getTransactionLog().info(
254                                 "Assertion artifact(s) (" + artifactBuffer.toString() + ") issued to provider ("
255                                                 + relyingParty.getIdentityProvider().getProviderId() + ") on behalf of principal ("
256                                                 + principal.getName() + "). Name Identifier: (" + nameId.getName()
257                                                 + "). Name Identifier Format: (" + nameId.getFormat() + ").");
258         }
259
260         private void respondWithPOST(HttpServletRequest request, HttpServletResponse response, IdPProtocolSupport support,
261                         AuthNPrincipal principal, RelyingParty relyingParty, EntityDescriptor provider, String acceptanceURL,
262                         SAMLNameIdentifier nameId, String authenticationMethod, SAMLSubject authNSubject, ArrayList assertions)
263                         throws SAMLException, IOException, ServletException {
264
265                 log.debug("Responding with POST profile.");
266
267                 authNSubject.addConfirmationMethod(SAMLSubject.CONF_BEARER);
268                 assertions.add(generateAuthNAssertion(request, relyingParty, provider, nameId, authenticationMethod, new Date(
269                                 System.currentTimeMillis()), authNSubject));
270
271                 // Set attributes needed by form
272                 request.setAttribute("acceptanceURL", acceptanceURL);
273                 request.setAttribute("target", request.getParameter("target"));
274
275                 SAMLResponse samlResponse = new SAMLResponse(null, acceptanceURL, assertions, null);
276
277                 IdPProtocolSupport.addSignatures(samlResponse, relyingParty, provider, true);
278
279                 createPOSTForm(request, response, samlResponse.toBase64());
280
281                 // Make transaction log entry
282                 if (relyingParty.isLegacyProvider()) {
283                         support.getTransactionLog().info(
284                                         "Authentication assertion issued to legacy provider (SHIRE: " + request.getParameter("shire")
285                                                         + ") on behalf of principal (" + principal.getName() + ") for resource ("
286                                                         + request.getParameter("target") + "). Name Identifier: (" + nameId.getName()
287                                                         + "). Name Identifier Format: (" + nameId.getFormat() + ").");
288
289                 } else {
290                         support.getTransactionLog().info(
291                                         "Authentication assertion issued to provider ("
292                                                         + relyingParty.getIdentityProvider().getProviderId() + ") on behalf of principal ("
293                                                         + principal.getName() + "). Name Identifier: (" + nameId.getName()
294                                                         + "). Name Identifier Format: (" + nameId.getFormat() + ").");
295                 }
296         }
297
298         private SAMLAssertion generateAttributeAssertion(IdPProtocolSupport support, AuthNPrincipal principal,
299                         RelyingParty relyingParty, SAMLSubject authNSubject) throws SAMLException {
300
301                 try {
302                         SAMLAttribute[] attributes = support.getReleaseAttributes(principal, relyingParty.getProviderId(), null);
303                         log.info("Found " + attributes.length + " attribute(s) for " + principal.getName());
304
305                         // Bail if we didn't get any attributes
306                         if (attributes == null || attributes.length < 1) {
307                                 return null;
308
309                         } else {
310                                 // Reference requested subject
311                                 SAMLSubject attrSubject = (SAMLSubject) authNSubject.clone();
312
313                                 ArrayList audiences = new ArrayList();
314                                 if (relyingParty.getProviderId() != null) {
315                                         audiences.add(relyingParty.getProviderId());
316                                 }
317                                 if (relyingParty.getName() != null && !relyingParty.getName().equals(relyingParty.getProviderId())) {
318                                         audiences.add(relyingParty.getName());
319                                 }
320                                 SAMLCondition condition = new SAMLAudienceRestrictionCondition(audiences);
321
322                                 // Put all attributes into an assertion
323                                 SAMLStatement statement = new SAMLAttributeStatement(attrSubject, Arrays.asList(attributes));
324
325                                 // Set assertion expiration to longest attribute expiration
326                                 long max = 0;
327                                 for (int i = 0; i < attributes.length; i++) {
328                                         if (max < attributes[i].getLifetime()) {
329                                                 max = attributes[i].getLifetime();
330                                         }
331                                 }
332                                 Date now = new Date();
333                                 Date then = new Date(now.getTime() + (max * 1000)); // max is in seconds
334
335                                 SAMLAssertion attrAssertion = new SAMLAssertion(relyingParty.getIdentityProvider().getProviderId(),
336                                                 now, then, Collections.singleton(condition), null, Collections.singleton(statement));
337
338                                 if (log.isDebugEnabled()) {
339                                         try {
340                                                 log.debug("Dumping generated Attribute Assertion:"
341                                                                 + System.getProperty("line.separator")
342                                                                 + new String(new BASE64Decoder().decodeBuffer(new String(attrAssertion.toBase64(),
343                                                                                 "ASCII")), "UTF8"));
344
345                                         } catch (Exception e) {
346                                                 log.error("Unable to dump assertion to debug log: " + e);
347                                         }
348                                 }
349                                 return attrAssertion;
350                         }
351                 } catch (AAException e) {
352                         log.error("An error was encountered while generating assertion for attribute push: " + e);
353                         throw new SAMLException(SAMLException.RESPONDER, "General error processing request.");
354                 } catch (CloneNotSupportedException 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                 }
358         }
359
360         private SAMLAssertion generateAuthNAssertion(HttpServletRequest request, RelyingParty relyingParty,
361                         EntityDescriptor provider, SAMLNameIdentifier nameId, String authenticationMethod, Date authTime,
362                         SAMLSubject subject) throws SAMLException, IOException {
363
364                 Document doc = org.opensaml.XML.parserPool.newDocument();
365
366                 // Determine the correct audiences
367                 ArrayList audiences = new ArrayList();
368                 if (relyingParty.getProviderId() != null) {
369                         audiences.add(relyingParty.getProviderId());
370                 }
371                 if (relyingParty.getName() != null && !relyingParty.getName().equals(relyingParty.getProviderId())) {
372                         audiences.add(relyingParty.getName());
373                 }
374
375                 // Determine the correct issuer
376                 String issuer = null;
377                 if (relyingParty.isLegacyProvider()) {
378
379                         log.debug("Service Provider is running Shibboleth <= 1.1. Using old style issuer.");
380                         if (relyingParty.getIdentityProvider().getSigningCredential() == null
381                                         || relyingParty.getIdentityProvider().getSigningCredential().getX509Certificate() == null) { throw new SAMLException(
382                                         "Cannot serve legacy style assertions without an X509 certificate"); }
383                         issuer = ShibBrowserProfile.getHostNameFromDN(relyingParty.getIdentityProvider().getSigningCredential()
384                                         .getX509Certificate().getSubjectX500Principal());
385                         if (issuer == null || issuer.equals("")) { throw new SAMLException(
386                                         "Error parsing certificate DN while determining legacy issuer name."); }
387
388                 } else {
389                         issuer = relyingParty.getIdentityProvider().getProviderId();
390                 }
391
392                 // For compatibility with pre-1.2 shibboleth targets, include a pointer to the AA
393                 ArrayList bindings = new ArrayList();
394                 if (relyingParty.isLegacyProvider()) {
395
396                         SAMLAuthorityBinding binding = new SAMLAuthorityBinding(SAMLBinding.SOAP, relyingParty.getAAUrl()
397                                         .toString(), new QName(org.opensaml.XML.SAMLP_NS, "AttributeQuery"));
398                         bindings.add(binding);
399                 }
400
401                 // Create the assertion
402                 Vector conditions = new Vector(1);
403                 if (audiences != null && audiences.size() > 0) conditions.add(new SAMLAudienceRestrictionCondition(audiences));
404
405                 SAMLStatement[] statements = {new SAMLAuthenticationStatement(subject, authenticationMethod, authTime, request
406                                 .getRemoteAddr(), null, bindings)};
407
408                 SAMLAssertion assertion = new SAMLAssertion(issuer, new Date(System.currentTimeMillis()), new Date(System
409                                 .currentTimeMillis() + 300000), conditions, null, Arrays.asList(statements));
410
411                 if (log.isDebugEnabled()) {
412                         log.debug("Dumping generated AuthN Assertion:" + System.getProperty("line.separator")
413                                         + new String(new BASE64Decoder().decodeBuffer(new String(assertion.toBase64(), "ASCII")), "UTF8"));
414                 }
415
416                 return assertion;
417         }
418
419         /*
420          * @see edu.internet2.middleware.shibboleth.idp.IdPResponder.ProtocolHandler#getHandlerName()
421          */
422         public String getHandlerName() {
423
424                 return "Shibboleth v1.x SSO";
425         }
426
427         private void validateShibSpecificData(HttpServletRequest request) throws InvalidClientDataException {
428
429                 if (request.getParameter("target") == null || request.getParameter("target").equals("")) { throw new InvalidClientDataException(
430                                 "Invalid data from Service Provider: no target URL received."); }
431                 if ((request.getParameter("shire") == null) || (request.getParameter("shire").equals(""))) { throw new InvalidClientDataException(
432                                 "Invalid data from Service Provider: No acceptance URL received."); }
433         }
434
435         private static void createPOSTForm(HttpServletRequest req, HttpServletResponse res, byte[] buf) throws IOException,
436                         ServletException {
437
438                 // Hardcoded to ASCII to ensure Base64 encoding compatibility
439                 req.setAttribute("assertion", new String(buf, "ASCII"));
440
441                 if (log.isDebugEnabled()) {
442                         try {
443                                 log.debug("Dumping generated SAML Response:" + System.getProperty("line.separator")
444                                                 + new String(new BASE64Decoder().decodeBuffer(new String(buf, "ASCII")), "UTF8"));
445                         } catch (IOException e) {
446                                 log.error("Encountered an error while decoding SAMLReponse for logging purposes.");
447                         }
448                 }
449
450                 RequestDispatcher rd = req.getRequestDispatcher("/IdP.jsp");
451                 rd.forward(req, res);
452         }
453
454         private static boolean useArtifactProfile(EntityDescriptor provider, String acceptanceURL) {
455
456                 // TODO this logic needs to be updated
457
458                 // Default to POST if we have no metadata
459                 if (provider == null) { return false; }
460
461                 // Default to POST if we have incomplete metadata
462                 SPSSODescriptor sp = provider.getSPSSODescriptor(org.opensaml.XML.SAML11_PROTOCOL_ENUM);
463                 if (sp == null) { return false; }
464
465                 // Look at the bindings.. prefer POST if we have multiple
466                 Iterator endpoints = sp.getAssertionConsumerServiceManager().getEndpoints();
467                 while (endpoints.hasNext()) {
468                         Endpoint ep = (Endpoint) endpoints.next();
469                         if (acceptanceURL.equals(ep.getLocation()) && SAMLBrowserProfile.PROFILE_POST_URI.equals(ep.getBinding())) { return false; }
470                         if (acceptanceURL.equals(ep.getLocation())
471                                         && SAMLBrowserProfile.PROFILE_ARTIFACT_URI.equals(ep.getBinding())) { return true; }
472                 }
473
474                 // Default to POST if we have incomplete metadata
475                 return false;
476         }
477
478         private static boolean pushAttributes(boolean artifactProfile) {
479
480                 if (artifactProfile) { return true; }
481                 // TODO implement overrides
482                 return false;
483         }
484
485         private static boolean isValidAssertionConsumerURL(EntityDescriptor provider, String shireURL)
486                         throws InvalidClientDataException {
487
488                 SPSSODescriptor sp = provider.getSPSSODescriptor(org.opensaml.XML.SAML11_PROTOCOL_ENUM);
489                 if (sp == null) {
490                         log.info("Inappropriate metadata for provider.");
491                         return false;
492                 }
493
494                 Iterator endpoints = sp.getAssertionConsumerServiceManager().getEndpoints();
495                 while (endpoints.hasNext()) {
496                         if (shireURL.equals(((Endpoint) endpoints.next()).getLocation())) { return true; }
497                 }
498                 log.info("Supplied consumer URL not found in metadata.");
499                 return false;
500         }
501 }