Removed unused XML call.
[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.List;
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.artifact.Artifact;
54 import org.w3c.dom.Document;
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                         ArrayList assertions = new ArrayList();
173
174                         // Is this artifact or POST?
175                         boolean artifactProfile = useArtifactProfile(descriptor, acceptanceURL, relyingParty);
176
177                         // Package attributes for push, if necessary - don't attempt this for legacy providers (they don't support
178                         // it)
179                         if (!relyingParty.isLegacyProvider() && pushAttributes(artifactProfile, relyingParty)) {
180                                 log.info("Resolving attributes for push.");
181                                 SAMLAssertion attrAssertion = generateAttributeAssertion(support, principal, relyingParty,
182                                                 authNSubject, request);
183                                 if (attrAssertion != null) {
184                                         assertions.add(attrAssertion);
185                                 } else {
186                                         log.info("No attributes resolved.");
187                                 }
188                         }
189
190                         // SAML Artifact profile - don't even attempt this for legacy providers (they don't support it)
191                         if (!relyingParty.isLegacyProvider() && artifactProfile) {
192                                 respondWithArtifact(request, response, support, principal, relyingParty, descriptor, acceptanceURL,
193                                                 nameId, authenticationMethod, authNSubject, assertions);
194
195                                 // SAML POST profile
196                         } else {
197                                 respondWithPOST(request, response, support, principal, relyingParty, descriptor, acceptanceURL, nameId,
198                                                 authenticationMethod, authNSubject, assertions);
199                         }
200                 } catch (InvalidClientDataException e) {
201                         throw new SAMLException(SAMLException.RESPONDER, e.getMessage());
202                 }
203                 return null;
204         }
205
206         private void respondWithArtifact(HttpServletRequest request, HttpServletResponse response,
207                         IdPProtocolSupport support, LocalPrincipal principal, RelyingParty relyingParty,
208                         EntityDescriptor descriptor, String acceptanceURL, SAMLNameIdentifier nameId, String authenticationMethod,
209                         SAMLSubject authNSubject, List assertions) throws SAMLException, IOException, UnsupportedEncodingException {
210
211                 log.debug("Responding with Artifact profile.");
212
213                 authNSubject.addConfirmationMethod(SAMLSubject.CONF_ARTIFACT);
214                 assertions.add(generateAuthNAssertion(request, relyingParty, descriptor, nameId, authenticationMethod,
215                                 getAuthNTime(request), authNSubject));
216
217                 // Sign the assertions, if necessary
218                 boolean metaDataIndicatesSignAssertions = false;
219                 if (descriptor != null) {
220                         SPSSODescriptor sp = descriptor.getSPSSODescriptor(org.opensaml.XML.SAML11_PROTOCOL_ENUM);
221                         if (sp != null) {
222                                 if (sp.getWantAssertionsSigned()) {
223                                         metaDataIndicatesSignAssertions = true;
224                                 }
225                         }
226                 }
227                 if (relyingParty.wantsAssertionsSigned() || metaDataIndicatesSignAssertions) {
228                         support.signAssertions((SAMLAssertion[]) assertions.toArray(new SAMLAssertion[0]), relyingParty);
229                 }
230
231                 // Create artifacts for each assertion
232                 ArrayList artifacts = new ArrayList();
233                 for (int i = 0; i < assertions.size(); i++) {
234                         SAMLAssertion assertion = (SAMLAssertion) assertions.get(i);
235                         Artifact artifact = support.getArtifactMapper().generateArtifact(assertion, relyingParty);
236                         artifacts.add(artifact);
237
238                         // Put attributes names in the transaction log when it is set to DEBUG
239                         if (support.getTransactionLog().isDebugEnabled()) {
240                                 Iterator statements = assertion.getStatements();
241                                 while (statements.hasNext()) {
242                                         SAMLStatement statement = (SAMLStatement) statements.next();
243                                         if (statement instanceof SAMLAttributeStatement) {
244                                                 Iterator attributes = ((SAMLAttributeStatement) statement).getAttributes();
245                                                 StringBuffer attributeBuffer = new StringBuffer();
246                                                 while (attributes.hasNext()) {
247                                                         SAMLAttribute attribute = (SAMLAttribute) attributes.next();
248                                                         attributeBuffer.append("(" + attribute.getName() + ")");
249                                                         support.getTransactionLog().debug(
250                                                                         "Artifact (" + artifact.encode() + ") created with the following attributes: "
251                                                                                         + attributeBuffer.toString());
252                                                 }
253                                         }
254                                 }
255                         }
256                 }
257
258                 // Assemble the query string
259                 StringBuffer destination = new StringBuffer(acceptanceURL);
260                 destination.append("?TARGET=");
261                 destination.append(URLEncoder.encode(request.getParameter("target"), "UTF-8"));
262                 Iterator iterator = artifacts.iterator();
263                 StringBuffer artifactBuffer = new StringBuffer(); // Buffer for the transaction log
264
265                 // Construct the artifact query parameter
266                 while (iterator.hasNext()) {
267                         Artifact artifact = (Artifact) iterator.next();
268                         artifactBuffer.append("(" + artifact.encode() + ")");
269                         destination.append("&SAMLart=");
270                         destination.append(URLEncoder.encode(artifact.encode(), "UTF-8"));
271                 }
272
273                 log.debug("Redirecting to (" + destination.toString() + ").");
274                 response.sendRedirect(destination.toString()); // Redirect to the artifact receiver
275                 support.getTransactionLog().info(
276                                 "Assertion artifact(s) (" + artifactBuffer.toString() + ") issued to provider ("
277                                                 + relyingParty.getProviderId() + ") on behalf of principal (" + principal.getName()
278                                                 + "). Name Identifier: (" + nameId.getName() + "). Name Identifier Format: ("
279                                                 + nameId.getFormat() + ").");
280         }
281
282         private void respondWithPOST(HttpServletRequest request, HttpServletResponse response, IdPProtocolSupport support,
283                         LocalPrincipal principal, RelyingParty relyingParty, EntityDescriptor descriptor, String acceptanceURL,
284                         SAMLNameIdentifier nameId, String authenticationMethod, SAMLSubject authNSubject, List assertions)
285                         throws SAMLException, IOException, ServletException {
286
287                 log.debug("Responding with POST profile.");
288                 authNSubject.addConfirmationMethod(SAMLSubject.CONF_BEARER);
289                 assertions.add(generateAuthNAssertion(request, relyingParty, descriptor, nameId, authenticationMethod,
290                                 getAuthNTime(request), authNSubject));
291
292                 // Sign the assertions, if necessary
293                 boolean metaDataIndicatesSignAssertions = false;
294                 if (descriptor != null) {
295                         SPSSODescriptor sp = descriptor.getSPSSODescriptor(org.opensaml.XML.SAML11_PROTOCOL_ENUM);
296                         if (sp != null) {
297                                 if (sp.getWantAssertionsSigned()) {
298                                         metaDataIndicatesSignAssertions = true;
299                                 }
300                         }
301                 }
302                 if (relyingParty.wantsAssertionsSigned() || metaDataIndicatesSignAssertions) {
303                         support.signAssertions((SAMLAssertion[]) assertions.toArray(new SAMLAssertion[0]), relyingParty);
304                 }
305
306                 // Set attributes needed by form
307                 request.setAttribute("acceptanceURL", acceptanceURL);
308                 request.setAttribute("target", request.getParameter("target"));
309
310                 SAMLResponse samlResponse = new SAMLResponse(null, acceptanceURL, assertions, null);
311
312                 support.signResponse(samlResponse, relyingParty);
313
314                 createPOSTForm(request, response, samlResponse.toBase64());
315
316                 // Make transaction log entry
317                 if (relyingParty.isLegacyProvider()) {
318                         support.getTransactionLog().info(
319                                         "Authentication assertion issued to legacy provider (SHIRE: " + request.getParameter("shire")
320                                                         + ") on behalf of principal (" + principal.getName() + ") for resource ("
321                                                         + request.getParameter("target") + "). Name Identifier: (" + nameId.getName()
322                                                         + "). Name Identifier Format: (" + nameId.getFormat() + ").");
323
324                 } else {
325                         support.getTransactionLog().info(
326                                         "Authentication assertion issued to provider (" + relyingParty.getProviderId()
327                                                         + ") on behalf of principal (" + principal.getName() + "). Name Identifier: ("
328                                                         + nameId.getName() + "). Name Identifier Format: (" + nameId.getFormat() + ").");
329                 }
330         }
331
332         private SAMLAssertion generateAttributeAssertion(IdPProtocolSupport support, LocalPrincipal principal,
333                         RelyingParty relyingParty, SAMLSubject authNSubject, HttpServletRequest request) throws SAMLException {
334
335                 try {
336                         SAMLAttribute[] attributes = support.getReleaseAttributes(principal, relyingParty, relyingParty
337                                         .getProviderId(), null);
338                         log.info("Found " + attributes.length + " attribute(s) for " + principal.getName());
339
340                         // Bail if we didn't get any attributes
341                         if (attributes == null || attributes.length < 1) {
342                                 return null;
343
344                         } else {
345                                 // Reference requested subject
346                                 SAMLSubject attrSubject = (SAMLSubject) authNSubject.clone();
347
348                                 ArrayList audiences = new ArrayList();
349                                 if (relyingParty.getProviderId() != null) {
350                                         audiences.add(relyingParty.getProviderId());
351                                 }
352                                 if (relyingParty.getName() != null && !relyingParty.getName().equals(relyingParty.getProviderId())) {
353                                         audiences.add(relyingParty.getName());
354                                 }
355                                 String remoteProviderId = request.getParameter("providerId");
356                                 if (remoteProviderId != null && !remoteProviderId.equals("") && !audiences.contains(remoteProviderId)) {
357                                         audiences.add(remoteProviderId);
358                                 }
359
360                                 SAMLCondition condition = new SAMLAudienceRestrictionCondition(audiences);
361
362                                 // Put all attributes into an assertion
363                                 SAMLStatement statement = new SAMLAttributeStatement(attrSubject, Arrays.asList(attributes));
364
365                                 // Set assertion expiration to longest attribute expiration
366                                 long max = 0;
367                                 for (int i = 0; i < attributes.length; i++) {
368                                         if (max < attributes[i].getLifetime()) {
369                                                 max = attributes[i].getLifetime();
370                                         }
371                                 }
372                                 Date now = new Date();
373                                 Date then = new Date(now.getTime() + (max * 1000)); // max is in seconds
374
375                                 SAMLAssertion attrAssertion = new SAMLAssertion(relyingParty.getIdentityProvider().getProviderId(),
376                                                 now, then, Collections.singleton(condition), null, Collections.singleton(statement));
377
378                                 if (log.isDebugEnabled()) {
379                                         log.debug("Dumping generated Attribute Assertion:" + System.getProperty("line.separator")
380                                                         + attrAssertion.toString());
381                                 }
382                                 return attrAssertion;
383                         }
384                 } catch (AAException e) {
385                         log.error("An error was encountered while generating assertion for attribute push: " + e);
386                         throw new SAMLException(SAMLException.RESPONDER, "General error processing request.");
387                 } catch (CloneNotSupportedException e) {
388                         log.error("An error was encountered while generating assertion for attribute push: " + e);
389                         throw new SAMLException(SAMLException.RESPONDER, "General error processing request.");
390                 }
391         }
392
393         private SAMLAssertion generateAuthNAssertion(HttpServletRequest request, RelyingParty relyingParty,
394                         EntityDescriptor descriptor, SAMLNameIdentifier nameId, String authenticationMethod, Date authTime,
395                         SAMLSubject subject) throws SAMLException, IOException {
396
397                 // Determine the correct audiences
398                 ArrayList audiences = new ArrayList();
399                 if (relyingParty.getProviderId() != null) {
400                         audiences.add(relyingParty.getProviderId());
401                 }
402                 if (relyingParty.getName() != null && !relyingParty.getName().equals(relyingParty.getProviderId())) {
403                         audiences.add(relyingParty.getName());
404                 }
405                 String remoteProviderId = request.getParameter("providerId");
406                 if (remoteProviderId != null && !remoteProviderId.equals("") && !audiences.contains(remoteProviderId)) {
407                         audiences.add(remoteProviderId);
408                 }
409
410                 // Determine the correct issuer
411                 String issuer = null;
412                 if (relyingParty.isLegacyProvider()) {
413
414                         log.debug("Service Provider is running Shibboleth <= 1.1. Using old style issuer.");
415                         if (relyingParty.getIdentityProvider().getSigningCredential() == null
416                                         || relyingParty.getIdentityProvider().getSigningCredential().getX509Certificate() == null) { throw new SAMLException(
417                                         "Cannot serve legacy style assertions without an X509 certificate"); }
418                         issuer = getHostNameFromDN(relyingParty.getIdentityProvider().getSigningCredential().getX509Certificate()
419                                         .getSubjectX500Principal());
420                         if (issuer == null || issuer.equals("")) { throw new SAMLException(
421                                         "Error parsing certificate DN while determining legacy issuer name."); }
422
423                 } else {
424                         issuer = relyingParty.getIdentityProvider().getProviderId();
425                 }
426
427                 // For compatibility with pre-1.2 shibboleth targets, include a pointer to the AA
428                 ArrayList bindings = new ArrayList();
429                 if (relyingParty.isLegacyProvider()) {
430
431                         SAMLAuthorityBinding binding = new SAMLAuthorityBinding(SAMLBinding.SOAP, relyingParty.getAAUrl()
432                                         .toString(), new QName(org.opensaml.XML.SAMLP_NS, "AttributeQuery"));
433                         bindings.add(binding);
434                 }
435
436                 // Create the assertion
437                 Vector conditions = new Vector(1);
438                 if (audiences != null && audiences.size() > 0) conditions.add(new SAMLAudienceRestrictionCondition(audiences));
439
440                 SAMLStatement[] statements = {new SAMLAuthenticationStatement(subject, authenticationMethod, authTime, request
441                                 .getRemoteAddr(), null, bindings)};
442
443                 SAMLAssertion assertion = new SAMLAssertion(issuer, new Date(System.currentTimeMillis()), new Date(System
444                                 .currentTimeMillis() + 300000), conditions, null, Arrays.asList(statements));
445
446                 if (log.isDebugEnabled()) {
447                         log.debug("Dumping generated AuthN Assertion:" + System.getProperty("line.separator")
448                                         + assertion.toString());
449                 }
450
451                 return assertion;
452         }
453
454         /*
455          * @see edu.internet2.middleware.shibboleth.idp.IdPResponder.ProtocolHandler#getHandlerName()
456          */
457         public String getHandlerName() {
458
459                 return "Shibboleth v1.x SSO";
460         }
461
462         private void validateShibSpecificData(HttpServletRequest request) throws InvalidClientDataException {
463
464                 if (request.getParameter("target") == null || request.getParameter("target").equals("")) { throw new InvalidClientDataException(
465                                 "Invalid data from Service Provider: no target URL received."); }
466                 if ((request.getParameter("shire") == null) || (request.getParameter("shire").equals(""))) { throw new InvalidClientDataException(
467                                 "Invalid data from Service Provider: No acceptance URL received."); }
468         }
469
470         private static void createPOSTForm(HttpServletRequest req, HttpServletResponse res, byte[] buf) throws IOException,
471                         ServletException {
472
473                 // Hardcoded to ASCII to ensure Base64 encoding compatibility
474                 req.setAttribute("assertion", new String(buf, "ASCII"));
475
476                 if (log.isDebugEnabled()) {
477                         log.debug("Dumping generated SAML Response:" + System.getProperty("line.separator")
478                                         + new String(Base64.decode(buf)));
479                 }
480
481                 RequestDispatcher rd = req.getRequestDispatcher("/IdP.jsp");
482                 rd.forward(req, res);
483         }
484
485         /**
486          * Boolean indication of which browser profile is in effect. "true" indicates Artifact and "false" indicates POST.
487          */
488         private static boolean useArtifactProfile(EntityDescriptor descriptor, String acceptanceURL,
489                         RelyingParty relyingParty) {
490
491                 boolean artifactMeta = false;
492                 boolean postMeta = false;
493
494                 // Look at the metadata bindings, if we can find them
495                 if (descriptor != null) {
496                         SPSSODescriptor sp = descriptor.getSPSSODescriptor(org.opensaml.XML.SAML11_PROTOCOL_ENUM);
497
498                         if (sp != null) {
499
500                                 Iterator endpoints = sp.getAssertionConsumerServiceManager().getEndpoints();
501                                 while (endpoints.hasNext()) {
502                                         Endpoint ep = (Endpoint) endpoints.next();
503                                         if (acceptanceURL.equals(ep.getLocation())
504                                                         && SAMLBrowserProfile.PROFILE_POST_URI.equals(ep.getBinding())) {
505                                                 log.debug("Metadata indicates support for POST profile.");
506                                                 postMeta = true;
507                                                 continue;
508                                         }
509                                 }
510                                 endpoints = sp.getAssertionConsumerServiceManager().getEndpoints();
511                                 while (endpoints.hasNext()) {
512                                         Endpoint ep = (Endpoint) endpoints.next();
513                                         if (acceptanceURL.equals(ep.getLocation())
514                                                         && SAMLBrowserProfile.PROFILE_ARTIFACT_URI.equals(ep.getBinding())) {
515                                                 log.debug("Metadata indicates support for Artifact profile.");
516                                                 artifactMeta = true;
517                                                 continue;
518                                         }
519                                 }
520                         }
521                 }
522
523                 // If we have metadata for both, use the relying party default
524                 if (!(artifactMeta && postMeta)) {
525
526                         // If we only have metadata for one, use it
527                         if (artifactMeta) { return true; }
528                         if (postMeta) { return false; }
529
530                 }
531
532                 // If we have missing or incomplete metadata, use relying party default
533                 if (relyingParty.defaultToPOSTProfile()) {
534                         return false;
535                 } else {
536                         return true;
537                 }
538         }
539
540         /**
541          * Boolean indication of whether an assertion containing an attribute statement should be bundled in the response
542          * with the assertion containing the AuthN statement.
543          */
544         private static boolean pushAttributes(boolean artifactProfile, RelyingParty relyingParty) {
545
546                 // By default push for Artifact and don't push for POST
547                 // This can be overriden at the level of the relying party
548                 if (relyingParty.forceAttributePush()) {
549                         return true;
550                 } else if (relyingParty.forceAttributeNoPush()) {
551                         return false;
552                 } else if (artifactProfile) {
553                         return true;
554                 } else {
555                         return false;
556                 }
557         }
558
559         /**
560          * Boolean indication of whethere or not a given assertion consumer URL is valid for a given SP.
561          */
562         private static boolean isValidAssertionConsumerURL(EntityDescriptor descriptor, String shireURL)
563                         throws InvalidClientDataException {
564
565                 SPSSODescriptor sp = descriptor.getSPSSODescriptor(org.opensaml.XML.SAML11_PROTOCOL_ENUM);
566                 if (sp == null) {
567                         log.info("Inappropriate metadata for provider.");
568                         return false;
569                 }
570
571                 Iterator endpoints = sp.getAssertionConsumerServiceManager().getEndpoints();
572                 while (endpoints.hasNext()) {
573                         if (shireURL.equals(((Endpoint) endpoints.next()).getLocation())) { return true; }
574                 }
575                 log.info("Supplied consumer URL not found in metadata.");
576                 return false;
577         }
578 }