Create parser pool interface and move current pool to an implementation of this inter...
[java-idp.git] / src / edu / internet2 / middleware / shibboleth / idp / provider / ShibbolethV1SSOHandler.java
1 /*
2  * Copyright [2005] [University Corporation for Advanced Internet Development, Inc.]
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package edu.internet2.middleware.shibboleth.idp.provider;
18
19 import java.io.IOException;
20 import java.io.UnsupportedEncodingException;
21 import java.net.URLEncoder;
22 import java.util.ArrayList;
23 import java.util.Arrays;
24 import java.util.Collection;
25 import java.util.Collections;
26 import java.util.Date;
27 import java.util.Iterator;
28 import java.util.List;
29 import java.util.Vector;
30
31 import javax.servlet.RequestDispatcher;
32 import javax.servlet.ServletException;
33 import javax.servlet.http.HttpServletRequest;
34 import javax.servlet.http.HttpServletResponse;
35
36 import org.apache.log4j.Logger;
37 import org.bouncycastle.util.encoders.Base64;
38 import org.opensaml.SAMLAssertion;
39 import org.opensaml.SAMLAttribute;
40 import org.opensaml.SAMLAttributeStatement;
41 import org.opensaml.SAMLAudienceRestrictionCondition;
42 import org.opensaml.SAMLAuthenticationStatement;
43 import org.opensaml.SAMLAuthorityBinding;
44 import org.opensaml.SAMLBrowserProfile;
45 import org.opensaml.SAMLCondition;
46 import org.opensaml.SAMLException;
47 import org.opensaml.SAMLNameIdentifier;
48 import org.opensaml.SAMLResponse;
49 import org.opensaml.SAMLStatement;
50 import org.opensaml.SAMLSubject;
51 import org.opensaml.SAMLSubjectStatement;
52 import org.opensaml.artifact.Artifact;
53 import org.opensaml.saml2.metadata.AssertionConsumerService;
54 import org.opensaml.saml2.metadata.EntityDescriptor;
55 import org.opensaml.saml2.metadata.SPSSODescriptor;
56 import org.opensaml.saml2.metadata.provider.MetadataProviderException;
57 import org.w3c.dom.Element;
58
59 import edu.internet2.middleware.shibboleth.aa.AAException;
60 import edu.internet2.middleware.shibboleth.common.LocalPrincipal;
61 import edu.internet2.middleware.shibboleth.common.NameIdentifierMappingException;
62 import edu.internet2.middleware.shibboleth.common.RelyingParty;
63 import edu.internet2.middleware.shibboleth.common.ShibbolethConfigurationException;
64 import edu.internet2.middleware.shibboleth.idp.IdPProtocolHandler;
65 import edu.internet2.middleware.shibboleth.idp.IdPProtocolSupport;
66 import edu.internet2.middleware.shibboleth.idp.InvalidClientDataException;
67 import edu.internet2.middleware.shibboleth.idp.RequestHandlingException;
68
69 /**
70  * <code>ProtocolHandler</code> implementation that responds to SSO flows as specified in "Shibboleth Architecture:
71  * Protocols and Profiles".
72  * 
73  * @author Walter Hoehn
74  */
75 public class ShibbolethV1SSOHandler extends SSOHandler implements IdPProtocolHandler {
76
77         private static Logger log = Logger.getLogger(ShibbolethV1SSOHandler.class.getName());
78
79         /**
80          * Required DOM-based constructor.
81          */
82         public ShibbolethV1SSOHandler(Element config) throws ShibbolethConfigurationException {
83
84                 super(config);
85         }
86
87         /*
88          * @see edu.internet2.middleware.shibboleth.idp.IdPResponder.ProtocolHandler#processRequest(javax.servlet.http.HttpServletRequest,
89          *      javax.servlet.http.HttpServletResponse)
90          */
91         public void processRequest(HttpServletRequest request, HttpServletResponse response, IdPProtocolSupport support)
92                         throws RequestHandlingException, ServletException {
93
94                 try {
95
96                         // Set attributes that are needed by the jsp
97                         request.setAttribute("shire", request.getParameter("shire"));
98                         request.setAttribute("target", request.getParameter("target"));
99
100                         // Ensure that we have the required data from the servlet container
101                         validateEngineData(request);
102                         validateShibSpecificData(request);
103
104                         // Get the authN info
105                         String username = support.getIdPConfig().getAuthHeaderName().equalsIgnoreCase("REMOTE_USER") ? request
106                                         .getRemoteUser() : request.getHeader(support.getIdPConfig().getAuthHeaderName());
107                         if ((username == null) || (username.equals(""))) { throw new InvalidClientDataException(
108                                         "Unauthenticated principal. This protocol handler requires that authentication information be "
109                                                         + "provided from the servlet container."); }
110                         LocalPrincipal principal = new LocalPrincipal(username);
111
112                         // Select the appropriate Relying Party configuration for the request
113                         RelyingParty relyingParty = null;
114                         String remoteProviderId = request.getParameter("providerId");
115                         // If the SP did not send a Provider Id, then assume it is a Shib
116                         // 1.1 or older SP
117                         if (remoteProviderId == null || remoteProviderId.equals("")) {
118                                 throw new InvalidClientDataException("Invalid or missing service provider id.");
119                         } else {
120                                 log.debug("Remote provider has identified itself as: (" + remoteProviderId + ").");
121                                 relyingParty = support.getServiceProviderMapper().getRelyingParty(remoteProviderId);
122                         }
123
124                         // Grab the metadata for the provider
125                         EntityDescriptor descriptor = null;
126                         try {
127                                 descriptor = support.getEntityDescriptor(relyingParty.getProviderId());
128                         } catch (MetadataProviderException e1) {
129                                 log.error("Metadata lookup for provider (" + relyingParty.getProviderId() + ") encountered an error: "
130                                                 + e1);
131                         }
132
133                         // Make sure that the selected relying party configuration is appropriate for this
134                         // acceptance URL
135                         String acceptanceURL = request.getParameter("shire");
136
137                         if (descriptor == null) {
138                                 log.info("No metadata found for provider: (" + relyingParty.getProviderId() + ").");
139                                 relyingParty = support.getServiceProviderMapper().getRelyingParty(null);
140
141                         } else {
142                                 if (isValidAssertionConsumerURL(descriptor, acceptanceURL)) {
143                                         log.info("Supplied consumer URL validated for this provider.");
144                                 } else {
145                                         log.error("Assertion consumer service URL (" + acceptanceURL + ") is NOT valid for provider ("
146                                                         + relyingParty.getProviderId() + ").");
147                                         throw new InvalidClientDataException("Invalid assertion consumer service URL.");
148                                 }
149                         }
150
151                         // Create SAML Name Identifier & Subject
152                         SAMLNameIdentifier nameId;
153
154                         nameId = getNameIdentifier(support.getNameMapper(), principal, relyingParty, descriptor);
155
156                         String authenticationMethod = request.getHeader("SAMLAuthenticationMethod");
157                         if (authenticationMethod == null || authenticationMethod.equals("")) {
158                                 authenticationMethod = relyingParty.getDefaultAuthMethod().toString();
159                                 log.debug("User was authenticated via the default method for this relying party ("
160                                                 + authenticationMethod + ").");
161                         } else {
162                                 log.debug("User was authenticated via the method (" + authenticationMethod + ").");
163                         }
164
165                         SAMLSubject authNSubject = new SAMLSubject(nameId, null, null, null);
166
167                         // Is this artifact or POST?
168                         boolean artifactProfile = useArtifactProfile(descriptor, acceptanceURL, relyingParty);
169
170                         // SAML Artifact profile - don't even attempt this for legacy providers (they don't support it)
171                         if (artifactProfile) {
172                                 respondWithArtifact(request, response, support, principal, relyingParty, descriptor, acceptanceURL,
173                                                 nameId, authenticationMethod, authNSubject);
174
175                                 // SAML POST profile
176                         } else {
177                                 respondWithPOST(request, response, support, principal, relyingParty, descriptor, acceptanceURL, nameId,
178                                                 authenticationMethod, authNSubject);
179                         }
180                 } catch (InvalidClientDataException e) {
181                         throw new RequestHandlingException("Unable to handle request.  Client data is invalid: " + e);
182                 } catch (NameIdentifierMappingException e) {
183                         log.error("Error converting principal to SAML Name Identifier: " + e);
184                         throw new RequestHandlingException("Unable to handle request.  Error recognizing principal.");
185                 } catch (SAMLException e) {
186                         log.error("Error creating SAML Response: " + e);
187                         throw new RequestHandlingException("Unalbe to handle request.  Error creating SAML Response.");
188                 }
189         }
190
191         private void respondWithArtifact(HttpServletRequest request, HttpServletResponse response,
192                         IdPProtocolSupport support, LocalPrincipal principal, RelyingParty relyingParty,
193                         EntityDescriptor descriptor, String acceptanceURL, SAMLNameIdentifier nameId, String authenticationMethod,
194                         SAMLSubject authNSubject) throws SAMLException, ServletException, RequestHandlingException {
195
196                 log.debug("Responding with Artifact profile.");
197                 ArrayList<SAMLAssertion> assertions = new ArrayList<SAMLAssertion>();
198
199                 authNSubject.addConfirmationMethod(SAMLSubject.CONF_ARTIFACT);
200                 assertions.add(generateAuthNAssertion(request, relyingParty, descriptor, nameId, authenticationMethod,
201                                 getAuthNTime(request), authNSubject));
202
203                 // Package attributes for push, if necessary.
204                 if (pushAttributes(true, relyingParty)) {
205                         log.info("Resolving attributes for push.");
206                         generateAttributes(support, principal, relyingParty, assertions, request);
207                 }
208
209                 // Sign the assertions, if necessary
210                 boolean metaDataIndicatesSignAssertions = false;
211                 if (descriptor != null) {
212                         SPSSODescriptor sp = descriptor.getSPSSODescriptor(org.opensaml.XML.SAML11_PROTOCOL_ENUM);
213                         if (sp != null) {
214                                 if (sp.getWantAssertionsSigned()) {
215                                         metaDataIndicatesSignAssertions = true;
216                                 }
217                         }
218                 }
219                 if (relyingParty.wantsAssertionsSigned() || metaDataIndicatesSignAssertions) {
220                         support.signAssertions((SAMLAssertion[]) assertions.toArray(new SAMLAssertion[0]), relyingParty);
221                 }
222
223                 // Create artifacts for each assertion
224                 ArrayList<Artifact> artifacts = new ArrayList<Artifact>();
225                 for (int i = 0; i < assertions.size(); i++) {
226                         SAMLAssertion assertion = (SAMLAssertion) assertions.get(i);
227                         Artifact artifact = support.getArtifactMapper().generateArtifact(assertion, relyingParty);
228                         artifacts.add(artifact);
229
230                         // Put attributes names in the transaction log when it is set to DEBUG
231                         if (support.getTransactionLog().isDebugEnabled()) {
232                                 Iterator statements = assertion.getStatements();
233                                 while (statements.hasNext()) {
234                                         SAMLStatement statement = (SAMLStatement) statements.next();
235                                         if (statement instanceof SAMLAttributeStatement) {
236                                                 Iterator attributes = ((SAMLAttributeStatement) statement).getAttributes();
237                                                 StringBuffer attributeBuffer = new StringBuffer();
238                                                 while (attributes.hasNext()) {
239                                                         SAMLAttribute attribute = (SAMLAttribute) attributes.next();
240                                                         attributeBuffer.append("(" + attribute.getName() + ")");
241                                                         support.getTransactionLog().debug(
242                                                                         "Artifact (" + artifact.encode() + ") created with the following attributes: "
243                                                                                         + attributeBuffer.toString());
244                                                 }
245                                         }
246                                 }
247                         }
248                 }
249
250                 try {
251                         // Assemble the query string
252                         StringBuffer destination = new StringBuffer(acceptanceURL);
253                         destination.append("?TARGET=");
254
255                         destination.append(URLEncoder.encode(request.getParameter("target"), "UTF-8"));
256
257                         Iterator iterator = artifacts.iterator();
258                         StringBuffer artifactBuffer = new StringBuffer(); // Buffer for the transaction log
259
260                         // Construct the artifact query parameter
261                         while (iterator.hasNext()) {
262                                 Artifact artifact = (Artifact) iterator.next();
263                                 artifactBuffer.append("(" + artifact.encode() + ")");
264                                 destination.append("&SAMLart=");
265                                 destination.append(URLEncoder.encode(artifact.encode(), "UTF-8"));
266                         }
267
268                         log.debug("Redirecting to (" + destination.toString() + ").");
269
270                         response.sendRedirect(destination.toString());
271
272                         // Redirect to the artifact receiver
273                         support.getTransactionLog().info(
274                                         "Assertion artifact(s) (" + artifactBuffer.toString() + ") issued to provider ("
275                                                         + relyingParty.getProviderId() + ") on behalf of principal (" + principal.getName()
276                                                         + "). Name Identifier: (" + nameId.getName() + "). Name Identifier Format: ("
277                                                         + nameId.getFormat() + ").");
278                 } catch (UnsupportedEncodingException e) {
279                         log.error("Error encoding URL: " + e);
280                         throw new RequestHandlingException("Unable to handle request.  URL Encoder malfuntion.");
281                 } catch (IOException e) {
282                         log.error("Error issuing redirect: " + e);
283                         throw new ServletException(e);
284                 }
285         }
286
287         public static boolean pushAttributeDefault = false;
288
289         private void respondWithPOST(HttpServletRequest request, HttpServletResponse response, IdPProtocolSupport support,
290                         LocalPrincipal principal, RelyingParty relyingParty, EntityDescriptor descriptor, String acceptanceURL,
291                         SAMLNameIdentifier nameId, String authenticationMethod, SAMLSubject authNSubject) throws SAMLException,
292                         ServletException {
293
294                 log.debug("Responding with POST profile.");
295                 ArrayList<SAMLAssertion> assertions = new ArrayList<SAMLAssertion>();
296                 authNSubject.addConfirmationMethod(SAMLSubject.CONF_BEARER);
297                 assertions.add(generateAuthNAssertion(request, relyingParty, descriptor, nameId, authenticationMethod,
298                                 getAuthNTime(request), authNSubject));
299
300                 // Package attributes for push, if necessary.
301                 if (pushAttributes(pushAttributeDefault, relyingParty)) {
302                         log.info("Resolving attributes for push.");
303                         generateAttributes(support, principal, relyingParty, assertions, request);
304                 }
305
306                 // Sign the assertions, if necessary
307                 boolean metaDataIndicatesSignAssertions = false;
308                 if (descriptor != null) {
309                         SPSSODescriptor sp = descriptor.getSPSSODescriptor(org.opensaml.XML.SAML11_PROTOCOL_ENUM);
310                         if (sp != null) {
311                                 if (sp.getWantAssertionsSigned()) {
312                                         metaDataIndicatesSignAssertions = true;
313                                 }
314                         }
315                 }
316                 if (relyingParty.wantsAssertionsSigned() || metaDataIndicatesSignAssertions) {
317                         support.signAssertions((SAMLAssertion[]) assertions.toArray(new SAMLAssertion[0]), relyingParty);
318                 }
319
320                 // Set attributes needed by form
321                 request.setAttribute("acceptanceURL", acceptanceURL);
322                 request.setAttribute("target", request.getParameter("target"));
323
324                 SAMLResponse samlResponse = new SAMLResponse(null, acceptanceURL, assertions, null);
325
326                 support.signResponse(samlResponse, relyingParty);
327
328                 try {
329                         createPOSTForm(request, response, samlResponse.toBase64());
330                 } catch (IOException e) {
331                         log.error("Error creating POST Form: " + e);
332                         throw new ServletException(e);
333                 }
334
335                 // Make transaction log entry
336                 support.getTransactionLog().info(
337                                 "Authentication assertion issued to provider (" + relyingParty.getProviderId()
338                                                 + ") on behalf of principal (" + principal.getName() + "). Name Identifier: ("
339                                                 + nameId.getName() + "). Name Identifier Format: (" + nameId.getFormat() + ").");
340
341         }
342
343         private void generateAttributes(IdPProtocolSupport support, LocalPrincipal principal, RelyingParty relyingParty,
344                         ArrayList<SAMLAssertion> assertions, HttpServletRequest request) throws SAMLException {
345
346                 try {
347                         Collection<? extends SAMLAttribute> attributes = support.getReleaseAttributes(principal, relyingParty,
348                                         relyingParty.getProviderId());
349                         log.info("Found " + attributes.size() + " attribute(s) for " + principal.getName());
350
351                         // Bail if we didn't get any attributes
352                         if (attributes == null || attributes.size() < 1) {
353                                 log.info("No attributes resolved.");
354                                 return;
355                         }
356
357                         // Reference requested subject
358                         SAMLSubject attrSubject = (SAMLSubject) ((SAMLSubjectStatement) ((SAMLAssertion) assertions.get(0))
359                                         .getStatements().next()).getSubject().clone();
360
361                         // May be one assertion or two.
362                         if (relyingParty.singleAssertion()) {
363                                 log.debug("merging attributes into existing authn assertion");
364                                 // Put all attributes into an assertion
365                                 ((SAMLAssertion) assertions.get(0)).addStatement(new SAMLAttributeStatement(attrSubject, Arrays
366                                                 .asList(attributes)));
367
368                                 if (log.isDebugEnabled()) {
369                                         log.debug("Dumping combined Assertion:" + System.getProperty("line.separator")
370                                                         + assertions.get(0).toString());
371                                 }
372                         } else {
373                                 ArrayList<String> audiences = new ArrayList<String>();
374                                 if (relyingParty.getProviderId() != null) {
375                                         audiences.add(relyingParty.getProviderId());
376                                 }
377                                 if (relyingParty.getName() != null && !relyingParty.getName().equals(relyingParty.getProviderId())) {
378                                         audiences.add(relyingParty.getName());
379                                 }
380                                 String remoteProviderId = request.getParameter("providerId");
381                                 if (remoteProviderId != null && !remoteProviderId.equals("") && !audiences.contains(remoteProviderId)) {
382                                         audiences.add(remoteProviderId);
383                                 }
384
385                                 SAMLCondition condition = new SAMLAudienceRestrictionCondition(audiences);
386
387                                 // Put all attributes into an assertion
388                                 SAMLStatement statement = new SAMLAttributeStatement(attrSubject, attributes);
389
390                                 // Set assertion expiration to longest attribute expiration
391                                 long max = 0;
392                                 for (SAMLAttribute attribute : attributes) {
393                                         if (max < attribute.getLifetime()) {
394                                                 max = attribute.getLifetime();
395                                         }
396                                 }
397                                 Date now = new Date();
398                                 Date then = new Date(now.getTime() + (max * 1000)); // max is in seconds
399
400                                 SAMLAssertion attrAssertion = new SAMLAssertion(relyingParty.getIdentityProvider().getProviderId(),
401                                                 now, then, Collections.singleton(condition), null, Collections.singleton(statement));
402                                 assertions.add(attrAssertion);
403
404                                 if (log.isDebugEnabled()) {
405                                         log.debug("Dumping generated Attribute Assertion:" + System.getProperty("line.separator")
406                                                         + attrAssertion.toString());
407                                 }
408                         }
409                 } catch (AAException e) {
410                         log.error("An error was encountered while generating assertion for attribute push: " + e);
411                         throw new SAMLException(SAMLException.RESPONDER, "General error processing request.");
412                 } catch (CloneNotSupportedException e) {
413                         log.error("An error was encountered while generating assertion for attribute push: " + e);
414                         throw new SAMLException(SAMLException.RESPONDER, "General error processing request.");
415                 }
416         }
417
418         private SAMLAssertion generateAuthNAssertion(HttpServletRequest request, RelyingParty relyingParty,
419                         EntityDescriptor descriptor, SAMLNameIdentifier nameId, String authenticationMethod, Date authTime,
420                         SAMLSubject subject) throws SAMLException {
421
422                 // Determine the correct audiences
423                 ArrayList<String> audiences = new ArrayList<String>();
424                 if (relyingParty.getProviderId() != null) {
425                         audiences.add(relyingParty.getProviderId());
426                 }
427                 if (relyingParty.getName() != null && !relyingParty.getName().equals(relyingParty.getProviderId())) {
428                         audiences.add(relyingParty.getName());
429                 }
430                 String remoteProviderId = request.getParameter("providerId");
431                 if (remoteProviderId != null && !remoteProviderId.equals("") && !audiences.contains(remoteProviderId)) {
432                         audiences.add(remoteProviderId);
433                 }
434
435                 // Determine the correct issuer
436                 String issuer = relyingParty.getIdentityProvider().getProviderId();
437
438                 ArrayList<SAMLAuthorityBinding> bindings = new ArrayList<SAMLAuthorityBinding>();
439
440                 // Create the assertion
441                 Vector<SAMLCondition> conditions = new Vector<SAMLCondition>(1);
442                 if (audiences != null && audiences.size() > 0) conditions.add(new SAMLAudienceRestrictionCondition(audiences));
443
444                 SAMLStatement[] statements = {new SAMLAuthenticationStatement(subject, authenticationMethod, authTime, request
445                                 .getRemoteAddr(), null, bindings)};
446
447                 SAMLAssertion assertion = new SAMLAssertion(issuer, new Date(System.currentTimeMillis()), new Date(System
448                                 .currentTimeMillis() + 300000), conditions, null, Arrays.asList(statements));
449
450                 if (log.isDebugEnabled()) {
451                         log.debug("Dumping generated AuthN Assertion:" + System.getProperty("line.separator")
452                                         + assertion.toString());
453                 }
454
455                 return assertion;
456         }
457
458         /*
459          * @see edu.internet2.middleware.shibboleth.idp.IdPResponder.ProtocolHandler#getHandlerName()
460          */
461         public String getHandlerName() {
462
463                 return "Shibboleth v1.x SSO";
464         }
465
466         private void validateShibSpecificData(HttpServletRequest request) throws InvalidClientDataException {
467
468                 if (request.getParameter("target") == null || request.getParameter("target").equals("")) { throw new InvalidClientDataException(
469                                 "Invalid data from Service Provider: no target URL received."); }
470                 if ((request.getParameter("shire") == null) || (request.getParameter("shire").equals(""))) { throw new InvalidClientDataException(
471                                 "Invalid data from Service Provider: No acceptance URL received."); }
472         }
473
474         private static void createPOSTForm(HttpServletRequest req, HttpServletResponse res, byte[] buf) throws IOException,
475                         ServletException {
476
477                 // Hardcoded to ASCII to ensure Base64 encoding compatibility
478                 req.setAttribute("assertion", new String(buf, "ASCII"));
479
480                 if (log.isDebugEnabled()) {
481                         log.debug("Dumping generated SAML Response:" + System.getProperty("line.separator")
482                                         + new String(Base64.decode(buf)));
483                 }
484
485                 RequestDispatcher rd = req.getRequestDispatcher("/IdP.jsp");
486                 rd.forward(req, res);
487         }
488
489         /**
490          * Boolean indication of which browser profile is in effect. "true" indicates Artifact and "false" indicates POST.
491          */
492         private static boolean useArtifactProfile(EntityDescriptor descriptor, String acceptanceURL,
493                         RelyingParty relyingParty) {
494
495                 boolean artifactMeta = false;
496                 boolean postMeta = false;
497
498                 // Look at the metadata bindings, if we can find them
499                 if (descriptor != null) {
500                         SPSSODescriptor sp = descriptor.getSPSSODescriptor(org.opensaml.XML.SAML11_PROTOCOL_ENUM);
501
502                         if (sp != null) {
503
504                                 // See if this is the default endpoint location.
505                                 AssertionConsumerService defaultEndpoint = sp.getDefaultAssertionConsumerService();
506                                 if (defaultEndpoint != null && defaultEndpoint.getLocation().equals(acceptanceURL)) {
507                                         // If we recognize the default binding, this is the one to use.
508                                         if (defaultEndpoint.getBinding().equals(SAMLBrowserProfile.PROFILE_POST_URI)) return false;
509                                         else if (defaultEndpoint.getBinding().equals(SAMLBrowserProfile.PROFILE_ARTIFACT_URI)) return true;
510                                 }
511                                 // If not, look through everything we have
512                                 List<AssertionConsumerService> endpoints = sp.getAssertionConsumerServices();
513                                 for (AssertionConsumerService ep : endpoints) {
514                                         if (acceptanceURL.equals(ep.getLocation())
515                                                         && SAMLBrowserProfile.PROFILE_POST_URI.equals(ep.getBinding())) {
516                                                 log.debug("Metadata indicates support for POST profile.");
517                                                 postMeta = true;
518                                                 continue;
519                                         }
520                                 }
521
522                                 endpoints = sp.getAssertionConsumerServices();
523                                 for (AssertionConsumerService ep : endpoints) {
524                                         if (acceptanceURL.equals(ep.getLocation())
525                                                         && SAMLBrowserProfile.PROFILE_ARTIFACT_URI.equals(ep.getBinding())) {
526                                                 log.debug("Metadata indicates support for Artifact profile.");
527                                                 artifactMeta = true;
528                                                 continue;
529                                         }
530                                 }
531                         }
532                 }
533
534                 // If we have metadata for both, use the relying party default
535                 if (!(artifactMeta && postMeta)) {
536
537                         // If we only have metadata for one, use it
538                         if (artifactMeta) { return true; }
539                         if (postMeta) { return false; }
540
541                 }
542
543                 // If we have missing or incomplete metadata, use relying party default
544                 if (relyingParty.defaultToPOSTProfile()) {
545                         return false;
546                 } else {
547                         return true;
548                 }
549         }
550
551         /**
552          * Boolean indication of whether an assertion containing an attribute statement should be bundled in the response
553          * with the assertion containing the AuthN statement.
554          */
555         private static boolean pushAttributes(boolean artifactProfile, RelyingParty relyingParty) {
556
557                 // By default push for Artifact and don't push for POST
558                 // This can be overriden at the level of the relying party
559                 if (relyingParty.forceAttributePush()) {
560                         return true;
561                 } else if (relyingParty.forceAttributeNoPush()) {
562                         return false;
563                 } else if (artifactProfile) {
564                         return true;
565                 } else {
566                         return false;
567                 }
568         }
569
570         /**
571          * Boolean indication of whethere or not a given assertion consumer URL is valid for a given SP.
572          */
573         private static boolean isValidAssertionConsumerURL(EntityDescriptor descriptor, String shireURL)
574                         throws InvalidClientDataException {
575
576                 SPSSODescriptor sp = descriptor.getSPSSODescriptor(org.opensaml.XML.SAML11_PROTOCOL_ENUM);
577                 if (sp == null) {
578                         log.info("Inappropriate metadata for provider.");
579                         return false;
580                 }
581
582                 List<AssertionConsumerService> endpoints = sp.getAssertionConsumerServices();
583                 for (AssertionConsumerService endpoint : endpoints) {
584                         if (shireURL.equals(endpoint.getLocation())) { return true; }
585                 }
586                 log.info("Supplied consumer URL not found in metadata.");
587                 return false;
588         }
589 }