import edu.internet2.middleware.shibboleth.common.profile.ProfileResponse;
import edu.internet2.middleware.shibboleth.common.relyingparty.RelyingPartyConfiguration;
import edu.internet2.middleware.shibboleth.common.relyingparty.RelyingPartyConfigurationManager;
+import edu.internet2.middleware.shibboleth.common.relyingparty.saml2.AbstractSAML2ProfileConfiguration;
import edu.internet2.middleware.shibboleth.common.relyingparty.saml2.SSOConfiguration;
import edu.internet2.middleware.shibboleth.idp.authn.AuthenticationManager;
import edu.internet2.middleware.shibboleth.idp.authn.LoginContext;
import edu.internet2.middleware.shibboleth.idp.authn.Saml2LoginContext;
+
/**
* Abstract SAML 2.0 Authentication Request profile handler.
*/
-public abstract class AbstractAuthenticationRequest extends
- AbstractSAML2ProfileHandler {
-
- /** Class logger. */
- private static final Logger log = Logger
- .getLogger(AbstractAuthenticationRequest.class.getName());
-
- /** Key in an HttpSession for the AssertionConsumerService object. */
- protected static final String ACS_SESSION_KEY = "AssertionConsumerService";
-
- /** Key in an HttpSession for the RelyingParty. */
- protected static final String RPCONFIG_SESSION_KEY = "RelyingPartyConfiguration";
-
- /** Key in an HttpSession for the SSOConfiguration. */
- protected static final String SSOCONFIG_SESSION_KEY = "SSOConfiguration";
-
- /** Key in an HttpSession for the SPSSODescriptor. */
- protected static final String SPSSODESC_SESSION_KEY = "SPSSODescriptor";
-
- /** Key in an HttpSession for the AuthnRequest. */
- protected static final String AUTHNREQUEST_SESSION_KEY = "AuthnRequest";
-
- /** Key in an HttpSession for the Issuer. */
- protected static final String ISSUER_SESSION_KEY = "Issuer";
-
- /**
- * Backing store for artifacts. This must be shared between ShibbolethSSO
- * and AttributeQuery.
- */
- protected ArtifactMap artifactMap;
-
- /** The path to the IdP's AuthenticationManager servlet */
- protected String authnMgrURL;
-
- /** AuthenticationManager to be used */
- protected AuthenticationManager authnMgr;
-
- /** ArtifactFactory used to make artifacts. */
- protected SAMLArtifactFactory artifactFactory;
-
- /** A pool of XML parsers. */
- protected ParserPool parserPool;
-
- /**
- * Constructor.
- */
- public AbstractAuthenticationRequest() {
-
- parserPool = new BasicParserPool();
- artifactFactory = new SAMLArtifactFactory();
- }
-
- /**
- * Set the Authentication Mananger.
- *
- * @param authnManager
- * The IdP's AuthenticationManager.
- */
- public void setAuthenticationManager(AuthenticationManager authnManager) {
- this.authnMgr = authnMgr;
- }
-
- /**
- * Set the ArtifactMap.
- *
- * @param artifactMap
- * The IdP's ArtifactMap.
- */
- public void setArtifactMap(ArtifactMap artifactMap) {
- this.artifactMap = artifactMap;
- }
-
- /**
- * Evaluate a SAML 2 AuthenticationRequest message.
- *
- * @param authnRequest
- * A SAML 2 AuthenticationRequest
- * @param issuer
- * The issuer of the authnRequest.
- * @param session
- * The HttpSession of the request.
- * @param relyingParty
- * The RelyingPartyConfiguration for the request.
- * @param ssoConfig
- * The SSOConfiguration for the request.
- * @param spDescriptor
- * The SPSSODescriptor for the request.
- *
- * @return A Response containing a failure message or a AuthenticationStmt.
- *
- * @throws ServletException
- * On Error.
- */
- protected Response evaluateRequest(final AuthnRequest authnRequest,
- final Issuer issuer, final HttpSession session,
- final RelyingPartyConfiguration relyingParty,
- final SSOConfiguration ssoConfig, final SPSSODescriptor spDescriptor)
- throws ServletException {
-
- Response samlResponse;
-
- try {
- // check if the authentication was successful.
- Saml2LoginContext loginCtx = getLoginContext(session);
- if (!loginCtx.getAuthenticationOK()) {
- // if authentication failed, send the appropriate SAML error
- // message.
- String failureMessage = loginCtx
- .getAuthenticationFailureMessage();
- Status failureStatus = buildStatus(StatusCode.RESPONDER_URI,
- StatusCode.AUTHN_FAILED_URI, failureMessage);
- samlResponse = buildResponse(authnRequest.getID(),
- new DateTime(), relyingParty.getProviderId(),
- failureStatus);
-
- return samlResponse;
- }
-
- // the user successfully authenticated.
- // build an authentication assertion.
- samlResponse = buildResponse(authnRequest.getID(), new DateTime(),
- relyingParty.getProviderId(), buildStatus(
- StatusCode.SUCCESS_URI, null, null));
-
- DateTime now = new DateTime();
- Conditions conditions = conditionsBuilder.buildObject();
- conditions.setNotBefore(now.minusSeconds(30)); // for now, clock
- // skew is
- // hard-coded to 30
- // seconds.
- conditions.setNotOnOrAfter(now.plus(ssoConfig
- .getAssertionLifetime()));
-
- // XXX: don't blindly copy conditions from the AuthnRequest.
- List<Condition> requestConditions = authnRequest.getConditions()
- .getConditions();
- if (requestConditions != null && requestConditions.size() > 0) {
- conditions.getConditions().addAll(requestConditions);
- }
-
- Assertion assertion = buildAssertion(authnRequest.getSubject(),
- conditions, issuer, new String[] { relyingParty
- .getRelyingPartyId() });
- setAuthenticationStatement(assertion, loginCtx, authnRequest);
-
- samlResponse.getAssertions().add(assertion);
-
- // retrieve the AssertionConsumerService endpoint (we parsed it in
- // verifyAuthnRequest()
- AssertionConsumerService acsEndpoint = getACSEndpointFromSession(session);
-
- } catch (AuthenticationRequestException ex) {
-
- Status errorStatus = ex.getStatus();
- if (errorStatus == null) {
- // if no explicit status code was set,
- // assume the error was in the message.
- samlResponse = buildResponse(authnRequest.getID(),
- new DateTime(), relyingParty.getProviderId(),
- errorStatus);
- }
- }
-
- return samlResponse;
- }
-
- /**
- * Check that a request's issuer can be found in the metadata. If so, store
- * the relevant metadata objects in the user's session.
- *
- * @param issuer
- * The issuer of the AuthnRequest.
- * @param relyingParty
- * The RelyingPartyConfiguration for the issuer.
- * @param ssoConfig
- * The SSOConfiguration for the relyingParty
- * @param spDescriptor
- * The SPSSODescriptor for the ssoConfig.
- *
- * @return <code>true</code> if Metadata was found for the issuer;
- * otherwise, <code>false</code>.
- */
- protected boolean findMetadataForSSORequest(Issuer issuer,
- RelyingPartyConfiguration relyingParty, SSOConfiguration ssoConfig,
- SPSSODescriptor spDescriptor) {
-
- MetadataProvider metadataProvider = getRelyingPartyConfigurationManager()
- .getMetadataProvider();
- String providerId = issuer.getSPProvidedID();
- relyingParty = getRelyingPartyConfigurationManager()
- .getRelyingPartyConfiguration(providerId);
- ssoConfig = (SSOConfiguration) relyingParty.getProfileConfigurations()
- .get(SSOConfiguration.PROFILE_ID);
-
- try {
- spDescriptor = metadataProvider.getEntityDescriptor(
- relyingParty.getRelyingPartyId()).getSPSSODescriptor(
- SAML20_PROTOCOL_URI);
- } catch (MetadataProviderException ex) {
- log.error(
- "SAML 2 Authentication Request: Unable to locate metadata for SP "
- + providerId + " for protocol "
- + SAML20_PROTOCOL_URI, ex);
- return false;
- }
-
- if (spDescriptor == null) {
- log
- .error("SAML 2 Authentication Request: Unable to locate metadata for SP "
- + providerId
- + " for protocol "
- + SAML20_PROTOCOL_URI);
- return false;
- }
-
- return true;
- }
-
- /**
- * Check if the user has already been authenticated.
- *
- * @param httpSession
- * the user's HttpSession.
- *
- * @return <code>true</code> if the user has been authenticated. otherwise
- * <code>false</code>
- */
- protected boolean hasUserAuthenticated(final HttpSession httpSession) {
-
- // if the user has authenticated, their session will have a LoginContext
-
- Object o = httpSession.getAttribute(LoginContext.LOGIN_CONTEXT_KEY);
- return (o != null && o instanceof LoginContext);
- }
-
- /**
- * Store a user's AuthnRequest and Issuer in the session.
- *
- * @param authnRequest
- * A SAML 2 AuthnRequest.
- * @param issuer
- * The issuer of the AuthnRequest.
- * @param session
- * The HttpSession in which the data should be stored.
- * @param relyingParty
- * The RelyingPartyConfiguration for the issuer.
- * @param ssoConfig
- * The SSOConfiguration for the relyingParty
- * @param spDescriptor
- * The SPSSODescriptor for the ssoConfig.
- */
- protected void storeRequestData(final HttpSession session,
- final AuthnRequest authnRequest, final Issuer issuer,
- final RelyingPartyConfiguration relyingParty,
- final SSOConfiguration ssoConfig, final SPSSODescriptor spDescriptor) {
-
- if (session == null) {
- return;
- }
-
- session.setAttribute(AUTHNREQUEST_SESSION_KEY, authnRequest);
- session.setAttribute(ISSUER_SESSION_KEY, issuer);
- session.setAttribute(RPCONFIG_SESSION_KEY, relyingParty);
- session.setAttribute(SSOCONFIG_SESSION_KEY, ssoConfig);
- session.setAttribute(SPSSODESC_SESSION_KEY, spDescriptor);
- }
-
- /**
- * Retrieve the AuthnRequest and Issuer from a session.
- *
- * @param session
- * The HttpSession in which the data was stored.
- * @param authnRequest
- * Will be populated with the AuthnRequest.
- * @param issuer
- * Will be populated with the ssuer of the AuthnRequest.
- * @param relyingParty
- * Will be populated with the RelyingPartyConfiguration for the
- * issuer.
- * @param ssoConfig
- * Will be populated with the SSOConfiguration for the
- * relyingParty
- * @param spDescriptor
- * Will be populated with the SPSSODescriptor for the ssoConfig.
- */
- protected void retrieveRequestData(final HttpSession session,
- AuthnRequest authnRequest, Issuer issuer,
- RelyingPartyConfiguration relyingParty, SSOConfiguration ssoConfig,
- SPSSODescriptor spDescriptor) {
-
- if (session == null) {
- authnRequest = null;
- issuer = null;
- }
-
- authnRequest = (AuthnRequest) session
- .getAttribute(AUTHNREQUEST_SESSION_KEY);
- issuer = (Issuer) session.getAttribute(ISSUER_SESSION_KEY);
- relyingParty = (RelyingPartyConfiguration) session
- .getAttribute(RPCONFIG_SESSION_KEY);
- ssoConfig = (SSOConfiguration) session
- .getAttribute(SSOCONFIG_SESSION_KEY);
- spDescriptor = (SPSSODescriptor) session
- .getAttribute(SPSSODESC_SESSION_KEY);
-
- session.removeAttribute(AUTHNREQUEST_SESSION_KEY);
- session.removeAttribute(ISSUER_SESSION_KEY);
- session.removeAttribute(RPCONFIG_SESSION_KEY);
- session.removeAttribute(SSOCONFIG_SESSION_KEY);
- session.removeAttribute(SPSSODESC_SESSION_KEY);
- }
-
- /**
- * Check if the user has already been authenticated. If so, return the
- * LoginContext. If not, redirect the user to the AuthenticationManager.
- *
- * @param authnRequest
- * The SAML 2 AuthnRequest.
- * @param httpSession
- * The user's HttpSession.
- * @param request
- * The user's HttpServletRequest.
- * @param response
- * The user's HttpServletResponse.
- *
- * @return A LoginContext for the authenticated user.
- *
- * @throws SerlvetException
- * on error.
- */
- protected void authenticateUser(final AuthnRequest authnRequest,
- final HttpSession httpSession, final HttpServletRequest request,
- final HttpServletResponse response) throws ServletException {
-
- // Forward the request to the AuthenticationManager.
- // When the AuthenticationManager is done it will
- // forward the request back to this servlet.
-
- Saml2LoginContext loginCtx = new Saml2LoginContext(authnRequest);
- loginCtx.setProfileHandlerURL(request.getPathInfo());
- httpSession.setAttribute(LoginContext.LOGIN_CONTEXT_KEY, loginCtx);
- try {
- RequestDispatcher dispatcher = request
- .getRequestDispatcher(authnMgrURL);
- dispatcher.forward(request, response);
- } catch (IOException ex) {
- log.error("Error forwarding SAML 2 AuthnRequest "
- + authnRequest.getID() + " to AuthenticationManager", ex);
- throw new ServletException("Error forwarding SAML 2 AuthnRequest "
- + authnRequest.getID() + " to AuthenticationManager", ex);
- }
- }
-
- /**
- * Build an AuthnStatement and add it to a Response.
- *
- * @param response
- * The Response to which the AuthnStatement will be added.
- * @param loginCtx
- * The LoginContext of the sucessfully authenticated user.
- * @param authnRequest
- * The AuthnRequest that prompted this message.
- * @param ssoConfig
- * The SSOConfiguration for the RP to which we are addressing
- * this message.
- * @param issuer
- * The IdP's identifier.
- * @param audiences
- * An array of URIs restricting the audience of this assertion.
- */
- protected void setAuthenticationStatement(Assertion assertion,
- final Saml2LoginContext loginContext,
- final AuthnRequest authnRequest) throws ServletException {
-
- // Build the AuthnCtx. We need to determine if the user was
- // authenticated
- // with an AuthnContextClassRef or a AuthnContextDeclRef
- AuthnContext authnCtx = buildAuthnCtx(authnRequest
- .getRequestedAuthnContext(), loginContext);
- if (authnCtx == null) {
- log.error("Error respond to SAML 2 AuthnRequest "
- + authnRequest.getID()
- + " : Unable to determine authentication method");
- }
-
- AuthnStatement stmt = authnStatementBuilder.buildObject();
- stmt.setAuthnInstant(loginContext.getAuthenticationInstant());
- stmt.setAuthnContext(authnCtx);
-
- // add the AuthnStatement to the Assertion
- List<AuthnStatement> authnStatements = assertion.getAuthnStatements();
- authnStatements.add(stmt);
- }
-
- /**
- * Create the AuthnContex object.
- *
- * To do this, we have to walk the AuthnRequest's RequestedAuthnContext
- * object and compare any values we find to what's set in the loginContext.
- *
- * @param requestedAuthnCtx
- * The RequestedAuthnContext from the Authentication Request.
- * @param authnMethod
- * The authentication method that was used.
- *
- * @return An AuthnCtx object on success or <code>null</code> on failure.
- */
- protected AuthnContext buildAuthnCtx(
- final RequestedAuthnContext requestedAuthnCtx,
- final Saml2LoginContext loginContext) {
-
- // this method assumes that only one URI will match.
-
- AuthnContext authnCtx = authnContextBuilder.buildObject();
- String authnMethod = loginContext.getAuthenticationMethod();
-
- List<AuthnContextClassRef> authnClasses = requestedAuthnCtx
- .getAuthnContextClassRefs();
- List<AuthnContextDeclRef> authnDeclRefs = requestedAuthnCtx
- .getAuthnContextDeclRefs();
-
- if (authnClasses != null) {
- for (AuthnContextClassRef classRef : authnClasses) {
- if (classRef != null) {
- String s = classRef.getAuthnContextClassRef();
- if (s != null && authnMethod.equals(s)) {
- AuthnContextClassRef ref = authnContextClassRefBuilder
- .buildObject();
- authnCtx.setAuthnContextClassRef(ref);
- return authnCtx;
- }
- }
- }
- }
-
- // if no AuthnContextClassRef's matched, try the DeclRefs
- if (authnDeclRefs != null) {
- for (AuthnContextDeclRef declRef : authnDeclRefs) {
- if (declRef != null) {
- String s = declRef.getAuthnContextDeclRef();
- if (s != null && authnMethod.equals((s))) {
- AuthnContextDeclRef ref = authnContextDeclRefBuilder
- .buildObject();
- authnCtx.setAuthnContextDeclRef(ref);
- return authnCtx;
- }
- }
- }
- }
-
- // no matches were found.
- return null;
- }
-
- /**
- * Get the user's LoginContext.
- *
- * @param httpSession
- * The user's HttpSession.
- *
- * @return The user's LoginContext.
- *
- * @throws ServletException
- * On error.
- */
- protected Saml2LoginContext getLoginContext(final HttpSession httpSession)
- throws ServletException {
-
- Object o = httpSession.getAttribute(LoginContext.LOGIN_CONTEXT_KEY);
- if (o == null) {
- log.error("User's session does not contain a LoginContext object.");
- throw new ServletException(
- "User's session does not contain a LoginContext object.");
- }
-
- if (!(o instanceof Saml2LoginContext)) {
- log
- .error("Invalid login context object -- object is not an instance of Saml2LoginContext.");
- throw new ServletException("Invalid login context object.");
- }
-
- Saml2LoginContext ctx = (Saml2LoginContext) o;
-
- httpSession.removeAttribute(LoginContext.LOGIN_CONTEXT_KEY);
-
- return ctx;
- }
-
- /**
- * Verify the AuthnRequest is well-formed.
- *
- * @param authnRequest
- * The user's SAML 2 AuthnRequest.
- * @param issuer
- * The Issuer of the AuthnRequest.
- * @param relyingParty
- * The relying party configuration for the request's originator.
- * @param session
- * The user's HttpSession.
- *
- * @throws AuthenticationRequestException
- * on error.
- */
- protected void verifyAuthnRequest(final AuthnRequest authnRequest,
- Issuer issuer, final RelyingPartyConfiguration relyingParty,
- final HttpSession session) throws AuthenticationRequestException {
-
- Status failureStatus;
-
- // Check if we are in scope to handle this AuthnRequest
- checkScope(authnRequest, issuer.getSPProvidedID());
-
- // XXX: run signature checks on authnRequest
-
- // verify that the AssertionConsumerService url is valid.
- AssertionConsumerService acsEndpoint = getAndVerifyACSEndpoint(
- authnRequest, relyingParty.getRelyingPartyId(),
- getRelyingPartyConfigurationManager().getMetadataProvider());
- session.setAttribute(ACS_SESSION_KEY, acsEndpoint);
-
- // check for nameID constraints.
- Subject subject = getAndVerifySubject(authnRequest);
- }
-
- /**
- * Get and verify the Subject element.
- *
- * @param authnRequest
- * The SAML 2 AuthnRequest.
- *
- * @return A Subject element.
- *
- * @throws AuthenticationRequestException
- * on error.
- */
- protected Subject getAndVerifySubject(final AuthnRequest authnRequest)
- throws AuthenticationRequestException {
-
- Status failureStatus;
-
- Subject subject = authnRequest.getSubject();
-
- if (subject == null) {
- failureStatus = buildStatus(StatusCode.REQUESTER_URI, null,
- "SAML 2 AuthnRequest " + authnRequest.getID()
- + " is malformed: It does not contain a Subject.");
- throw new AuthenticationRequestException(
- "AuthnRequest lacks a Subject", failureStatus);
- }
-
- // The Web Browser SSO profile disallows SubjectConfirmation
- // methods in the requested subject.
- List<SubjectConfirmation> confMethods = subject
- .getSubjectConfirmations();
- if (confMethods != null || confMethods.size() > 0) {
- log
- .error("SAML 2 AuthnRequest "
- + authnRequest.getID()
- + " is malformed: It contains SubjectConfirmation elements.");
- failureStatus = buildStatus(
- StatusCode.REQUESTER_URI,
- null,
- "SAML 2 AuthnRequest "
- + authnRequest.getID()
- + " is malformed: It contains SubjectConfirmation elements.");
- throw new AuthenticationRequestException(
- "AuthnRequest contains SubjectConfirmation elements",
- failureStatus);
- }
-
- return subject;
- }
-
- /**
- * Return the endpoint URL and protocol binding to use for the AuthnRequest.
- *
- * @param authnRequest
- * The SAML 2 AuthnRequest.
- * @param providerId
- * The SP's providerId.
- * @param metadata
- * The appropriate Metadata.
- *
- * @return The AssertionConsumerService for the endpoint, or
- * <code>null</code> on error.
- *
- * @throws AuthenticationRequestException
- * On error.
- */
- protected AssertionConsumerService getAndVerifyACSEndpoint(
- final AuthnRequest authnRequest, String providerId,
- final MetadataProvider metadata)
- throws AuthenticationRequestException {
-
- Status failureStatus;
-
- // Either the AssertionConsumerServiceIndex must be present
- // or AssertionConsumerServiceURL must be present.
-
- Integer idx = authnRequest.getAssertionConsumerServiceIndex();
- String acsURL = authnRequest.getAssertionConsumerServiceURL();
-
- if (idx != null && acsURL != null) {
- log
- .error("SAML 2 AuthnRequest "
- + authnRequest.getID()
- + " is malformed: It contains both an AssertionConsumerServiceIndex and an AssertionConsumerServiceURL");
- failureStatus = buildStatus(
- StatusCode.REQUESTER_URI,
- null,
- "SAML 2 AuthnRequest "
- + authnRequest.getID()
- + " is malformed: It contains both an AssertionConsumerServiceIndex and an AssertionConsumerServiceURL");
- throw new AuthenticationRequestException("Malformed AuthnRequest",
- failureStatus);
- }
-
- SPSSODescriptor spDescriptor;
- try {
- spDescriptor = metadata.getEntityDescriptor(providerId)
- .getSPSSODescriptor(SAML20_PROTOCOL_URI);
- } catch (MetadataProviderException ex) {
- log.error(
- "Unable retrieve SPSSODescriptor metadata for providerId "
- + providerId
- + " while processing SAML 2 AuthnRequest "
- + authnRequest.getID(), ex);
- failureStatus = buildStatus(StatusCode.RESPONDER_URI, null,
- "Unable to locate metadata for " + providerId);
- throw new AuthenticationRequestException(
- "Unable to locate metadata", ex, failureStatus);
- }
-
- List<AssertionConsumerService> acsList = spDescriptor
- .getAssertionConsumerServices();
-
- // if the ACS index is specified, retrieve it from the metadata
- if (idx != null) {
-
- int i = idx.intValue();
-
- // if the index is out of range, return an appropriate error.
- if (i > acsList.size()) {
- log.error("Illegal AssertionConsumerIndex specicifed (" + i
- + ") in SAML 2 AuthnRequest " + authnRequest.getID());
-
- failureStatus = buildStatus(StatusCode.REQUESTER_URI, null,
- "Illegal AssertionConsumerIndex specicifed (" + i
- + ") in SAML 2 AuthnRequest "
- + authnRequest.getID());
-
- throw new AuthenticationRequestException(
- "Illegal AssertionConsumerIndex in AuthnRequest",
- failureStatus);
- }
-
- return acsList.get(i);
- }
-
- // if the ACS endpoint is specified, validate it against the metadata
- String protocolBinding = authnRequest.getProtocolBinding();
- for (AssertionConsumerService acs : acsList) {
- if (acsURL.equals(acs.getLocation())) {
- if (protocolBinding != null) {
- if (protocolBinding.equals(acs.getBinding())) {
- return acs;
- }
- }
- }
- }
-
- log
- .error("Error processing SAML 2 AuthnRequest message "
- + authnRequest.getID()
- + ": Unable to validate AssertionConsumerServiceURL against metadata: "
- + acsURL + " for binding " + protocolBinding);
-
- failureStatus = buildStatus(StatusCode.REQUESTER_URI, null,
- "Unable to validate AssertionConsumerService against metadata.");
-
- throw new AuthenticationRequestException(
- "SAML 2 AuthenticationRequest: Unable to validate AssertionConsumerService against Metadata",
- failureStatus);
- }
-
- /**
- * Retrieve a parsed AssertionConsumerService endpoint from the user's
- * session.
- *
- * @param session
- * The user's HttpSession.
- *
- * @return An AssertionConsumerServiceEndpoint object.
- *
- * @throws ServletException
- * On error.
- */
- protected AssertionConsumerService getACSEndpointFromSession(
- final HttpSession session) throws ServletException {
-
- Object o = session.getAttribute(ACS_SESSION_KEY);
- if (o == null) {
- log
- .error("User's session does not contain an AssertionConsumerService object.");
- throw new ServletException(
- "User's session does not contain an AssertionConsumerService object.");
- }
-
- if (!(o instanceof AssertionConsumerService)) {
- log
- .error("Invalid session data -- object is not an instance of AssertionConsumerService.");
- throw new ServletException(
- "Invalid session data -- object is not an instance of AssertionConsumerService.");
- }
-
- AssertionConsumerService endpoint = (AssertionConsumerService) o;
-
- session.removeAttribute(ACS_SESSION_KEY);
-
- return endpoint;
- }
-
- /**
- * Check if an {@link AuthnRequest} contains a {@link Scoping} element. If
- * so, check if the specified IdP is in the {@link IDPList} element. If no
- * Scoping element is present, this method returns <code>true</code>.
- *
- * @param authnRequest
- * The {@link AuthnRequest} element to check.
- * @param providerId
- * The IdP's ProviderID.
- *
- * @throws AuthenticationRequestException
- * on error.
- */
- protected void checkScope(final AuthnRequest authnRequest, String providerId)
- throws AuthenticationRequestException {
-
- Status failureStatus;
-
- List<String> idpEntries = new LinkedList<String>();
-
- Scoping scoping = authnRequest.getScoping();
- if (scoping == null) {
- return;
- }
-
- // process all of the explicitly listed idp provider ids
- IDPList idpList = scoping.getIDPList();
- if (idpList == null) {
- return;
- }
-
- List<IDPEntry> explicitIDPEntries = idpList.getIDPEntrys();
- if (explicitIDPEntries != null) {
- for (IDPEntry entry : explicitIDPEntries) {
- String s = entry.getProviderID();
- if (s != null) {
- idpEntries.add(s);
- }
- }
- }
-
- // If the IDPList is incomplete, retrieve the complete list
- // and add the entries to idpEntries.
- GetComplete getComplete = idpList.getGetComplete();
- IDPList referencedIdPs = getCompleteIDPList(getComplete);
- if (referencedIdPs != null) {
- List<IDPEntry> referencedIDPEntries = referencedIdPs.getIDPEntrys();
- if (referencedIDPEntries != null) {
- for (IDPEntry entry : referencedIDPEntries) {
- String s = entry.getProviderID();
- if (s != null) {
- idpEntries.add(s);
- }
- }
- }
- }
-
- // iterate over all the IDPEntries we've gathered,
- // and check if we're in scope.
- for (String requestProviderId : idpEntries) {
- if (providerId.equals(requestProviderId)) {
- log.debug("Found Scoping match for IdP: (" + providerId + ")");
- return;
- }
- }
-
- log.error("SAML 2 AuthnRequest " + authnRequest.getID()
- + " contains a Scoping element which "
- + "does not contain a providerID registered with this IdP.");
-
- failureStatus = buildStatus(StatusCode.RESPONDER_URI,
- StatusCode.NO_SUPPORTED_IDP_URI, null);
- throw new AuthenticationRequestException(
- "Unrecognized providerID in Scoping element", failureStatus);
- }
-
- /**
- * Retrieve an incomplete IDPlist.
- *
- * This only handles URL-based <GetComplete/> references.
- *
- * @param getComplete
- * The (possibly <code>null</code>) <GetComplete/>
- * element
- *
- * @return an {@link IDPList} or <code>null</code> if the uri can't be
- * dereferenced.
- */
- protected IDPList getCompleteIDPList(final GetComplete getComplete) {
-
- // XXX: enhance this method to cache the url and last-modified-header
-
- if (getComplete == null) {
- return null;
- }
-
- String uri = getComplete.getGetComplete();
- if (uri != null) {
- return null;
- }
-
- IDPList idpList = null;
- InputStream istream = null;
-
- try {
- URL url = new URL(uri);
- URLConnection conn = url.openConnection();
- istream = conn.getInputStream();
-
- // convert the raw data into an XML object
- Document doc = parserPool.parse(istream);
- Element docElement = doc.getDocumentElement();
- Unmarshaller unmarshaller = Configuration.getUnmarshallerFactory()
- .getUnmarshaller(docElement);
- idpList = (IDPList) unmarshaller.unmarshall(docElement);
-
- } catch (MalformedURLException ex) {
- log.error(
- "Unable to retrieve GetComplete IDPList. Unsupported URI: "
- + uri, ex);
- } catch (IOException ex) {
- log.error("IO Error while retreieving GetComplete IDPList from "
- + uri, ex);
- } catch (ConfigurationException ex) {
- log.error(
- "Internal OpenSAML error while parsing GetComplete IDPList from "
- + uri, ex);
- } catch (XMLParserException ex) {
- log.error(
- "Internal OpenSAML error while parsing GetComplete IDPList from "
- + uri, ex);
- } catch (UnmarshallingException ex) {
- log.error(
- "Internal OpenSAML error while unmarshalling GetComplete IDPList from "
- + uri, ex);
- } finally {
-
- if (istream != null) {
- try {
- istream.close();
- } catch (IOException ex) {
- // pass
- }
- }
- }
-
- return idpList;
- }
+public abstract class AbstractAuthenticationRequest extends AbstractSAML2ProfileHandler {
+
+ /** Class logger. */
+ private static final Logger log = Logger.getLogger(AbstractAuthenticationRequest.class);
+
+ /** Key in an HttpSession for the AssertionConsumerService object. */
+ protected static final String ACS_SESSION_KEY = "AssertionConsumerService";
+
+ /** Key in an HttpSession for the RelyingParty. */
+ protected static final String RPCONFIG_SESSION_KEY = "RelyingPartyConfiguration";
+
+ /** Key in an HttpSession for the SSOConfiguration. */
+ protected static final String SSOCONFIG_SESSION_KEY = "SSOConfiguration";
+
+ /** Key in an HttpSession for the SPSSODescriptor. */
+ protected static final String SPSSODESC_SESSION_KEY = "SPSSODescriptor";
+
+ /** Key in an HttpSession for the AuthnRequest. */
+ protected static final String AUTHNREQUEST_SESSION_KEY = "AuthnRequest";
+
+ /** Key in an HttpSession for the Issuer. */
+ protected static final String ISSUER_SESSION_KEY = "Issuer";
+
+ /** Backing store for artifacts. */
+ protected ArtifactMap artifactMap;
+
+ /** The path to the IdP's AuthenticationManager servlet */
+ protected String authnMgrURL;
+
+ /** AuthenticationManager to be used */
+ protected AuthenticationManager authnMgr;
+
+ /** ArtifactFactory used to make artifacts. */
+ protected SAMLArtifactFactory artifactFactory;
+
+ /** A pool of XML parsers. */
+ protected ParserPool parserPool;
+
+ /** Builder for AuthnStatements. */
+ protected SAMLObjectBuilder<AuthnStatement> authnStatementBuilder;
+
+ /** Builder for AuthnContexts. */
+ protected SAMLObjectBuilder<AuthnContext> authnContextBuilder;
+
+ /** Builder for AuthnContextDeclRef's */
+ protected SAMLObjectBuilder<AuthnContextDeclRef> authnContextDeclRefBuilder;
+
+ /** Builder for AuthnContextClassRef's. */
+ protected SAMLObjectBuilder<AuthnContextClassRef> authnContextClassRefBuilder;
+
+ /**
+ * Constructor.
+ */
+ public AbstractAuthenticationRequest() {
+
+ parserPool = new BasicParserPool();
+ artifactFactory = new SAMLArtifactFactory();
+ authnStatementBuilder = (SAMLObjectBuilder<AuthnStatement>) getBuilderFactory().getBuilder(AuthnStatement.DEFAULT_ELEMENT_NAME);
+ authnContextBuilder = (SAMLObjectBuilder<AuthnContext>) getBuilderFactory().getBuilder(AuthnContext.DEFAULT_ELEMENT_NAME);
+ authnContextDeclRefBuilder = (SAMLObjectBuilder<AuthnContextDeclRef>) getBuilderFactory().getBuilder(AuthnContextDeclRef.DEFAULT_ELEMENT_NAME);
+ authnContextClassRefBuilder = (SAMLObjectBuilder<AuthnContextClassRef>) getBuilderFactory().getBuilder(AuthnContextClassRef.DEFAULT_ELEMENT_NAME);
+ }
+
+ /**
+ * Set the Authentication Mananger.
+ *
+ * @param authnManager
+ * The IdP's AuthenticationManager.
+ */
+ public void setAuthenticationManager(AuthenticationManager authnManager) {
+ this.authnMgr = authnMgr;
+ }
+
+ /**
+ * Set the ArtifactMap.
+ *
+ * @param artifactMap
+ * The IdP's ArtifactMap.
+ */
+ public void setArtifactMap(ArtifactMap artifactMap) {
+ this.artifactMap = artifactMap;
+ }
+
+ /**
+ * Evaluate a SAML 2 AuthenticationRequest message.
+ *
+ * @param authnRequest
+ * A SAML 2 AuthenticationRequest
+ * @param issuer
+ * The issuer of the authnRequest.
+ * @param session
+ * The HttpSession of the request.
+ * @param relyingParty
+ * The RelyingPartyConfiguration for the request.
+ * @param ssoConfig
+ * The SSOConfiguration for the request.
+ * @param spDescriptor
+ * The SPSSODescriptor for the request.
+ *
+ * @return A Response containing a failure message or a AuthenticationStmt.
+ *
+ * @throws ServletException
+ * On Error.
+ */
+ protected Response evaluateRequest(final AuthnRequest authnRequest,
+ final Issuer issuer, final HttpSession session,
+ final RelyingPartyConfiguration relyingParty,
+ final SSOConfiguration ssoConfig, final SPSSODescriptor spDescriptor)
+ throws ServletException {
+
+ Response samlResponse;
+
+ try {
+ // check if the authentication was successful.
+ Saml2LoginContext loginCtx = getLoginContext(session);
+ if (!loginCtx.getAuthenticationOK()) {
+ // if authentication failed, send the appropriate SAML error message.
+ String failureMessage = loginCtx.getAuthenticationFailureMessage();
+ Status failureStatus = buildStatus(StatusCode.RESPONDER_URI, StatusCode.AUTHN_FAILED_URI, failureMessage);
+ samlResponse = buildResponse(authnRequest.getID(), new DateTime(), relyingParty.getProviderId(),
+ failureStatus);
+
+ return samlResponse;
+ }
+
+ // the user successfully authenticated.
+ // build an authentication assertion.
+ samlResponse = buildResponse(authnRequest.getID(), new DateTime(),
+ relyingParty.getProviderId(), buildStatus(StatusCode.SUCCESS_URI, null, null));
+
+ DateTime now = new DateTime();
+ Assertion assertion = buildAssertion(now, relyingParty,
+ (AbstractSAML2ProfileConfiguration) relyingParty.getProfileConfigurations().get(SSOConfiguration.PROFILE_ID));
+ assertion.setSubject(authnRequest.getSubject());
+ setAuthenticationStatement(assertion, loginCtx, authnRequest);
+
+ samlResponse.getAssertions().add(assertion);
+
+ // retrieve the AssertionConsumerService endpoint (we parsed it in
+ // verifyAuthnRequest()
+ AssertionConsumerService acsEndpoint = getACSEndpointFromSession(session);
+
+ } catch (AuthenticationRequestException ex) {
+
+ Status errorStatus = ex.getStatus();
+ if (errorStatus == null) {
+ // if no explicit status code was set,
+ // assume the error was in the message.
+ samlResponse = buildResponse(authnRequest.getID(),
+ new DateTime(), relyingParty.getProviderId(),
+ errorStatus);
+ }
+ }
+
+ return samlResponse;
+ }
+
+ /**
+ * Build a SAML 2 Response element with basic fields populated.
+ *
+ * Failure handlers can send the returned response element to the RP.
+ * Success handlers should add the assertions before sending it.
+ *
+ * @param inResponseTo
+ * The ID of the request this is in response to.
+ * @param issueInstant
+ * The timestamp of this response.
+ * @param issuer
+ * The URI of the RP issuing the response.
+ * @param status
+ * The response's status code.
+ *
+ * @return The populated Response object.
+ */
+ protected Response buildResponse(String inResponseTo,
+ final DateTime issueInstant, String issuer, final Status status) {
+
+ Response response = getResponseBuilder().buildObject();
+
+ Issuer i = getIssuerBuilder().buildObject();
+ i.setValue(issuer);
+
+ response.setVersion(SAML_VERSION);
+ response.setID(getIdGenerator().generateIdentifier());
+ response.setInResponseTo(inResponseTo);
+ response.setIssueInstant(issueInstant);
+ response.setIssuer(i);
+ response.setStatus(status);
+
+ return response;
+ }
+
+
+ /**
+ * Check that a request's issuer can be found in the metadata. If so, store
+ * the relevant metadata objects in the user's session.
+ *
+ * @param issuer
+ * The issuer of the AuthnRequest.
+ * @param relyingParty
+ * The RelyingPartyConfiguration for the issuer.
+ * @param ssoConfig
+ * The SSOConfiguration for the relyingParty
+ * @param spDescriptor
+ * The SPSSODescriptor for the ssoConfig.
+ *
+ * @return <code>true</code> if Metadata was found for the issuer;
+ * otherwise, <code>false</code>.
+ */
+ protected boolean findMetadataForSSORequest(Issuer issuer,
+ RelyingPartyConfiguration relyingParty, SSOConfiguration ssoConfig,
+ SPSSODescriptor spDescriptor) {
+
+ MetadataProvider metadataProvider = getRelyingPartyConfigurationManager()
+ .getMetadataProvider();
+ String providerId = issuer.getSPProvidedID();
+ relyingParty = getRelyingPartyConfigurationManager()
+ .getRelyingPartyConfiguration(providerId);
+ ssoConfig = (SSOConfiguration) relyingParty.getProfileConfigurations()
+ .get(SSOConfiguration.PROFILE_ID);
+
+ try {
+ spDescriptor = metadataProvider.getEntityDescriptor(
+ relyingParty.getRelyingPartyId()).getSPSSODescriptor(
+ SAML20_PROTOCOL_URI);
+ } catch (MetadataProviderException ex) {
+ log.error(
+ "SAML 2 Authentication Request: Unable to locate metadata for SP "
+ + providerId + " for protocol "
+ + SAML20_PROTOCOL_URI, ex);
+ return false;
+ }
+
+ if (spDescriptor == null) {
+ log
+ .error("SAML 2 Authentication Request: Unable to locate metadata for SP "
+ + providerId
+ + " for protocol "
+ + SAML20_PROTOCOL_URI);
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Check if the user has already been authenticated.
+ *
+ * @param httpSession
+ * the user's HttpSession.
+ *
+ * @return <code>true</code> if the user has been authenticated. otherwise
+ * <code>false</code>
+ */
+ protected boolean hasUserAuthenticated(final HttpSession httpSession) {
+
+ // if the user has authenticated, their session will have a LoginContext
+
+ Object o = httpSession.getAttribute(LoginContext.LOGIN_CONTEXT_KEY);
+ return (o != null && o instanceof LoginContext);
+ }
+
+ /**
+ * Store a user's AuthnRequest and Issuer in the session.
+ *
+ * @param authnRequest
+ * A SAML 2 AuthnRequest.
+ * @param issuer
+ * The issuer of the AuthnRequest.
+ * @param session
+ * The HttpSession in which the data should be stored.
+ * @param relyingParty
+ * The RelyingPartyConfiguration for the issuer.
+ * @param ssoConfig
+ * The SSOConfiguration for the relyingParty
+ * @param spDescriptor
+ * The SPSSODescriptor for the ssoConfig.
+ */
+ protected void storeRequestData(final HttpSession session,
+ final AuthnRequest authnRequest, final Issuer issuer,
+ final RelyingPartyConfiguration relyingParty,
+ final SSOConfiguration ssoConfig, final SPSSODescriptor spDescriptor) {
+
+ if (session == null) {
+ return;
+ }
+
+ session.setAttribute(AUTHNREQUEST_SESSION_KEY, authnRequest);
+ session.setAttribute(ISSUER_SESSION_KEY, issuer);
+ session.setAttribute(RPCONFIG_SESSION_KEY, relyingParty);
+ session.setAttribute(SSOCONFIG_SESSION_KEY, ssoConfig);
+ session.setAttribute(SPSSODESC_SESSION_KEY, spDescriptor);
+ }
+
+ /**
+ * Retrieve the AuthnRequest and Issuer from a session.
+ *
+ * @param session
+ * The HttpSession in which the data was stored.
+ * @param authnRequest
+ * Will be populated with the AuthnRequest.
+ * @param issuer
+ * Will be populated with the ssuer of the AuthnRequest.
+ * @param relyingParty
+ * Will be populated with the RelyingPartyConfiguration for the
+ * issuer.
+ * @param ssoConfig
+ * Will be populated with the SSOConfiguration for the
+ * relyingParty
+ * @param spDescriptor
+ * Will be populated with the SPSSODescriptor for the ssoConfig.
+ */
+ protected void retrieveRequestData(final HttpSession session,
+ AuthnRequest authnRequest, Issuer issuer,
+ RelyingPartyConfiguration relyingParty, SSOConfiguration ssoConfig,
+ SPSSODescriptor spDescriptor) {
+
+ if (session == null) {
+ authnRequest = null;
+ issuer = null;
+ }
+
+ authnRequest = (AuthnRequest) session
+ .getAttribute(AUTHNREQUEST_SESSION_KEY);
+ issuer = (Issuer) session.getAttribute(ISSUER_SESSION_KEY);
+ relyingParty = (RelyingPartyConfiguration) session
+ .getAttribute(RPCONFIG_SESSION_KEY);
+ ssoConfig = (SSOConfiguration) session
+ .getAttribute(SSOCONFIG_SESSION_KEY);
+ spDescriptor = (SPSSODescriptor) session
+ .getAttribute(SPSSODESC_SESSION_KEY);
+
+ session.removeAttribute(AUTHNREQUEST_SESSION_KEY);
+ session.removeAttribute(ISSUER_SESSION_KEY);
+ session.removeAttribute(RPCONFIG_SESSION_KEY);
+ session.removeAttribute(SSOCONFIG_SESSION_KEY);
+ session.removeAttribute(SPSSODESC_SESSION_KEY);
+ }
+
+ /**
+ * Check if the user has already been authenticated. If so, return the
+ * LoginContext. If not, redirect the user to the AuthenticationManager.
+ *
+ * @param authnRequest
+ * The SAML 2 AuthnRequest.
+ * @param httpSession
+ * The user's HttpSession.
+ * @param request
+ * The user's HttpServletRequest.
+ * @param response
+ * The user's HttpServletResponse.
+ *
+ * @return A LoginContext for the authenticated user.
+ *
+ * @throws SerlvetException
+ * on error.
+ */
+ protected void authenticateUser(final AuthnRequest authnRequest,
+ final HttpSession httpSession, final HttpServletRequest request,
+ final HttpServletResponse response) throws ServletException {
+
+ // Forward the request to the AuthenticationManager.
+ // When the AuthenticationManager is done it will
+ // forward the request back to this servlet.
+
+ Saml2LoginContext loginCtx = new Saml2LoginContext(authnRequest);
+ loginCtx.setProfileHandlerURL(request.getPathInfo());
+ httpSession.setAttribute(LoginContext.LOGIN_CONTEXT_KEY, loginCtx);
+ try {
+ RequestDispatcher dispatcher = request
+ .getRequestDispatcher(authnMgrURL);
+ dispatcher.forward(request, response);
+ } catch (IOException ex) {
+ log.error("Error forwarding SAML 2 AuthnRequest "
+ + authnRequest.getID() + " to AuthenticationManager", ex);
+ throw new ServletException("Error forwarding SAML 2 AuthnRequest "
+ + authnRequest.getID() + " to AuthenticationManager", ex);
+ }
+ }
+
+ /**
+ * Build an AuthnStatement and add it to a Response.
+ *
+ * @param response
+ * The Response to which the AuthnStatement will be added.
+ * @param loginCtx
+ * The LoginContext of the sucessfully authenticated user.
+ * @param authnRequest
+ * The AuthnRequest that prompted this message.
+ * @param ssoConfig
+ * The SSOConfiguration for the RP to which we are addressing
+ * this message.
+ * @param issuer
+ * The IdP's identifier.
+ * @param audiences
+ * An array of URIs restricting the audience of this assertion.
+ */
+ protected void setAuthenticationStatement(Assertion assertion,
+ final Saml2LoginContext loginContext,
+ final AuthnRequest authnRequest) throws ServletException {
+
+ // Build the AuthnCtx. We need to determine if the user was
+ // authenticated
+ // with an AuthnContextClassRef or a AuthnContextDeclRef
+ AuthnContext authnCtx = buildAuthnCtx(authnRequest
+ .getRequestedAuthnContext(), loginContext);
+ if (authnCtx == null) {
+ log.error("Error respond to SAML 2 AuthnRequest "
+ + authnRequest.getID()
+ + " : Unable to determine authentication method");
+ }
+
+ AuthnStatement stmt = authnStatementBuilder.buildObject();
+ stmt.setAuthnInstant(loginContext.getAuthenticationInstant());
+ stmt.setAuthnContext(authnCtx);
+
+ // add the AuthnStatement to the Assertion
+ List<AuthnStatement> authnStatements = assertion.getAuthnStatements();
+ authnStatements.add(stmt);
+ }
+
+ /**
+ * Create the AuthnContex object.
+ *
+ * To do this, we have to walk the AuthnRequest's RequestedAuthnContext
+ * object and compare any values we find to what's set in the loginContext.
+ *
+ * @param requestedAuthnCtx
+ * The RequestedAuthnContext from the Authentication Request.
+ * @param authnMethod
+ * The authentication method that was used.
+ *
+ * @return An AuthnCtx object on success or <code>null</code> on failure.
+ */
+ protected AuthnContext buildAuthnCtx(
+ final RequestedAuthnContext requestedAuthnCtx,
+ final Saml2LoginContext loginContext) {
+
+ // this method assumes that only one URI will match.
+
+ AuthnContext authnCtx = authnContextBuilder.buildObject();
+ String authnMethod = loginContext.getAuthenticationMethod();
+
+ List<AuthnContextClassRef> authnClasses = requestedAuthnCtx
+ .getAuthnContextClassRefs();
+ List<AuthnContextDeclRef> authnDeclRefs = requestedAuthnCtx
+ .getAuthnContextDeclRefs();
+
+ if (authnClasses != null) {
+ for (AuthnContextClassRef classRef : authnClasses) {
+ if (classRef != null) {
+ String s = classRef.getAuthnContextClassRef();
+ if (s != null && authnMethod.equals(s)) {
+ AuthnContextClassRef ref = authnContextClassRefBuilder
+ .buildObject();
+ authnCtx.setAuthnContextClassRef(ref);
+ return authnCtx;
+ }
+ }
+ }
+ }
+
+ // if no AuthnContextClassRef's matched, try the DeclRefs
+ if (authnDeclRefs != null) {
+ for (AuthnContextDeclRef declRef : authnDeclRefs) {
+ if (declRef != null) {
+ String s = declRef.getAuthnContextDeclRef();
+ if (s != null && authnMethod.equals((s))) {
+ AuthnContextDeclRef ref = authnContextDeclRefBuilder
+ .buildObject();
+ authnCtx.setAuthnContextDeclRef(ref);
+ return authnCtx;
+ }
+ }
+ }
+ }
+
+ // no matches were found.
+ return null;
+ }
+
+ /**
+ * Get the user's LoginContext.
+ *
+ * @param httpSession
+ * The user's HttpSession.
+ *
+ * @return The user's LoginContext.
+ *
+ * @throws ServletException
+ * On error.
+ */
+ protected Saml2LoginContext getLoginContext(final HttpSession httpSession)
+ throws ServletException {
+
+ Object o = httpSession.getAttribute(LoginContext.LOGIN_CONTEXT_KEY);
+ if (o == null) {
+ log.error("User's session does not contain a LoginContext object.");
+ throw new ServletException(
+ "User's session does not contain a LoginContext object.");
+ }
+
+ if (!(o instanceof Saml2LoginContext)) {
+ log
+ .error("Invalid login context object -- object is not an instance of Saml2LoginContext.");
+ throw new ServletException("Invalid login context object.");
+ }
+
+ Saml2LoginContext ctx = (Saml2LoginContext) o;
+
+ httpSession.removeAttribute(LoginContext.LOGIN_CONTEXT_KEY);
+
+ return ctx;
+ }
+
+ /**
+ * Verify the AuthnRequest is well-formed.
+ *
+ * @param authnRequest
+ * The user's SAML 2 AuthnRequest.
+ * @param issuer
+ * The Issuer of the AuthnRequest.
+ * @param relyingParty
+ * The relying party configuration for the request's originator.
+ * @param session
+ * The user's HttpSession.
+ *
+ * @throws AuthenticationRequestException
+ * on error.
+ */
+ protected void verifyAuthnRequest(final AuthnRequest authnRequest,
+ Issuer issuer, final RelyingPartyConfiguration relyingParty,
+ final HttpSession session) throws AuthenticationRequestException {
+
+ Status failureStatus;
+
+ // Check if we are in scope to handle this AuthnRequest
+ checkScope(authnRequest, issuer.getSPProvidedID());
+
+ // XXX: run signature checks on authnRequest
+
+ // verify that the AssertionConsumerService url is valid.
+ AssertionConsumerService acsEndpoint = getAndVerifyACSEndpoint(
+ authnRequest, relyingParty.getRelyingPartyId(),
+ getRelyingPartyConfigurationManager().getMetadataProvider());
+ session.setAttribute(ACS_SESSION_KEY, acsEndpoint);
+
+ // check for nameID constraints.
+ Subject subject = getAndVerifySubject(authnRequest);
+ }
+
+ /**
+ * Get and verify the Subject element.
+ *
+ * @param authnRequest
+ * The SAML 2 AuthnRequest.
+ *
+ * @return A Subject element.
+ *
+ * @throws AuthenticationRequestException
+ * on error.
+ */
+ protected Subject getAndVerifySubject(final AuthnRequest authnRequest)
+ throws AuthenticationRequestException {
+
+ Status failureStatus;
+
+ Subject subject = authnRequest.getSubject();
+
+ if (subject == null) {
+ failureStatus = buildStatus(StatusCode.REQUESTER_URI, null,
+ "SAML 2 AuthnRequest " + authnRequest.getID()
+ + " is malformed: It does not contain a Subject.");
+ throw new AuthenticationRequestException(
+ "AuthnRequest lacks a Subject", failureStatus);
+ }
+
+ // The Web Browser SSO profile disallows SubjectConfirmation
+ // methods in the requested subject.
+ List<SubjectConfirmation> confMethods = subject
+ .getSubjectConfirmations();
+ if (confMethods != null || confMethods.size() > 0) {
+ log
+ .error("SAML 2 AuthnRequest "
+ + authnRequest.getID()
+ + " is malformed: It contains SubjectConfirmation elements.");
+ failureStatus = buildStatus(
+ StatusCode.REQUESTER_URI,
+ null,
+ "SAML 2 AuthnRequest "
+ + authnRequest.getID()
+ + " is malformed: It contains SubjectConfirmation elements.");
+ throw new AuthenticationRequestException(
+ "AuthnRequest contains SubjectConfirmation elements",
+ failureStatus);
+ }
+
+ return subject;
+ }
+
+ /**
+ * Return the endpoint URL and protocol binding to use for the AuthnRequest.
+ *
+ * @param authnRequest
+ * The SAML 2 AuthnRequest.
+ * @param providerId
+ * The SP's providerId.
+ * @param metadata
+ * The appropriate Metadata.
+ *
+ * @return The AssertionConsumerService for the endpoint, or
+ * <code>null</code> on error.
+ *
+ * @throws AuthenticationRequestException
+ * On error.
+ */
+ protected AssertionConsumerService getAndVerifyACSEndpoint(
+ final AuthnRequest authnRequest, String providerId,
+ final MetadataProvider metadata)
+ throws AuthenticationRequestException {
+
+ Status failureStatus;
+
+ // Either the AssertionConsumerServiceIndex must be present
+ // or AssertionConsumerServiceURL must be present.
+
+ Integer idx = authnRequest.getAssertionConsumerServiceIndex();
+ String acsURL = authnRequest.getAssertionConsumerServiceURL();
+
+ if (idx != null && acsURL != null) {
+ log
+ .error("SAML 2 AuthnRequest "
+ + authnRequest.getID()
+ + " is malformed: It contains both an AssertionConsumerServiceIndex and an AssertionConsumerServiceURL");
+ failureStatus = buildStatus(
+ StatusCode.REQUESTER_URI,
+ null,
+ "SAML 2 AuthnRequest "
+ + authnRequest.getID()
+ + " is malformed: It contains both an AssertionConsumerServiceIndex and an AssertionConsumerServiceURL");
+ throw new AuthenticationRequestException("Malformed AuthnRequest",
+ failureStatus);
+ }
+
+ SPSSODescriptor spDescriptor;
+ try {
+ spDescriptor = metadata.getEntityDescriptor(providerId)
+ .getSPSSODescriptor(SAML20_PROTOCOL_URI);
+ } catch (MetadataProviderException ex) {
+ log.error(
+ "Unable retrieve SPSSODescriptor metadata for providerId "
+ + providerId
+ + " while processing SAML 2 AuthnRequest "
+ + authnRequest.getID(), ex);
+ failureStatus = buildStatus(StatusCode.RESPONDER_URI, null,
+ "Unable to locate metadata for " + providerId);
+ throw new AuthenticationRequestException(
+ "Unable to locate metadata", ex, failureStatus);
+ }
+
+ List<AssertionConsumerService> acsList = spDescriptor
+ .getAssertionConsumerServices();
+
+ // if the ACS index is specified, retrieve it from the metadata
+ if (idx != null) {
+
+ int i = idx.intValue();
+
+ // if the index is out of range, return an appropriate error.
+ if (i > acsList.size()) {
+ log.error("Illegal AssertionConsumerIndex specicifed (" + i
+ + ") in SAML 2 AuthnRequest " + authnRequest.getID());
+
+ failureStatus = buildStatus(StatusCode.REQUESTER_URI, null,
+ "Illegal AssertionConsumerIndex specicifed (" + i
+ + ") in SAML 2 AuthnRequest "
+ + authnRequest.getID());
+
+ throw new AuthenticationRequestException(
+ "Illegal AssertionConsumerIndex in AuthnRequest",
+ failureStatus);
+ }
+
+ return acsList.get(i);
+ }
+
+ // if the ACS endpoint is specified, validate it against the metadata
+ String protocolBinding = authnRequest.getProtocolBinding();
+ for (AssertionConsumerService acs : acsList) {
+ if (acsURL.equals(acs.getLocation())) {
+ if (protocolBinding != null) {
+ if (protocolBinding.equals(acs.getBinding())) {
+ return acs;
+ }
+ }
+ }
+ }
+
+ log
+ .error("Error processing SAML 2 AuthnRequest message "
+ + authnRequest.getID()
+ + ": Unable to validate AssertionConsumerServiceURL against metadata: "
+ + acsURL + " for binding " + protocolBinding);
+
+ failureStatus = buildStatus(StatusCode.REQUESTER_URI, null,
+ "Unable to validate AssertionConsumerService against metadata.");
+
+ throw new AuthenticationRequestException(
+ "SAML 2 AuthenticationRequest: Unable to validate AssertionConsumerService against Metadata",
+ failureStatus);
+ }
+
+ /**
+ * Retrieve a parsed AssertionConsumerService endpoint from the user's
+ * session.
+ *
+ * @param session
+ * The user's HttpSession.
+ *
+ * @return An AssertionConsumerServiceEndpoint object.
+ *
+ * @throws ServletException
+ * On error.
+ */
+ protected AssertionConsumerService getACSEndpointFromSession(
+ final HttpSession session) throws ServletException {
+
+ Object o = session.getAttribute(ACS_SESSION_KEY);
+ if (o == null) {
+ log
+ .error("User's session does not contain an AssertionConsumerService object.");
+ throw new ServletException(
+ "User's session does not contain an AssertionConsumerService object.");
+ }
+
+ if (!(o instanceof AssertionConsumerService)) {
+ log
+ .error("Invalid session data -- object is not an instance of AssertionConsumerService.");
+ throw new ServletException(
+ "Invalid session data -- object is not an instance of AssertionConsumerService.");
+ }
+
+ AssertionConsumerService endpoint = (AssertionConsumerService) o;
+
+ session.removeAttribute(ACS_SESSION_KEY);
+
+ return endpoint;
+ }
+
+ /**
+ * Check if an {@link AuthnRequest} contains a {@link Scoping} element. If
+ * so, check if the specified IdP is in the {@link IDPList} element. If no
+ * Scoping element is present, this method returns <code>true</code>.
+ *
+ * @param authnRequest
+ * The {@link AuthnRequest} element to check.
+ * @param providerId
+ * The IdP's ProviderID.
+ *
+ * @throws AuthenticationRequestException
+ * on error.
+ */
+ protected void checkScope(final AuthnRequest authnRequest, String providerId)
+ throws AuthenticationRequestException {
+
+ Status failureStatus;
+
+ List<String> idpEntries = new LinkedList<String>();
+
+ Scoping scoping = authnRequest.getScoping();
+ if (scoping == null) {
+ return;
+ }
+
+ // process all of the explicitly listed idp provider ids
+ IDPList idpList = scoping.getIDPList();
+ if (idpList == null) {
+ return;
+ }
+
+ List<IDPEntry> explicitIDPEntries = idpList.getIDPEntrys();
+ if (explicitIDPEntries != null) {
+ for (IDPEntry entry : explicitIDPEntries) {
+ String s = entry.getProviderID();
+ if (s != null) {
+ idpEntries.add(s);
+ }
+ }
+ }
+
+ // If the IDPList is incomplete, retrieve the complete list
+ // and add the entries to idpEntries.
+ GetComplete getComplete = idpList.getGetComplete();
+ IDPList referencedIdPs = getCompleteIDPList(getComplete);
+ if (referencedIdPs != null) {
+ List<IDPEntry> referencedIDPEntries = referencedIdPs.getIDPEntrys();
+ if (referencedIDPEntries != null) {
+ for (IDPEntry entry : referencedIDPEntries) {
+ String s = entry.getProviderID();
+ if (s != null) {
+ idpEntries.add(s);
+ }
+ }
+ }
+ }
+
+ // iterate over all the IDPEntries we've gathered,
+ // and check if we're in scope.
+ for (String requestProviderId : idpEntries) {
+ if (providerId.equals(requestProviderId)) {
+ log.debug("Found Scoping match for IdP: (" + providerId + ")");
+ return;
+ }
+ }
+
+ log.error("SAML 2 AuthnRequest " + authnRequest.getID()
+ + " contains a Scoping element which "
+ + "does not contain a providerID registered with this IdP.");
+
+ failureStatus = buildStatus(StatusCode.RESPONDER_URI,
+ StatusCode.NO_SUPPORTED_IDP_URI, null);
+ throw new AuthenticationRequestException(
+ "Unrecognized providerID in Scoping element", failureStatus);
+ }
+
+ /**
+ * Retrieve an incomplete IDPlist.
+ *
+ * This only handles URL-based <GetComplete/> references.
+ *
+ * @param getComplete
+ * The (possibly <code>null</code>) <GetComplete/>
+ * element
+ *
+ * @return an {@link IDPList} or <code>null</code> if the uri can't be
+ * dereferenced.
+ */
+ protected IDPList getCompleteIDPList(final GetComplete getComplete) {
+
+ // XXX: enhance this method to cache the url and last-modified-header
+
+ if (getComplete == null) {
+ return null;
+ }
+
+ String uri = getComplete.getGetComplete();
+ if (uri != null) {
+ return null;
+ }
+
+ IDPList idpList = null;
+ InputStream istream = null;
+
+ try {
+ URL url = new URL(uri);
+ URLConnection conn = url.openConnection();
+ istream = conn.getInputStream();
+
+ // convert the raw data into an XML object
+ Document doc = parserPool.parse(istream);
+ Element docElement = doc.getDocumentElement();
+ Unmarshaller unmarshaller = Configuration.getUnmarshallerFactory()
+ .getUnmarshaller(docElement);
+ idpList = (IDPList) unmarshaller.unmarshall(docElement);
+
+ } catch (MalformedURLException ex) {
+ log.error(
+ "Unable to retrieve GetComplete IDPList. Unsupported URI: "
+ + uri, ex);
+ } catch (IOException ex) {
+ log.error("IO Error while retreieving GetComplete IDPList from "
+ + uri, ex);
+ } catch (ConfigurationException ex) {
+ log.error(
+ "Internal OpenSAML error while parsing GetComplete IDPList from "
+ + uri, ex);
+ } catch (XMLParserException ex) {
+ log.error(
+ "Internal OpenSAML error while parsing GetComplete IDPList from "
+ + uri, ex);
+ } catch (UnmarshallingException ex) {
+ log.error(
+ "Internal OpenSAML error while unmarshalling GetComplete IDPList from "
+ + uri, ex);
+ } finally {
+
+ if (istream != null) {
+ try {
+ istream.close();
+ } catch (IOException ex) {
+ // pass
+ }
+ }
+ }
+
+ return idpList;
+ }
}
import java.util.Collection;
import org.joda.time.DateTime;
+
import org.opensaml.common.SAMLObjectBuilder;
import org.opensaml.common.SAMLVersion;
import org.opensaml.common.impl.SAMLObjectContentReference;
+
import org.opensaml.saml2.core.Advice;
import org.opensaml.saml2.core.Assertion;
import org.opensaml.saml2.core.Audience;
import org.opensaml.saml2.core.StatusMessage;
import org.opensaml.saml2.core.StatusResponseType;
import org.opensaml.saml2.core.Subject;
+
import org.opensaml.xml.XMLObjectBuilder;
import org.opensaml.xml.encryption.EncryptionException;
import org.opensaml.xml.security.credential.Credential;
* Common implementation details for profile handlers.
*/
public abstract class AbstractSAML2ProfileHandler extends AbstractSAMLProfileHandler {
-
+
+ /** SAML Version for this profile handler. */
+ public static final SAMLVersion SAML_VERSION = SAMLVersion.VERSION_20;
+
+ /** URI for the SAML 2 protocol. */
+ public static final String SAML20_PROTOCOL_URI = "urn:oasis:names:tc:SAML:2.0:protocol";
+
/** For building response. */
private SAMLObjectBuilder<Response> responseBuilder;
-
+
/** For building status. */
private SAMLObjectBuilder<Status> statusBuilder;
-
+
/** For building statuscode. */
private SAMLObjectBuilder<StatusCode> statusCodeBuilder;
-
+
/** For building StatusMessages. */
private SAMLObjectBuilder<StatusMessage> statusMessageBuilder;
-
+
/** For building assertion. */
private SAMLObjectBuilder<Assertion> assertionBuilder;
-
+
/** For building issuer. */
private SAMLObjectBuilder<Issuer> issuerBuilder;
-
+
/** For building subject. */
private SAMLObjectBuilder<Subject> subjectBuilder;
-
+
/** For building conditions. */
private SAMLObjectBuilder<Conditions> conditionsBuilder;
-
+
/** For building audience restriction. */
private SAMLObjectBuilder<AudienceRestriction> audienceRestrictionBuilder;
-
+
/** For building proxy retrictions. */
private SAMLObjectBuilder<ProxyRestriction> proxyRestrictionBuilder;
-
+
/** For building audience. */
private SAMLObjectBuilder<Audience> audienceBuilder;
-
+
/** For building advice. */
private SAMLObjectBuilder<Advice> adviceBuilder;
-
+
/** For building signature. */
private XMLObjectBuilder<Signature> signatureBuilder;
-
+
/** Constructor. */
@SuppressWarnings("unchecked")
protected AbstractSAML2ProfileHandler() {
+
super();
-
- responseBuilder = (SAMLObjectBuilder<Response>) getBuilderFactory().getBuilder(Response.DEFAULT_ELEMENT_NAME);
- statusBuilder = (SAMLObjectBuilder<Status>) getBuilderFactory().getBuilder(Status.DEFAULT_ELEMENT_NAME);
- statusCodeBuilder = (SAMLObjectBuilder<StatusCode>) getBuilderFactory().getBuilder(
- StatusCode.DEFAULT_ELEMENT_NAME);
- statusMessageBuilder = (SAMLObjectBuilder<StatusMessage>) getBuilderFactory().getBuilder(
- StatusMessage.DEFAULT_ELEMENT_NAME);
- issuerBuilder = (SAMLObjectBuilder<Issuer>) getBuilderFactory().getBuilder(Issuer.DEFAULT_ELEMENT_NAME);
- assertionBuilder = (SAMLObjectBuilder<Assertion>) getBuilderFactory()
- .getBuilder(Assertion.DEFAULT_ELEMENT_NAME);
- subjectBuilder = (SAMLObjectBuilder<Subject>) getBuilderFactory().getBuilder(Subject.DEFAULT_ELEMENT_NAME);
- conditionsBuilder = (SAMLObjectBuilder<Conditions>) getBuilderFactory().getBuilder(
- Conditions.DEFAULT_ELEMENT_NAME);
- audienceRestrictionBuilder = (SAMLObjectBuilder<AudienceRestriction>) getBuilderFactory().getBuilder(
- AudienceRestriction.DEFAULT_ELEMENT_NAME);
- proxyRestrictionBuilder = (SAMLObjectBuilder<ProxyRestriction>) getBuilderFactory().getBuilder(
- ProxyRestriction.DEFAULT_ELEMENT_NAME);
- audienceBuilder = (SAMLObjectBuilder<Audience>) getBuilderFactory().getBuilder(Audience.DEFAULT_ELEMENT_NAME);
- adviceBuilder = (SAMLObjectBuilder<Advice>) getBuilderFactory().getBuilder(Advice.DEFAULT_ELEMENT_NAME);
- signatureBuilder = (XMLObjectBuilder<Signature>) getBuilderFactory().getBuilder(Signature.DEFAULT_ELEMENT_NAME);
+
+ responseBuilder = (SAMLObjectBuilder<Response>) getBuilderFactory().getBuilder(Response.DEFAULT_ELEMENT_NAME);
+ statusBuilder = (SAMLObjectBuilder<Status>) getBuilderFactory().getBuilder(Status.DEFAULT_ELEMENT_NAME);
+ statusCodeBuilder = (SAMLObjectBuilder<StatusCode>) getBuilderFactory().getBuilder(StatusCode.DEFAULT_ELEMENT_NAME);
+ statusMessageBuilder = (SAMLObjectBuilder<StatusMessage>) getBuilderFactory().getBuilder(StatusMessage.DEFAULT_ELEMENT_NAME);
+ issuerBuilder = (SAMLObjectBuilder<Issuer>) getBuilderFactory().getBuilder(Issuer.DEFAULT_ELEMENT_NAME);
+ assertionBuilder = (SAMLObjectBuilder<Assertion>) getBuilderFactory().getBuilder(Assertion.DEFAULT_ELEMENT_NAME);
+ subjectBuilder = (SAMLObjectBuilder<Subject>) getBuilderFactory().getBuilder(Subject.DEFAULT_ELEMENT_NAME);
+ conditionsBuilder = (SAMLObjectBuilder<Conditions>) getBuilderFactory().getBuilder(Conditions.DEFAULT_ELEMENT_NAME);
+ audienceRestrictionBuilder = (SAMLObjectBuilder<AudienceRestriction>) getBuilderFactory().getBuilder(AudienceRestriction.DEFAULT_ELEMENT_NAME);
+ proxyRestrictionBuilder = (SAMLObjectBuilder<ProxyRestriction>) getBuilderFactory().getBuilder(ProxyRestriction.DEFAULT_ELEMENT_NAME);
+ audienceBuilder = (SAMLObjectBuilder<Audience>) getBuilderFactory().getBuilder(Audience.DEFAULT_ELEMENT_NAME);
+ adviceBuilder = (SAMLObjectBuilder<Advice>) getBuilderFactory().getBuilder(Advice.DEFAULT_ELEMENT_NAME);
+ signatureBuilder = (XMLObjectBuilder<Signature>) getBuilderFactory().getBuilder(Signature.DEFAULT_ELEMENT_NAME);
}
-
+
/**
* Convenience method for getting the SAML 2 advice builder.
- *
+ *
* @return SAML 2 advice builder
*/
public SAMLObjectBuilder<Advice> getAdviceBuilder() {
return adviceBuilder;
}
-
+
/**
* Convenience method for getting the SAML 2 assertion builder.
- *
+ *
* @return SAML 2 assertion builder
*/
public SAMLObjectBuilder<Assertion> getAssertionBuilder() {
return assertionBuilder;
}
-
+
/**
* Convenience method for getting the SAML 2 audience builder.
- *
+ *
* @return SAML 2 audience builder
*/
public SAMLObjectBuilder<Audience> getAudienceBuilder() {
return audienceBuilder;
}
-
+
/**
* Convenience method for getting the SAML 2 audience restriction builder.
- *
+ *
* @return SAML 2 audience restriction builder
*/
public SAMLObjectBuilder<AudienceRestriction> getAudienceRestrictionBuilder() {
return audienceRestrictionBuilder;
}
-
+
/**
* Convenience method for getting the SAML 2 conditions builder.
- *
+ *
* @return SAML 2 conditions builder
*/
public SAMLObjectBuilder<Conditions> getConditionsBuilder() {
return conditionsBuilder;
}
-
+
/**
* Convenience method for getting the SAML 2 Issuer builder.
- *
+ *
* @return SAML 2 Issuer builder
*/
public SAMLObjectBuilder<Issuer> getIssuerBuilder() {
return issuerBuilder;
}
-
+
/**
* Convenience method for getting the SAML 2 proxy restriction builder.
- *
+ *
* @return SAML 2 proxy restriction builder
*/
public SAMLObjectBuilder<ProxyRestriction> getProxyRestrictionBuilder() {
return proxyRestrictionBuilder;
}
-
+
/**
* Convenience method for getting the SAML 2 response builder.
- *
+ *
* @return SAML 2 response builder
*/
public SAMLObjectBuilder<Response> getResponseBuilder() {
return responseBuilder;
}
-
+
/**
* Convenience method for getting the Signature builder.
- *
+ *
* @return signature builder
*/
public XMLObjectBuilder<Signature> getSignatureBuilder() {
return signatureBuilder;
}
-
+
/**
* Convenience method for getting the SAML 2 status builder.
- *
+ *
* @return SAML 2 status builder
*/
public SAMLObjectBuilder<Status> getStatusBuilder() {
return statusBuilder;
}
-
+
/**
* Convenience method for getting the SAML 2 status code builder.
- *
+ *
* @return SAML 2 status code builder
*/
public SAMLObjectBuilder<StatusCode> getStatusCodeBuilder() {
return statusCodeBuilder;
}
-
+
/**
* Convenience method for getting the SAML 2 status message builder.
- *
+ *
* @return SAML 2 status message builder
*/
public SAMLObjectBuilder<StatusMessage> getStatusMessageBuilder() {
return statusMessageBuilder;
}
-
+
/**
* Convenience method for getting the SAML 2 subject builder.
- *
+ *
* @return SAML 2 subject builder
*/
public SAMLObjectBuilder<Subject> getSubjectBuilder() {
return subjectBuilder;
}
-
+
/**
* Populates the response's id, in response to, issue instant, version, and issuer properties.
- *
+ *
* @param response the response to populate
* @param issueInstant timestamp to use as the issue instant for the response
* @param request the request that the response is for
*/
protected void populateStatusResponse(StatusResponseType response, DateTime issueInstant,
RequestAbstractType request, RelyingPartyConfiguration rpConfig) {
+
response.setID(getIdGenerator().generateIdentifier());
response.setInResponseTo(request.getID());
response.setIssueInstant(issueInstant);
response.setVersion(SAMLVersion.VERSION_20);
response.setIssuer(buildEntityIssuer(rpConfig));
}
-
+
/**
- * Builds a {@link Status} object populated with the given code and message.
- *
- * @param statusCode status code or null
- * @param statusMessage status message or null
- *
- * @return built status object
+ * Build a status message, with an optional second-level failure message.
+ *
+ * @param topLevelCode
+ * The top-level status code. Should be from saml-core-2.0-os,
+ * sec. 3.2.2.2
+ * @param secondLevelCode
+ * An optional second-level failure code. Should be from
+ * saml-core-2.0-is, sec 3.2.2.2. If null, no second-level Status
+ * element will be set.
+ * @param secondLevelFailureMessage
+ * An optional second-level failure message.
+ *
+ * @return a Status object.
*/
- protected Status buildStatus(String statusCode, String statusMessage) {
- Status status = getStatusBuilder().buildObject();
-
- String trimmedCode = DatatypeHelper.safeTrimOrNullString(statusCode);
- if (trimmedCode != null) {
- StatusCode code = getStatusCodeBuilder().buildObject();
- code.setValue(trimmedCode);
- status.setStatusCode(code);
+ protected Status buildStatus(String topLevelCode, String secondLevelCode,
+ String secondLevelFailureMessage) {
+
+ Status status = statusBuilder.buildObject();
+ StatusCode statusCode = statusCodeBuilder.buildObject();
+
+ statusCode.setValue(DatatypeHelper.safeTrimOrNullString(topLevelCode));
+ if (secondLevelCode != null) {
+ StatusCode secondLevelStatusCode = statusCodeBuilder.buildObject();
+ secondLevelStatusCode.setValue(DatatypeHelper.safeTrimOrNullString(secondLevelCode));
+ statusCode.setStatusCode(secondLevelStatusCode);
}
-
- String trimmedMessage = DatatypeHelper.safeTrimOrNullString(statusMessage);
- if (trimmedMessage != null) {
- StatusMessage message = getStatusMessageBuilder().buildObject();
- message.setMessage(trimmedMessage);
- status.setStatusMessage(message);
+
+ if (secondLevelFailureMessage != null) {
+ StatusMessage msg = statusMessageBuilder.buildObject();
+ msg.setMessage(secondLevelFailureMessage);
+ status.setStatusMessage(msg);
}
-
+
return status;
}
-
+
/**
* Builds a basic assertion with its id, issue instant, SAML version, issuer, subject, and conditions populated.
- *
+ *
* @param issueInstant time to use as assertion issue instant
* @param rpConfig the relying party configuration
* @param profileConfig current profile configuration
- *
+ *
* @return the built assertion
*/
- protected Assertion buildAssertion(DateTime issueInstant, RelyingPartyConfiguration rpConfig,
- AbstractSAML2ProfileConfiguration profileConfig) {
+ protected Assertion buildAssertion(final DateTime issueInstant, final RelyingPartyConfiguration rpConfig,
+ final AbstractSAML2ProfileConfiguration profileConfig) {
+
Assertion assertion = assertionBuilder.buildObject();
assertion.setID(getIdGenerator().generateIdentifier());
assertion.setIssueInstant(issueInstant);
assertion.setVersion(SAMLVersion.VERSION_20);
assertion.setIssuer(buildEntityIssuer(rpConfig));
//TODO assertion.setSubject(buildSubject());
-
+
Conditions conditions = buildConditions(issueInstant, profileConfig);
assertion.setConditions(conditions);
-
+
return assertion;
}
-
+
/**
* Builds an entity type Issuer populated with the correct provider Id for this relying party configuration.
- *
+ *
* @param rpConfig the relying party configuration
- *
+ *
* @return the built Issuer
*/
- protected Issuer buildEntityIssuer(RelyingPartyConfiguration rpConfig) {
+ protected Issuer buildEntityIssuer(final RelyingPartyConfiguration rpConfig) {
+
Issuer issuer = getIssuerBuilder().buildObject();
issuer.setFormat(Issuer.ENTITY);
issuer.setValue(rpConfig.getProviderId());
-
+
return issuer;
}
-
+
/**
* Builds the SAML subject for the user for the service provider.
- *
+ *
* @return SAML subject for the user for the service provider
- *
+ *
* @throws EncryptionException thrown if there is a problem encryption the subject's NameID
*/
protected Subject buildSubject() throws EncryptionException {
// TODO
return null;
}
-
+
/**
* Builds a SAML assertion condition set. The following fields are set; not before, not on or after, audience
* restrictions, and proxy restrictions.
- *
+ *
* @param issueInstant timestamp the assertion was created
* @param profileConfig current profile configuration
- *
+ *
* @return constructed conditions
*/
- private Conditions buildConditions(DateTime issueInstant, AbstractSAML2ProfileConfiguration profileConfig) {
+ private Conditions buildConditions(final DateTime issueInstant, final AbstractSAML2ProfileConfiguration profileConfig) {
+
Conditions conditions = conditionsBuilder.buildObject();
conditions.setNotBefore(issueInstant);
conditions.setNotOnOrAfter(issueInstant.plus(profileConfig.getAssertionLifetime()));
-
+
Collection<String> audiences;
-
+
// add audience restrictions
audiences = profileConfig.getAssertionAudiences();
if (audiences != null && audiences.size() > 0) {
AudienceRestriction audienceRestriction = audienceRestrictionBuilder.buildObject();
- Audience audience;
for (String audienceUri : audiences) {
- audience = audienceBuilder.buildObject();
+ Audience audience = audienceBuilder.buildObject();
audience.setAudienceURI(audienceUri);
audienceRestriction.getAudiences().add(audience);
}
conditions.getAudienceRestrictions().add(audienceRestriction);
}
-
+
// add proxy restrictions
audiences = profileConfig.getProxyAudiences();
if (audiences != null && audiences.size() > 0) {
audience.setAudienceURI(audienceUri);
proxyRestriction.getAudiences().add(audience);
}
-
+
proxyRestriction.setProxyCount(profileConfig.getProxyCount());
conditions.getConditions().add(proxyRestriction);
}
-
+
return conditions;
}
-
+
/**
* Signs the given assertion if either the current profile configuration or the relying party configuration contains
* signing credentials.
- *
+ *
* @param assertion assertion to sign
* @param rpConfig relying party configuration
* @param profileConfig current profile configuration
if (!profileConfig.getSignAssertions()) {
return;
}
-
+
Credential signatureCredential = profileConfig.getSigningCredential();
if (signatureCredential == null) {
signatureCredential = rpConfig.getDefaultSigningCredential();
}
-
+
if (signatureCredential == null) {
return;
}
-
+
SAMLObjectContentReference contentRef = new SAMLObjectContentReference(assertion);
Signature signature = signatureBuilder.buildObject(Signature.DEFAULT_ELEMENT_NAME);
signature.getContentReferences().add(contentRef);
assertion.setSignature(signature);
-
+
Signer.signObject(signature);
}
if (!profileConfig.getSignResponses()) {
return;
}
-
+
Credential signatureCredential = profileConfig.getSigningCredential();
if (signatureCredential == null) {
signatureCredential = rpConfig.getDefaultSigningCredential();
}
-
+
if (signatureCredential == null) {
return;
}
-
+
SAMLObjectContentReference contentRef = new SAMLObjectContentReference(response);
Signature signature = signatureBuilder.buildObject(Signature.DEFAULT_ELEMENT_NAME);
signature.getContentReferences().add(contentRef);
response.setSignature(signature);
-
+
Signer.signObject(signature);
}
-
+
// TODO encryption support
}
\ No newline at end of file