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