2 * Copyright [2006] [University Corporation for Advanced Internet Development, Inc.]
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package edu.internet2.middleware.shibboleth.idp.profile.saml2;
19 import java.io.IOException;
20 import java.io.InputStream;
21 import java.net.MalformedURLException;
23 import java.net.URLConnection;
24 import java.util.LinkedList;
25 import java.util.List;
27 import javax.servlet.RequestDispatcher;
28 import javax.servlet.ServletResponse;
29 import javax.servlet.http.HttpServletRequest;
30 import javax.servlet.http.HttpServletResponse;
31 import javax.servlet.http.HttpSession;
33 import org.apache.log4j.Logger;
34 import org.joda.time.DateTime;
35 import org.opensaml.Configuration;
36 import org.opensaml.common.SAMLObjectBuilder;
37 import org.opensaml.common.binding.BindingException;
38 import org.opensaml.common.binding.encoding.MessageEncoder;
39 import org.opensaml.saml2.core.Assertion;
40 import org.opensaml.saml2.core.AuthnContext;
41 import org.opensaml.saml2.core.AuthnContextClassRef;
42 import org.opensaml.saml2.core.AuthnContextDeclRef;
43 import org.opensaml.saml2.core.AuthnRequest;
44 import org.opensaml.saml2.core.AuthnStatement;
45 import org.opensaml.saml2.core.GetComplete;
46 import org.opensaml.saml2.core.IDPEntry;
47 import org.opensaml.saml2.core.IDPList;
48 import org.opensaml.saml2.core.Issuer;
49 import org.opensaml.saml2.core.RequestedAuthnContext;
50 import org.opensaml.saml2.core.Response;
51 import org.opensaml.saml2.core.Scoping;
52 import org.opensaml.saml2.core.Status;
53 import org.opensaml.saml2.core.StatusCode;
54 import org.opensaml.saml2.core.Subject;
55 import org.opensaml.saml2.core.SubjectConfirmation;
56 import org.opensaml.saml2.metadata.AssertionConsumerService;
57 import org.opensaml.saml2.metadata.SPSSODescriptor;
58 import org.opensaml.saml2.metadata.provider.MetadataProvider;
59 import org.opensaml.saml2.metadata.provider.MetadataProviderException;
60 import org.opensaml.xml.io.Unmarshaller;
61 import org.opensaml.xml.io.UnmarshallingException;
62 import org.opensaml.xml.parse.BasicParserPool;
63 import org.opensaml.xml.parse.ParserPool;
64 import org.opensaml.xml.parse.XMLParserException;
65 import org.w3c.dom.Document;
66 import org.w3c.dom.Element;
69 import edu.internet2.middleware.shibboleth.common.profile.ProfileException;
70 import edu.internet2.middleware.shibboleth.common.profile.ProfileResponse;
71 import edu.internet2.middleware.shibboleth.common.relyingparty.RelyingPartyConfiguration;
72 import edu.internet2.middleware.shibboleth.common.relyingparty.saml2.AbstractSAML2ProfileConfiguration;
73 import edu.internet2.middleware.shibboleth.common.relyingparty.saml2.SSOConfiguration;
74 import edu.internet2.middleware.shibboleth.idp.authn.AuthenticationManager;
75 import edu.internet2.middleware.shibboleth.idp.authn.LoginContext;
76 import edu.internet2.middleware.shibboleth.idp.authn.Saml2LoginContext;
77 import javax.servlet.ServletException;
81 * Abstract SAML 2.0 Authentication Request profile handler.
83 public abstract class AbstractAuthenticationRequest extends AbstractSAML2ProfileHandler {
86 private static final Logger log = Logger.getLogger(AbstractAuthenticationRequest.class);
88 /** Key in an HttpSession for the AssertionConsumerService object. */
89 protected static final String ACS_SESSION_KEY = "AssertionConsumerService";
91 /** Key in an HttpSession for the RelyingParty. */
92 protected static final String RPCONFIG_SESSION_KEY = "RelyingPartyConfiguration";
94 /** Key in an HttpSession for the SSOConfiguration. */
95 protected static final String SSOCONFIG_SESSION_KEY = "SSOConfiguration";
97 /** Key in an HttpSession for the SPSSODescriptor. */
98 protected static final String SPSSODESC_SESSION_KEY = "SPSSODescriptor";
100 /** Key in an HttpSession for the AuthnRequest. */
101 protected static final String AUTHNREQUEST_SESSION_KEY = "AuthnRequest";
103 /** Key in an HttpSession for the Issuer. */
104 protected static final String ISSUER_SESSION_KEY = "Issuer";
107 /** The path to the IdP's AuthenticationManager servlet */
108 protected String authnMgrURL;
110 /** AuthenticationManager to be used */
111 protected AuthenticationManager authnMgr;
113 /** A pool of XML parsers. */
114 protected ParserPool parserPool;
116 /** Builder for AuthnStatements. */
117 protected SAMLObjectBuilder<AuthnStatement> authnStatementBuilder;
119 /** Builder for AuthnContexts. */
120 protected SAMLObjectBuilder<AuthnContext> authnContextBuilder;
122 /** Builder for AuthnContextDeclRef's */
123 protected SAMLObjectBuilder<AuthnContextDeclRef> authnContextDeclRefBuilder;
125 /** Builder for AuthnContextClassRef's. */
126 protected SAMLObjectBuilder<AuthnContextClassRef> authnContextClassRefBuilder;
131 public AbstractAuthenticationRequest() {
133 parserPool = new BasicParserPool();
134 authnStatementBuilder = (SAMLObjectBuilder<AuthnStatement>) getBuilderFactory().getBuilder(AuthnStatement.DEFAULT_ELEMENT_NAME);
135 authnContextBuilder = (SAMLObjectBuilder<AuthnContext>) getBuilderFactory().getBuilder(AuthnContext.DEFAULT_ELEMENT_NAME);
136 authnContextDeclRefBuilder = (SAMLObjectBuilder<AuthnContextDeclRef>) getBuilderFactory().getBuilder(AuthnContextDeclRef.DEFAULT_ELEMENT_NAME);
137 authnContextClassRefBuilder = (SAMLObjectBuilder<AuthnContextClassRef>) getBuilderFactory().getBuilder(AuthnContextClassRef.DEFAULT_ELEMENT_NAME);
141 * Set the Authentication Mananger.
143 * @param authnManager
144 * The IdP's AuthenticationManager.
146 public void setAuthenticationManager(AuthenticationManager authnManager) {
147 this.authnMgr = authnMgr;
151 * Evaluate a SAML 2 AuthenticationRequest message.
153 * @param authnRequest
154 * A SAML 2 AuthenticationRequest
156 * The issuer of the authnRequest.
158 * The HttpSession of the request.
159 * @param relyingParty
160 * The RelyingPartyConfiguration for the request.
162 * The SSOConfiguration for the request.
163 * @param spDescriptor
164 * The SPSSODescriptor for the request.
166 * @return A Response containing a failure message or a AuthenticationStmt.
168 * @throws ProfileException
171 protected Response evaluateRequest(final AuthnRequest authnRequest,
172 final Issuer issuer, final HttpSession session,
173 final RelyingPartyConfiguration relyingParty,
174 final SSOConfiguration ssoConfig, final SPSSODescriptor spDescriptor)
175 throws ProfileException {
177 Response samlResponse;
179 // check if the authentication was successful.
180 Saml2LoginContext loginCtx = getLoginContext(session);
181 if (!loginCtx.getAuthenticationOK()) {
182 // if authentication failed, send the appropriate SAML error message.
183 String failureMessage = loginCtx.getAuthenticationFailureMessage();
184 Status failureStatus = buildStatus(StatusCode.RESPONDER_URI, StatusCode.AUTHN_FAILED_URI, failureMessage);
185 samlResponse = buildResponse(authnRequest.getID(), new DateTime(), relyingParty.getProviderId(),
191 // the user successfully authenticated.
192 // build an authentication assertion.
193 samlResponse = buildResponse(authnRequest.getID(), new DateTime(),
194 relyingParty.getProviderId(), buildStatus(StatusCode.SUCCESS_URI, null, null));
196 DateTime now = new DateTime();
197 Assertion assertion = buildAssertion(now, relyingParty,
198 (AbstractSAML2ProfileConfiguration) relyingParty.getProfileConfigurations().get(SSOConfiguration.PROFILE_ID));
199 assertion.setSubject(authnRequest.getSubject());
200 setAuthenticationStatement(assertion, loginCtx, authnRequest);
202 samlResponse.getAssertions().add(assertion);
204 // retrieve the AssertionConsumerService endpoint (we parsed it in
205 // verifyAuthnRequest()
206 AssertionConsumerService acsEndpoint = getACSEndpointFromSession(session);
214 * Build a SAML 2 Response element with basic fields populated.
216 * Failure handlers can send the returned response element to the RP.
217 * Success handlers should add the assertions before sending it.
219 * @param inResponseTo
220 * The ID of the request this is in response to.
221 * @param issueInstant
222 * The timestamp of this response.
224 * The URI of the RP issuing the response.
226 * The response's status code.
228 * @return The populated Response object.
230 protected Response buildResponse(String inResponseTo,
231 final DateTime issueInstant, String issuer, final Status status) {
233 Response response = getResponseBuilder().buildObject();
235 Issuer i = getIssuerBuilder().buildObject();
238 response.setVersion(SAML_VERSION);
239 response.setID(getIdGenerator().generateIdentifier());
240 response.setInResponseTo(inResponseTo);
241 response.setIssueInstant(issueInstant);
242 response.setIssuer(i);
243 response.setStatus(status);
249 * Check if the user has already been authenticated.
252 * the user's HttpSession.
254 * @return <code>true</code> if the user has been authenticated. otherwise
257 protected boolean hasUserAuthenticated(final HttpSession httpSession) {
259 // if the user has authenticated, their session will have a LoginContext
261 Object o = httpSession.getAttribute(LoginContext.LOGIN_CONTEXT_KEY);
262 return (o != null && o instanceof LoginContext);
266 * Store a user's AuthnRequest and Issuer in the session.
268 * @param authnRequest
269 * A SAML 2 AuthnRequest.
271 * The issuer of the AuthnRequest.
273 * The HttpSession in which the data should be stored.
274 * @param relyingParty
275 * The RelyingPartyConfiguration for the issuer.
277 * The SSOConfiguration for the relyingParty
278 * @param spDescriptor
279 * The SPSSODescriptor for the ssoConfig.
281 protected void storeRequestData(final HttpSession session,
282 final AuthnRequest authnRequest, final Issuer issuer,
283 final RelyingPartyConfiguration relyingParty,
284 final SSOConfiguration ssoConfig, final SPSSODescriptor spDescriptor) {
286 if (session == null) {
290 session.setAttribute(AUTHNREQUEST_SESSION_KEY, authnRequest);
291 session.setAttribute(ISSUER_SESSION_KEY, issuer);
292 session.setAttribute(RPCONFIG_SESSION_KEY, relyingParty);
293 session.setAttribute(SSOCONFIG_SESSION_KEY, ssoConfig);
294 session.setAttribute(SPSSODESC_SESSION_KEY, spDescriptor);
298 * Retrieve the AuthnRequest and Issuer from a session.
301 * The HttpSession in which the data was stored.
302 * @param authnRequest
303 * Will be populated with the AuthnRequest.
305 * Will be populated with the ssuer of the AuthnRequest.
306 * @param relyingParty
307 * Will be populated with the RelyingPartyConfiguration for the
310 * Will be populated with the SSOConfiguration for the
312 * @param spDescriptor
313 * Will be populated with the SPSSODescriptor for the ssoConfig.
315 protected void retrieveRequestData(final HttpSession session,
316 AuthnRequest authnRequest, Issuer issuer,
317 RelyingPartyConfiguration relyingParty, SSOConfiguration ssoConfig,
318 SPSSODescriptor spDescriptor) {
320 if (session == null) {
325 authnRequest = (AuthnRequest) session
326 .getAttribute(AUTHNREQUEST_SESSION_KEY);
327 issuer = (Issuer) session.getAttribute(ISSUER_SESSION_KEY);
328 relyingParty = (RelyingPartyConfiguration) session
329 .getAttribute(RPCONFIG_SESSION_KEY);
330 ssoConfig = (SSOConfiguration) session
331 .getAttribute(SSOCONFIG_SESSION_KEY);
332 spDescriptor = (SPSSODescriptor) session
333 .getAttribute(SPSSODESC_SESSION_KEY);
335 session.removeAttribute(AUTHNREQUEST_SESSION_KEY);
336 session.removeAttribute(ISSUER_SESSION_KEY);
337 session.removeAttribute(RPCONFIG_SESSION_KEY);
338 session.removeAttribute(SSOCONFIG_SESSION_KEY);
339 session.removeAttribute(SPSSODESC_SESSION_KEY);
343 * Check if the user has already been authenticated. If so, return the
344 * LoginContext. If not, redirect the user to the AuthenticationManager.
346 * @param authnRequest
347 * The SAML 2 AuthnRequest.
349 * The user's HttpSession.
351 * The user's HttpServletRequest.
353 * The user's HttpServletResponse.
355 * @throws ProfileException
358 protected void authenticateUser(final AuthnRequest authnRequest,
359 final HttpSession httpSession, final HttpServletRequest request,
360 final HttpServletResponse response) throws ProfileException {
362 // Forward the request to the AuthenticationManager.
363 // When the AuthenticationManager is done it will
364 // forward the request back to this servlet.
366 Saml2LoginContext loginCtx = new Saml2LoginContext(authnRequest);
367 loginCtx.setProfileHandlerURL(request.getPathInfo());
368 httpSession.setAttribute(LoginContext.LOGIN_CONTEXT_KEY, loginCtx);
370 RequestDispatcher dispatcher = request
371 .getRequestDispatcher(authnMgrURL);
372 dispatcher.forward(request, response);
373 } catch (IOException ex) {
374 log.error("Error forwarding SAML 2 AuthnRequest "
375 + authnRequest.getID() + " to AuthenticationManager", ex);
376 throw new ProfileException("Error forwarding SAML 2 AuthnRequest "
377 + authnRequest.getID() + " to AuthenticationManager", ex);
378 } catch (ServletException ex) {
379 log.error("Error forwarding SAML 2 AuthnRequest "
380 + authnRequest.getID() + " to AuthenticationManager", ex);
381 throw new ProfileException("Error forwarding SAML 2 AuthnRequest "
382 + authnRequest.getID() + " to AuthenticationManager", ex);
387 * Build an AuthnStatement and add it to an Assertion.
389 * @param assertion An empty SAML 2 Assertion object.
390 * @param loginContext The processed login context for the AuthnRequest.
391 * @param authnRequest The AuthnRequest to which this is in response.
393 * @throws ProfileException On error.
395 protected void setAuthenticationStatement(Assertion assertion,
396 final Saml2LoginContext loginContext,
397 final AuthnRequest authnRequest) throws ProfileException {
399 // Build the AuthnCtx.
400 // We need to determine if the user was authenticated
401 // with an AuthnContextClassRef or a AuthnContextDeclRef
402 AuthnContext authnCtx = buildAuthnCtx(authnRequest.getRequestedAuthnContext(), loginContext);
403 if (authnCtx == null) {
404 log.error("Error respond to SAML 2 AuthnRequest "
405 + authnRequest.getID()
406 + " : Unable to determine authentication method");
409 AuthnStatement stmt = authnStatementBuilder.buildObject();
410 stmt.setAuthnInstant(loginContext.getAuthenticationInstant());
411 stmt.setAuthnContext(authnCtx);
413 // add the AuthnStatement to the Assertion
414 List<AuthnStatement> authnStatements = assertion.getAuthnStatements();
415 authnStatements.add(stmt);
419 * Create the AuthnContex object.
421 * To do this, we have to walk the AuthnRequest's RequestedAuthnContext
422 * object and compare any values we find to what's set in the loginContext.
424 * @param requestedAuthnCtx
425 * The RequestedAuthnContext from the Authentication Request.
426 * @param loginContext
427 * The processed LoginContext (it must contain the authn method).
429 * @return An AuthnCtx object on success or <code>null</code> on failure.
431 protected AuthnContext buildAuthnCtx(
432 final RequestedAuthnContext requestedAuthnCtx,
433 final Saml2LoginContext loginContext) {
435 // this method assumes that only one URI will match.
437 AuthnContext authnCtx = authnContextBuilder.buildObject();
438 String authnMethod = loginContext.getAuthenticationMethod();
440 List<AuthnContextClassRef> authnClasses = requestedAuthnCtx
441 .getAuthnContextClassRefs();
442 List<AuthnContextDeclRef> authnDeclRefs = requestedAuthnCtx
443 .getAuthnContextDeclRefs();
445 if (authnClasses != null) {
446 for (AuthnContextClassRef classRef : authnClasses) {
447 if (classRef != null) {
448 String s = classRef.getAuthnContextClassRef();
449 if (s != null && authnMethod.equals(s)) {
450 AuthnContextClassRef ref = authnContextClassRefBuilder
452 authnCtx.setAuthnContextClassRef(ref);
459 // if no AuthnContextClassRef's matched, try the DeclRefs
460 if (authnDeclRefs != null) {
461 for (AuthnContextDeclRef declRef : authnDeclRefs) {
462 if (declRef != null) {
463 String s = declRef.getAuthnContextDeclRef();
464 if (s != null && authnMethod.equals((s))) {
465 AuthnContextDeclRef ref = authnContextDeclRefBuilder
467 authnCtx.setAuthnContextDeclRef(ref);
474 // no matches were found.
479 * Get the user's LoginContext.
482 * The user's HttpSession.
484 * @return The user's LoginContext.
486 * @throws ProfileException
489 protected Saml2LoginContext getLoginContext(final HttpSession httpSession)
490 throws ProfileException {
492 Object o = httpSession.getAttribute(LoginContext.LOGIN_CONTEXT_KEY);
494 log.error("User's session does not contain a LoginContext object.");
495 throw new ProfileException(
496 "User's session does not contain a LoginContext object.");
499 if (!(o instanceof Saml2LoginContext)) {
501 .error("Invalid login context object -- object is not an instance of Saml2LoginContext.");
502 throw new ProfileException("Invalid login context object.");
505 Saml2LoginContext ctx = (Saml2LoginContext) o;
507 httpSession.removeAttribute(LoginContext.LOGIN_CONTEXT_KEY);
513 * Verify the AuthnRequest is well-formed.
515 * @param authnRequest
516 * The user's SAML 2 AuthnRequest.
518 * The Issuer of the AuthnRequest.
519 * @param relyingParty
520 * The relying party configuration for the request's originator.
522 * The user's HttpSession.
524 * @throws AuthenticationRequestException
527 protected void verifyAuthnRequest(final AuthnRequest authnRequest,
528 Issuer issuer, final RelyingPartyConfiguration relyingParty,
529 final HttpSession session) throws AuthenticationRequestException {
531 Status failureStatus;
533 // Check if we are in scope to handle this AuthnRequest
534 checkScope(authnRequest, issuer.getSPProvidedID());
536 // XXX: run signature checks on authnRequest
538 // verify that the AssertionConsumerService url is valid.
539 AssertionConsumerService acsEndpoint = getAndVerifyACSEndpoint(
540 authnRequest, relyingParty.getRelyingPartyId(),
541 getRelyingPartyConfigurationManager().getMetadataProvider());
542 session.setAttribute(ACS_SESSION_KEY, acsEndpoint);
544 // check for nameID constraints.
545 Subject subject = getAndVerifySubject(authnRequest);
549 * Get and verify the Subject element.
551 * @param authnRequest
552 * The SAML 2 AuthnRequest.
554 * @return A Subject element.
556 * @throws AuthenticationRequestException
559 protected Subject getAndVerifySubject(final AuthnRequest authnRequest)
560 throws AuthenticationRequestException {
562 Status failureStatus;
564 Subject subject = authnRequest.getSubject();
566 if (subject == null) {
567 failureStatus = buildStatus(StatusCode.REQUESTER_URI, null,
568 "SAML 2 AuthnRequest " + authnRequest.getID()
569 + " is malformed: It does not contain a Subject.");
570 throw new AuthenticationRequestException(
571 "AuthnRequest lacks a Subject", failureStatus);
574 // The Web Browser SSO profile disallows SubjectConfirmation
575 // methods in the requested subject.
576 List<SubjectConfirmation> confMethods = subject
577 .getSubjectConfirmations();
578 if (confMethods != null || confMethods.size() > 0) {
580 .error("SAML 2 AuthnRequest "
581 + authnRequest.getID()
582 + " is malformed: It contains SubjectConfirmation elements.");
583 failureStatus = buildStatus(
584 StatusCode.REQUESTER_URI,
586 "SAML 2 AuthnRequest "
587 + authnRequest.getID()
588 + " is malformed: It contains SubjectConfirmation elements.");
589 throw new AuthenticationRequestException(
590 "AuthnRequest contains SubjectConfirmation elements",
598 * Return the endpoint URL and protocol binding to use for the AuthnRequest.
600 * @param authnRequest
601 * The SAML 2 AuthnRequest.
603 * The SP's providerId.
605 * The appropriate Metadata.
607 * @return The AssertionConsumerService for the endpoint, or
608 * <code>null</code> on error.
610 * @throws AuthenticationRequestException
613 protected AssertionConsumerService getAndVerifyACSEndpoint(
614 final AuthnRequest authnRequest, String providerId,
615 final MetadataProvider metadata)
616 throws AuthenticationRequestException {
618 Status failureStatus;
620 // Either the AssertionConsumerServiceIndex must be present
621 // or AssertionConsumerServiceURL must be present.
623 Integer idx = authnRequest.getAssertionConsumerServiceIndex();
624 String acsURL = authnRequest.getAssertionConsumerServiceURL();
626 if (idx != null && acsURL != null) {
628 .error("SAML 2 AuthnRequest "
629 + authnRequest.getID()
630 + " is malformed: It contains both an AssertionConsumerServiceIndex and an AssertionConsumerServiceURL");
631 failureStatus = buildStatus(
632 StatusCode.REQUESTER_URI,
634 "SAML 2 AuthnRequest "
635 + authnRequest.getID()
636 + " is malformed: It contains both an AssertionConsumerServiceIndex and an AssertionConsumerServiceURL");
637 throw new AuthenticationRequestException("Malformed AuthnRequest",
641 SPSSODescriptor spDescriptor;
643 spDescriptor = metadata.getEntityDescriptor(providerId)
644 .getSPSSODescriptor(SAML20_PROTOCOL_URI);
645 } catch (MetadataProviderException ex) {
647 "Unable retrieve SPSSODescriptor metadata for providerId "
649 + " while processing SAML 2 AuthnRequest "
650 + authnRequest.getID(), ex);
651 failureStatus = buildStatus(StatusCode.RESPONDER_URI, null,
652 "Unable to locate metadata for " + providerId);
653 throw new AuthenticationRequestException(
654 "Unable to locate metadata", ex, failureStatus);
657 List<AssertionConsumerService> acsList = spDescriptor
658 .getAssertionConsumerServices();
660 // if the ACS index is specified, retrieve it from the metadata
663 int i = idx.intValue();
665 // if the index is out of range, return an appropriate error.
666 if (i > acsList.size()) {
667 log.error("Illegal AssertionConsumerIndex specicifed (" + i
668 + ") in SAML 2 AuthnRequest " + authnRequest.getID());
670 failureStatus = buildStatus(StatusCode.REQUESTER_URI, null,
671 "Illegal AssertionConsumerIndex specicifed (" + i
672 + ") in SAML 2 AuthnRequest "
673 + authnRequest.getID());
675 throw new AuthenticationRequestException(
676 "Illegal AssertionConsumerIndex in AuthnRequest",
680 return acsList.get(i);
683 // if the ACS endpoint is specified, validate it against the metadata
684 String protocolBinding = authnRequest.getProtocolBinding();
685 for (AssertionConsumerService acs : acsList) {
686 if (acsURL.equals(acs.getLocation())) {
687 if (protocolBinding != null) {
688 if (protocolBinding.equals(acs.getBinding())) {
696 .error("Error processing SAML 2 AuthnRequest message "
697 + authnRequest.getID()
698 + ": Unable to validate AssertionConsumerServiceURL against metadata: "
699 + acsURL + " for binding " + protocolBinding);
701 failureStatus = buildStatus(StatusCode.REQUESTER_URI, null,
702 "Unable to validate AssertionConsumerService against metadata.");
704 throw new AuthenticationRequestException(
705 "SAML 2 AuthenticationRequest: Unable to validate AssertionConsumerService against Metadata",
710 * Retrieve a parsed AssertionConsumerService endpoint from the user's
714 * The user's HttpSession.
716 * @return An AssertionConsumerServiceEndpoint object.
718 * @throws ProfileException
721 protected AssertionConsumerService getACSEndpointFromSession(
722 final HttpSession session) throws ProfileException {
724 Object o = session.getAttribute(ACS_SESSION_KEY);
727 .error("User's session does not contain an AssertionConsumerService object.");
728 throw new ProfileException(
729 "User's session does not contain an AssertionConsumerService object.");
732 if (!(o instanceof AssertionConsumerService)) {
734 .error("Invalid session data -- object is not an instance of AssertionConsumerService.");
735 throw new ProfileException(
736 "Invalid session data -- object is not an instance of AssertionConsumerService.");
739 AssertionConsumerService endpoint = (AssertionConsumerService) o;
741 session.removeAttribute(ACS_SESSION_KEY);
747 * Check if an {@link AuthnRequest} contains a {@link Scoping} element. If
748 * so, check if the specified IdP is in the {@link IDPList} element. If no
749 * Scoping element is present, this method returns <code>true</code>.
751 * @param authnRequest
752 * The {@link AuthnRequest} element to check.
754 * The IdP's ProviderID.
756 * @throws AuthenticationRequestException
759 protected void checkScope(final AuthnRequest authnRequest, String providerId)
760 throws AuthenticationRequestException {
762 Status failureStatus;
764 List<String> idpEntries = new LinkedList<String>();
766 Scoping scoping = authnRequest.getScoping();
767 if (scoping == null) {
771 // process all of the explicitly listed idp provider ids
772 IDPList idpList = scoping.getIDPList();
773 if (idpList == null) {
777 List<IDPEntry> explicitIDPEntries = idpList.getIDPEntrys();
778 if (explicitIDPEntries != null) {
779 for (IDPEntry entry : explicitIDPEntries) {
780 String s = entry.getProviderID();
787 // If the IDPList is incomplete, retrieve the complete list
788 // and add the entries to idpEntries.
789 GetComplete getComplete = idpList.getGetComplete();
790 IDPList referencedIdPs = getCompleteIDPList(getComplete);
791 if (referencedIdPs != null) {
792 List<IDPEntry> referencedIDPEntries = referencedIdPs.getIDPEntrys();
793 if (referencedIDPEntries != null) {
794 for (IDPEntry entry : referencedIDPEntries) {
795 String s = entry.getProviderID();
803 // iterate over all the IDPEntries we've gathered,
804 // and check if we're in scope.
805 for (String requestProviderId : idpEntries) {
806 if (providerId.equals(requestProviderId)) {
807 log.debug("Found Scoping match for IdP: (" + providerId + ")");
812 log.error("SAML 2 AuthnRequest " + authnRequest.getID()
813 + " contains a Scoping element which "
814 + "does not contain a providerID registered with this IdP.");
816 failureStatus = buildStatus(StatusCode.RESPONDER_URI,
817 StatusCode.NO_SUPPORTED_IDP_URI, null);
818 throw new AuthenticationRequestException(
819 "Unrecognized providerID in Scoping element", failureStatus);
823 * Retrieve an incomplete IDPlist.
825 * This only handles URL-based <GetComplete/> references.
828 * The (possibly <code>null</code>) <GetComplete/>
831 * @return an {@link IDPList} or <code>null</code> if the uri can't be
834 protected IDPList getCompleteIDPList(final GetComplete getComplete) {
836 // XXX: enhance this method to cache the url and last-modified-header
838 if (getComplete == null) {
842 String uri = getComplete.getGetComplete();
847 IDPList idpList = null;
848 InputStream istream = null;
851 URL url = new URL(uri);
852 URLConnection conn = url.openConnection();
853 istream = conn.getInputStream();
855 // convert the raw data into an XML object
856 Document doc = parserPool.parse(istream);
857 Element docElement = doc.getDocumentElement();
858 Unmarshaller unmarshaller = Configuration.getUnmarshallerFactory()
859 .getUnmarshaller(docElement);
860 idpList = (IDPList) unmarshaller.unmarshall(docElement);
862 } catch (MalformedURLException ex) {
864 "Unable to retrieve GetComplete IDPList. Unsupported URI: "
866 } catch (IOException ex) {
867 log.error("IO Error while retreieving GetComplete IDPList from "
869 } catch (XMLParserException ex) {
871 "Internal OpenSAML error while parsing GetComplete IDPList from "
873 } catch (UnmarshallingException ex) {
875 "Internal OpenSAML error while unmarshalling GetComplete IDPList from "
879 if (istream != null) {
882 } catch (IOException ex) {
891 protected void encodeResponse(String binding, final ProfileResponse<ServletResponse> response,
892 final Response samlResponse,
893 final RelyingPartyConfiguration relyingParty,
894 final SSOConfiguration ssoConfig, final SPSSODescriptor spDescriptor) throws ProfileException {
896 MessageEncoder<ServletResponse> encoder = getMessageEncoderFactory().getMessageEncoder(binding);
897 if (encoder == null) {
898 log.error("SAML 2 Authentication Request: No MessageEncoder registered for " + binding);
899 throw new ProfileException("SAML 2 Authentication Request: No MessageEncoder registered for " + binding);
902 encoder.setResponse(response.getRawResponse());
903 encoder.setIssuer(relyingParty.getProviderId());
904 encoder.setMetadataProvider(getRelyingPartyConfigurationManager().getMetadataProvider());
905 encoder.setRelyingPartyRole(spDescriptor);
906 encoder.setSigningCredential(relyingParty.getDefaultSigningCredential());
907 encoder.setSamlMessage(samlResponse);
908 encoder.setRelyingPartyEndpoint(spDescriptor.getDefaultAssertionConsumerService());
912 } catch (BindingException ex) {
913 log.error("Unable to encode response the relying party: " + relyingParty.getRelyingPartyId(), ex);
914 throw new ProfileException("Unable to encode response the relying party: "
915 + relyingParty.getRelyingPartyId(), ex);