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