Code cleanups in preparation for 2.0 work.
[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.Collections;
25 import java.util.Date;
26 import java.util.Iterator;
27 import java.util.Vector;
28
29 import javax.servlet.RequestDispatcher;
30 import javax.servlet.ServletException;
31 import javax.servlet.http.HttpServletRequest;
32 import javax.servlet.http.HttpServletResponse;
33 import javax.xml.namespace.QName;
34
35 import org.apache.log4j.Logger;
36 import org.bouncycastle.util.encoders.Base64;
37 import org.opensaml.SAMLAssertion;
38 import org.opensaml.SAMLAttribute;
39 import org.opensaml.SAMLAttributeStatement;
40 import org.opensaml.SAMLAudienceRestrictionCondition;
41 import org.opensaml.SAMLAuthenticationStatement;
42 import org.opensaml.SAMLAuthorityBinding;
43 import org.opensaml.SAMLBinding;
44 import org.opensaml.SAMLBrowserProfile;
45 import org.opensaml.SAMLCondition;
46 import org.opensaml.SAMLException;
47 import org.opensaml.SAMLNameIdentifier;
48 import org.opensaml.SAMLRequest;
49 import org.opensaml.SAMLResponse;
50 import org.opensaml.SAMLStatement;
51 import org.opensaml.SAMLSubject;
52 import org.opensaml.SAMLSubjectStatement;
53 import org.opensaml.artifact.Artifact;
54 import org.w3c.dom.Element;
55
56 import edu.internet2.middleware.shibboleth.aa.AAException;
57 import edu.internet2.middleware.shibboleth.common.LocalPrincipal;
58 import edu.internet2.middleware.shibboleth.common.NameIdentifierMappingException;
59 import edu.internet2.middleware.shibboleth.common.RelyingParty;
60 import edu.internet2.middleware.shibboleth.common.ShibbolethConfigurationException;
61 import edu.internet2.middleware.shibboleth.idp.IdPProtocolHandler;
62 import edu.internet2.middleware.shibboleth.idp.IdPProtocolSupport;
63 import edu.internet2.middleware.shibboleth.idp.InvalidClientDataException;
64 import edu.internet2.middleware.shibboleth.metadata.Endpoint;
65 import edu.internet2.middleware.shibboleth.metadata.EntityDescriptor;
66 import edu.internet2.middleware.shibboleth.metadata.SPSSODescriptor;
67
68 /**
69  * <code>ProtocolHandler</code> implementation that responds to SSO flows as specified in "Shibboleth Architecture:
70  * Protocols and Profiles". Includes a compatibility mode for dealing with Shibboleth v1.1 SPs.
71  * 
72  * @author Walter Hoehn
73  */
74 public class ShibbolethV1SSOHandler extends SSOHandler implements IdPProtocolHandler {
75
76         private static Logger log = Logger.getLogger(ShibbolethV1SSOHandler.class.getName());
77
78         /**
79          * Required DOM-based constructor.
80          */
81         public ShibbolethV1SSOHandler(Element config) throws ShibbolethConfigurationException {
82
83                 super(config);
84         }
85
86         /*
87          * @see edu.internet2.middleware.shibboleth.idp.IdPResponder.ProtocolHandler#processRequest(javax.servlet.http.HttpServletRequest,
88          *      javax.servlet.http.HttpServletResponse)
89          */
90         public SAMLResponse processRequest(HttpServletRequest request, HttpServletResponse response,
91                         SAMLRequest samlRequest, IdPProtocolSupport support) throws SAMLException, ServletException, IOException {
92
93                 if (request == null) {
94                         log.error("Protocol Handler received a SAML Request, but is unable to handle it.");
95                         throw new SAMLException(SAMLException.RESPONDER, "General error processing request.");
96                 }
97
98                 // Set attributes that are needed by the jsp
99                 request.setAttribute("shire", request.getParameter("shire"));
100                 request.setAttribute("target", request.getParameter("target"));
101
102                 try {
103                         // Ensure that we have the required data from the servlet container
104                         validateEngineData(request);
105                         validateShibSpecificData(request);
106
107                         // Get the authN info
108                         String username = support.getIdPConfig().getAuthHeaderName().equalsIgnoreCase("REMOTE_USER") ? request
109                                         .getRemoteUser() : request.getHeader(support.getIdPConfig().getAuthHeaderName());
110                         if ((username == null) || (username.equals(""))) { throw new InvalidClientDataException(
111                                         "Unauthenticated principal. This protocol handler requires that authentication information be "
112                                                         + "provided from the servlet container."); }
113                         LocalPrincipal principal = new LocalPrincipal(username);
114
115                         // Select the appropriate Relying Party configuration for the request
116                         RelyingParty relyingParty = null;
117                         String remoteProviderId = request.getParameter("providerId");
118                         // If the SP did not send a Provider Id, then assume it is a Shib
119                         // 1.1 or older SP
120                         if (remoteProviderId == null) {
121                                 relyingParty = support.getServiceProviderMapper().getLegacyRelyingParty();
122                         } else if (remoteProviderId.equals("")) {
123                                 throw new InvalidClientDataException("Invalid service provider id.");
124                         } else {
125                                 log.debug("Remote provider has identified itself as: (" + remoteProviderId + ").");
126                                 relyingParty = support.getServiceProviderMapper().getRelyingParty(remoteProviderId);
127                         }
128
129                         // Grab the metadata for the provider
130                         EntityDescriptor descriptor = support.lookup(relyingParty.getProviderId());
131
132                         // Make sure that the selected relying party configuration is appropriate for this
133                         // acceptance URL
134                         String acceptanceURL = request.getParameter("shire");
135                         if (!relyingParty.isLegacyProvider()) {
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
152                         // Create SAML Name Identifier & Subject
153                         SAMLNameIdentifier nameId;
154                         try {
155                                 nameId = getNameIdentifier(support.getNameMapper(), principal, relyingParty, descriptor);
156                         } catch (NameIdentifierMappingException e) {
157                                 log.error("Error converting principal to SAML Name Identifier: " + e);
158                                 throw new SAMLException("Error converting principal to SAML Name Identifier.", e);
159                         }
160
161                         String authenticationMethod = request.getHeader("SAMLAuthenticationMethod");
162                         if (authenticationMethod == null || authenticationMethod.equals("")) {
163                                 authenticationMethod = relyingParty.getDefaultAuthMethod().toString();
164                                 log.debug("User was authenticated via the default method for this relying party ("
165                                                 + authenticationMethod + ").");
166                         } else {
167                                 log.debug("User was authenticated via the method (" + authenticationMethod + ").");
168                         }
169
170                         SAMLSubject authNSubject = new SAMLSubject(nameId, null, null, null);
171
172                         // Is this artifact or POST?
173                         boolean artifactProfile = useArtifactProfile(descriptor, acceptanceURL, relyingParty);
174
175                         // SAML Artifact profile - don't even attempt this for legacy providers (they don't support it)
176                         if (!relyingParty.isLegacyProvider() && artifactProfile) {
177                                 respondWithArtifact(request, response, support, principal, relyingParty, descriptor, acceptanceURL,
178                                                 nameId, authenticationMethod, authNSubject);
179
180                                 // SAML POST profile
181                         } else {
182                                 respondWithPOST(request, response, support, principal, relyingParty, descriptor, acceptanceURL, nameId,
183                                                 authenticationMethod, authNSubject);
184                         }
185                 } catch (InvalidClientDataException e) {
186                         throw new SAMLException(SAMLException.RESPONDER, e.getMessage());
187                 }
188                 return null;
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, IOException, UnsupportedEncodingException {
195
196                 log.debug("Responding with Artifact profile.");
197                 ArrayList assertions = new ArrayList();
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 (!relyingParty.isLegacyProvider() && 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 artifacts = new ArrayList();
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                 // Assemble the query string
251                 StringBuffer destination = new StringBuffer(acceptanceURL);
252                 destination.append("?TARGET=");
253                 destination.append(URLEncoder.encode(request.getParameter("target"), "UTF-8"));
254                 Iterator iterator = artifacts.iterator();
255                 StringBuffer artifactBuffer = new StringBuffer(); // Buffer for the transaction log
256
257                 // Construct the artifact query parameter
258                 while (iterator.hasNext()) {
259                         Artifact artifact = (Artifact) iterator.next();
260                         artifactBuffer.append("(" + artifact.encode() + ")");
261                         destination.append("&SAMLart=");
262                         destination.append(URLEncoder.encode(artifact.encode(), "UTF-8"));
263                 }
264
265                 log.debug("Redirecting to (" + destination.toString() + ").");
266                 response.sendRedirect(destination.toString()); // Redirect to the artifact receiver
267                 support.getTransactionLog().info(
268                                 "Assertion artifact(s) (" + artifactBuffer.toString() + ") issued to provider ("
269                                                 + relyingParty.getProviderId() + ") on behalf of principal (" + principal.getName()
270                                                 + "). Name Identifier: (" + nameId.getName() + "). Name Identifier Format: ("
271                                                 + nameId.getFormat() + ").");
272         }
273
274     public static boolean pushAttributeDefault = false;
275     
276         private void respondWithPOST(HttpServletRequest request, HttpServletResponse response, IdPProtocolSupport support,
277                         LocalPrincipal principal, RelyingParty relyingParty, EntityDescriptor descriptor, String acceptanceURL,
278                         SAMLNameIdentifier nameId, String authenticationMethod, SAMLSubject authNSubject) throws SAMLException,
279                         IOException, ServletException {
280
281                 log.debug("Responding with POST profile.");
282                 ArrayList assertions = new ArrayList();
283                 authNSubject.addConfirmationMethod(SAMLSubject.CONF_BEARER);
284                 assertions.add(generateAuthNAssertion(request, relyingParty, descriptor, nameId, authenticationMethod,
285                                 getAuthNTime(request), authNSubject));
286
287                 // Package attributes for push, if necessary.
288                 if (!relyingParty.isLegacyProvider() && pushAttributes(pushAttributeDefault, relyingParty)) {
289                         log.info("Resolving attributes for push.");
290                         generateAttributes(support, principal, relyingParty, assertions, request);
291                 }
292
293                 // Sign the assertions, if necessary
294                 boolean metaDataIndicatesSignAssertions = false;
295                 if (descriptor != null) {
296                         SPSSODescriptor sp = descriptor.getSPSSODescriptor(org.opensaml.XML.SAML11_PROTOCOL_ENUM);
297                         if (sp != null) {
298                                 if (sp.getWantAssertionsSigned()) {
299                                         metaDataIndicatesSignAssertions = true;
300                                 }
301                         }
302                 }
303                 if (relyingParty.wantsAssertionsSigned() || metaDataIndicatesSignAssertions) {
304                         support.signAssertions((SAMLAssertion[]) assertions.toArray(new SAMLAssertion[0]), relyingParty);
305                 }
306
307                 // Set attributes needed by form
308                 request.setAttribute("acceptanceURL", acceptanceURL);
309                 request.setAttribute("target", request.getParameter("target"));
310
311                 SAMLResponse samlResponse = new SAMLResponse(null, acceptanceURL, assertions, null);
312
313                 support.signResponse(samlResponse, relyingParty);
314
315                 createPOSTForm(request, response, samlResponse.toBase64());
316
317                 // Make transaction log entry
318                 if (relyingParty.isLegacyProvider()) {
319                         support.getTransactionLog().info(
320                                         "Authentication assertion issued to legacy provider (SHIRE: " + request.getParameter("shire")
321                                                         + ") on behalf of principal (" + principal.getName() + ") for resource ("
322                                                         + request.getParameter("target") + "). Name Identifier: (" + nameId.getName()
323                                                         + "). Name Identifier Format: (" + nameId.getFormat() + ").");
324
325                 } else {
326                         support.getTransactionLog().info(
327                                         "Authentication assertion issued to provider (" + relyingParty.getProviderId()
328                                                         + ") on behalf of principal (" + principal.getName() + "). Name Identifier: ("
329                                                         + nameId.getName() + "). Name Identifier Format: (" + nameId.getFormat() + ").");
330                 }
331         }
332
333         private void generateAttributes(IdPProtocolSupport support, LocalPrincipal principal, RelyingParty relyingParty,
334                         ArrayList assertions, HttpServletRequest request) throws SAMLException {
335
336                 try {
337                         SAMLAttribute[] attributes = support.getReleaseAttributes(principal, relyingParty, relyingParty
338                                         .getProviderId(), null);
339                         log.info("Found " + attributes.length + " attribute(s) for " + principal.getName());
340
341                         // Bail if we didn't get any attributes
342                         if (attributes == null || attributes.length < 1) {
343                                 log.info("No attributes resolved.");
344                                 return;
345                         }
346
347                         // Reference requested subject
348                         SAMLSubject attrSubject = (SAMLSubject) ((SAMLSubjectStatement) ((SAMLAssertion) assertions.get(0))
349                                         .getStatements().next()).getSubject().clone();
350
351                         // May be one assertion or two.
352                         if (relyingParty.singleAssertion()) {
353                                 log.debug("merging attributes into existing authn assertion");
354                                 // Put all attributes into an assertion
355                                 ((SAMLAssertion) assertions.get(0)).addStatement(new SAMLAttributeStatement(attrSubject, Arrays
356                                                 .asList(attributes)));
357
358                                 if (log.isDebugEnabled()) {
359                                         log.debug("Dumping combined Assertion:" + System.getProperty("line.separator")
360                                                         + assertions.get(0).toString());
361                                 }
362                         } else {
363                                 ArrayList audiences = new ArrayList();
364                                 if (relyingParty.getProviderId() != null) {
365                                         audiences.add(relyingParty.getProviderId());
366                                 }
367                                 if (relyingParty.getName() != null && !relyingParty.getName().equals(relyingParty.getProviderId())) {
368                                         audiences.add(relyingParty.getName());
369                                 }
370                                 String remoteProviderId = request.getParameter("providerId");
371                                 if (remoteProviderId != null && !remoteProviderId.equals("") && !audiences.contains(remoteProviderId)) {
372                                         audiences.add(remoteProviderId);
373                                 }
374
375                                 SAMLCondition condition = new SAMLAudienceRestrictionCondition(audiences);
376
377                                 // Put all attributes into an assertion
378                                 SAMLStatement statement = new SAMLAttributeStatement(attrSubject, Arrays.asList(attributes));
379
380                                 // Set assertion expiration to longest attribute expiration
381                                 long max = 0;
382                                 for (int i = 0; i < attributes.length; i++) {
383                                         if (max < attributes[i].getLifetime()) {
384                                                 max = attributes[i].getLifetime();
385                                         }
386                                 }
387                                 Date now = new Date();
388                                 Date then = new Date(now.getTime() + (max * 1000)); // max is in seconds
389
390                                 SAMLAssertion attrAssertion = new SAMLAssertion(relyingParty.getIdentityProvider().getProviderId(),
391                                                 now, then, Collections.singleton(condition), null, Collections.singleton(statement));
392                                 assertions.add(attrAssertion);
393
394                                 if (log.isDebugEnabled()) {
395                                         log.debug("Dumping generated Attribute Assertion:" + System.getProperty("line.separator")
396                                                         + attrAssertion.toString());
397                                 }
398                         }
399                 } catch (AAException e) {
400                         log.error("An error was encountered while generating assertion for attribute push: " + e);
401                         throw new SAMLException(SAMLException.RESPONDER, "General error processing request.");
402                 } catch (CloneNotSupportedException e) {
403                         log.error("An error was encountered while generating assertion for attribute push: " + e);
404                         throw new SAMLException(SAMLException.RESPONDER, "General error processing request.");
405                 }
406         }
407
408         private SAMLAssertion generateAuthNAssertion(HttpServletRequest request, RelyingParty relyingParty,
409                         EntityDescriptor descriptor, SAMLNameIdentifier nameId, String authenticationMethod, Date authTime,
410                         SAMLSubject subject) throws SAMLException, IOException {
411
412                 // Determine the correct audiences
413                 ArrayList audiences = new ArrayList();
414                 if (relyingParty.getProviderId() != null) {
415                         audiences.add(relyingParty.getProviderId());
416                 }
417                 if (relyingParty.getName() != null && !relyingParty.getName().equals(relyingParty.getProviderId())) {
418                         audiences.add(relyingParty.getName());
419                 }
420                 String remoteProviderId = request.getParameter("providerId");
421                 if (remoteProviderId != null && !remoteProviderId.equals("") && !audiences.contains(remoteProviderId)) {
422                         audiences.add(remoteProviderId);
423                 }
424
425                 // Determine the correct issuer
426                 String issuer = null;
427                 if (relyingParty.isLegacyProvider()) {
428
429                         log.debug("Service Provider is running Shibboleth <= 1.1. Using old style issuer.");
430                         if (relyingParty.getIdentityProvider().getSigningCredential() == null
431                                         || relyingParty.getIdentityProvider().getSigningCredential().getX509Certificate() == null) { throw new SAMLException(
432                                         "Cannot serve legacy style assertions without an X509 certificate"); }
433                         issuer = getHostNameFromDN(relyingParty.getIdentityProvider().getSigningCredential().getX509Certificate()
434                                         .getSubjectX500Principal());
435                         if (issuer == null || issuer.equals("")) { throw new SAMLException(
436                                         "Error parsing certificate DN while determining legacy issuer name."); }
437
438                 } else {
439                         issuer = relyingParty.getIdentityProvider().getProviderId();
440                 }
441
442                 // For compatibility with pre-1.2 shibboleth targets, include a pointer to the AA
443                 ArrayList bindings = new ArrayList();
444                 if (relyingParty.isLegacyProvider()) {
445
446                         SAMLAuthorityBinding binding = new SAMLAuthorityBinding(SAMLBinding.SOAP, relyingParty.getAAUrl()
447                                         .toString(), new QName(org.opensaml.XML.SAMLP_NS, "AttributeQuery"));
448                         bindings.add(binding);
449                 }
450
451                 // Create the assertion
452                 Vector conditions = new Vector(1);
453                 if (audiences != null && audiences.size() > 0) conditions.add(new SAMLAudienceRestrictionCondition(audiences));
454
455                 SAMLStatement[] statements = {new SAMLAuthenticationStatement(subject, authenticationMethod, authTime, request
456                                 .getRemoteAddr(), null, bindings)};
457
458                 SAMLAssertion assertion = new SAMLAssertion(issuer, new Date(System.currentTimeMillis()), new Date(System
459                                 .currentTimeMillis() + 300000), conditions, null, Arrays.asList(statements));
460
461                 if (log.isDebugEnabled()) {
462                         log.debug("Dumping generated AuthN Assertion:" + System.getProperty("line.separator")
463                                         + assertion.toString());
464                 }
465
466                 return assertion;
467         }
468
469         /*
470          * @see edu.internet2.middleware.shibboleth.idp.IdPResponder.ProtocolHandler#getHandlerName()
471          */
472         public String getHandlerName() {
473
474                 return "Shibboleth v1.x SSO";
475         }
476
477         private void validateShibSpecificData(HttpServletRequest request) throws InvalidClientDataException {
478
479                 if (request.getParameter("target") == null || request.getParameter("target").equals("")) { throw new InvalidClientDataException(
480                                 "Invalid data from Service Provider: no target URL received."); }
481                 if ((request.getParameter("shire") == null) || (request.getParameter("shire").equals(""))) { throw new InvalidClientDataException(
482                                 "Invalid data from Service Provider: No acceptance URL received."); }
483         }
484
485         private static void createPOSTForm(HttpServletRequest req, HttpServletResponse res, byte[] buf) throws IOException,
486                         ServletException {
487
488                 // Hardcoded to ASCII to ensure Base64 encoding compatibility
489                 req.setAttribute("assertion", new String(buf, "ASCII"));
490
491                 if (log.isDebugEnabled()) {
492                         log.debug("Dumping generated SAML Response:" + System.getProperty("line.separator")
493                                         + new String(Base64.decode(buf)));
494                 }
495
496                 RequestDispatcher rd = req.getRequestDispatcher("/IdP.jsp");
497                 rd.forward(req, res);
498         }
499
500         /**
501          * Boolean indication of which browser profile is in effect. "true" indicates Artifact and "false" indicates POST.
502          */
503         private static boolean useArtifactProfile(EntityDescriptor descriptor, String acceptanceURL,
504                         RelyingParty relyingParty) {
505
506                 boolean artifactMeta = false;
507                 boolean postMeta = false;
508
509                 // Look at the metadata bindings, if we can find them
510                 if (descriptor != null) {
511                         SPSSODescriptor sp = descriptor.getSPSSODescriptor(org.opensaml.XML.SAML11_PROTOCOL_ENUM);
512
513                         if (sp != null) {
514
515                                 // See if this is the default endpoint location.
516                                 Endpoint defaultEndpoint = sp.getAssertionConsumerServiceManager().getDefaultEndpoint();
517                                 if (defaultEndpoint.getLocation().equals(acceptanceURL)) {
518
519                                         // If we recognize the default binding, this is the one to use.
520                                         if (defaultEndpoint.getBinding().equals(SAMLBrowserProfile.PROFILE_POST_URI)) return false;
521                                         else if (defaultEndpoint.getBinding().equals(SAMLBrowserProfile.PROFILE_ARTIFACT_URI)) return true;
522                                 }
523
524                                 Iterator endpoints = sp.getAssertionConsumerServiceManager().getEndpoints();
525                                 while (endpoints.hasNext()) {
526                                         Endpoint ep = (Endpoint) endpoints.next();
527                                         if (acceptanceURL.equals(ep.getLocation())
528                                                         && SAMLBrowserProfile.PROFILE_POST_URI.equals(ep.getBinding())) {
529                                                 log.debug("Metadata indicates support for POST profile.");
530                                                 postMeta = true;
531                                                 continue;
532                                         }
533                                 }
534                                 endpoints = sp.getAssertionConsumerServiceManager().getEndpoints();
535                                 while (endpoints.hasNext()) {
536                                         Endpoint ep = (Endpoint) endpoints.next();
537                                         if (acceptanceURL.equals(ep.getLocation())
538                                                         && SAMLBrowserProfile.PROFILE_ARTIFACT_URI.equals(ep.getBinding())) {
539                                                 log.debug("Metadata indicates support for Artifact profile.");
540                                                 artifactMeta = true;
541                                                 continue;
542                                         }
543                                 }
544                         }
545                 }
546
547                 // If we have metadata for both, use the relying party default
548                 if (!(artifactMeta && postMeta)) {
549
550                         // If we only have metadata for one, use it
551                         if (artifactMeta) { return true; }
552                         if (postMeta) { return false; }
553
554                 }
555
556                 // If we have missing or incomplete metadata, use relying party default
557                 if (relyingParty.defaultToPOSTProfile()) {
558                         return false;
559                 } else {
560                         return true;
561                 }
562         }
563
564         /**
565          * Boolean indication of whether an assertion containing an attribute statement should be bundled in the response
566          * with the assertion containing the AuthN statement.
567          */
568         private static boolean pushAttributes(boolean artifactProfile, RelyingParty relyingParty) {
569
570                 // By default push for Artifact and don't push for POST
571                 // This can be overriden at the level of the relying party
572                 if (relyingParty.forceAttributePush()) {
573                         return true;
574                 } else if (relyingParty.forceAttributeNoPush()) {
575                         return false;
576                 } else if (artifactProfile) {
577                         return true;
578                 } else {
579                         return false;
580                 }
581         }
582
583         /**
584          * Boolean indication of whethere or not a given assertion consumer URL is valid for a given SP.
585          */
586         private static boolean isValidAssertionConsumerURL(EntityDescriptor descriptor, String shireURL)
587                         throws InvalidClientDataException {
588
589                 SPSSODescriptor sp = descriptor.getSPSSODescriptor(org.opensaml.XML.SAML11_PROTOCOL_ENUM);
590                 if (sp == null) {
591                         log.info("Inappropriate metadata for provider.");
592                         return false;
593                 }
594
595                 Iterator endpoints = sp.getAssertionConsumerServiceManager().getEndpoints();
596                 while (endpoints.hasNext()) {
597                         if (shireURL.equals(((Endpoint) endpoints.next()).getLocation())) { return true; }
598                 }
599                 log.info("Supplied consumer URL not found in metadata.");
600                 return false;
601         }
602 }