Repository: cxf Updated Branches: refs/heads/master b3288ed94 -> 1026909f9
[CXF-5362] Support for collocating RACS with endpoints Project: http://git-wip-us.apache.org/repos/asf/cxf/repo Commit: http://git-wip-us.apache.org/repos/asf/cxf/commit/1026909f Tree: http://git-wip-us.apache.org/repos/asf/cxf/tree/1026909f Diff: http://git-wip-us.apache.org/repos/asf/cxf/diff/1026909f Branch: refs/heads/master Commit: 1026909f918661c6d25080a9932f4d63af017a31 Parents: b3288ed Author: Sergey Beryozkin <[email protected]> Authored: Mon Mar 31 17:35:55 2014 +0100 Committer: Sergey Beryozkin <[email protected]> Committed: Mon Mar 31 17:35:55 2014 +0100 ---------------------------------------------------------------------- ...AbstractRequestAssertionConsumerHandler.java | 311 +++++++++++++++++++ .../saml/sso/AbstractServiceProviderFilter.java | 3 + .../sso/RequestAssertionConsumerFilter.java | 83 +++++ .../sso/RequestAssertionConsumerService.java | 292 +---------------- 4 files changed, 407 insertions(+), 282 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cxf/blob/1026909f/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/AbstractRequestAssertionConsumerHandler.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/AbstractRequestAssertionConsumerHandler.java b/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/AbstractRequestAssertionConsumerHandler.java new file mode 100644 index 0000000..75d634c --- /dev/null +++ b/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/AbstractRequestAssertionConsumerHandler.java @@ -0,0 +1,311 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.cxf.rs.security.saml.sso; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.UnsupportedEncodingException; +import java.util.Date; +import java.util.ResourceBundle; +import java.util.UUID; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.zip.DataFormatException; + +import javax.annotation.PreDestroy; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.Context; + +import org.w3c.dom.Document; + +import org.apache.cxf.Bus; +import org.apache.cxf.common.i18n.BundleUtils; +import org.apache.cxf.common.logging.LogUtils; +import org.apache.cxf.common.util.Base64Exception; +import org.apache.cxf.common.util.Base64Utility; +import org.apache.cxf.common.util.StringUtils; +import org.apache.cxf.jaxrs.ext.MessageContext; +import org.apache.cxf.jaxrs.utils.ExceptionUtils; +import org.apache.cxf.rs.security.saml.DeflateEncoderDecoder; +import org.apache.cxf.rs.security.saml.sso.state.RequestState; +import org.apache.cxf.rs.security.saml.sso.state.ResponseState; +import org.apache.cxf.staxutils.StaxUtils; +import org.apache.wss4j.common.ext.WSSecurityException; +import org.apache.wss4j.common.saml.OpenSAMLUtil; +import org.apache.wss4j.common.util.DOM2Writer; +import org.opensaml.xml.XMLObject; + +public abstract class AbstractRequestAssertionConsumerHandler extends AbstractSSOSpHandler { + private static final Logger LOG = + LogUtils.getL7dLogger(AbstractRequestAssertionConsumerHandler.class); + private static final ResourceBundle BUNDLE = + BundleUtils.getBundle(AbstractRequestAssertionConsumerHandler.class); + + private boolean supportDeflateEncoding = true; + private boolean supportBase64Encoding = true; + private boolean enforceAssertionsSigned = true; + private boolean enforceKnownIssuer = true; + private boolean keyInfoMustBeAvailable = true; + private TokenReplayCache<String> replayCache; + + private MessageContext messageContext; + + @Context + public void setMessageContext(MessageContext mc) { + this.messageContext = mc; + } + + public void setSupportDeflateEncoding(boolean deflate) { + supportDeflateEncoding = deflate; + } + public boolean isSupportDeflateEncoding() { + return supportDeflateEncoding; + } + + public void setReplayCache(TokenReplayCache<String> replayCache) { + this.replayCache = replayCache; + } + + public TokenReplayCache<String> getReplayCache() { + if (replayCache == null) { + Bus bus = (Bus)messageContext.getContextualProperty(Bus.class.getName()); + replayCache = new EHCacheTokenReplayCache(bus); + } + return replayCache; + } + + /** + * Enforce that Assertions must be signed if the POST binding was used. The default is true. + */ + public void setEnforceAssertionsSigned(boolean enforceAssertionsSigned) { + this.enforceAssertionsSigned = enforceAssertionsSigned; + } + + /** + * Enforce that the Issuer of the received Response/Assertion is known to this RACS. The + * default is true. + */ + public void setEnforceKnownIssuer(boolean enforceKnownIssuer) { + this.enforceKnownIssuer = enforceKnownIssuer; + } + + public void setSupportBase64Encoding(boolean supportBase64Encoding) { + this.supportBase64Encoding = supportBase64Encoding; + } + public boolean isSupportBase64Encoding() { + return supportBase64Encoding; + } + + @PreDestroy + @Override + public void close() { + if (replayCache != null) { + try { + replayCache.close(); + } catch (IOException ex) { + LOG.warning("Replay cache can not be closed: " + ex.getMessage()); + } + } + super.close(); + } + + protected String createSecurityContext(RequestState requestState, + String encodedSamlResponse, + String relayState, + boolean postBinding) { + + org.opensaml.saml2.core.Response samlResponse = + readSAMLResponse(postBinding, encodedSamlResponse); + + // Validate the Response + validateSamlResponseProtocol(samlResponse); + SSOValidatorResponse validatorResponse = + validateSamlSSOResponse(postBinding, samlResponse, requestState); + + // Set the security context + String securityContextKey = UUID.randomUUID().toString(); + + long currentTime = System.currentTimeMillis(); + Date notOnOrAfter = validatorResponse.getSessionNotOnOrAfter(); + long expiresAt = 0; + if (notOnOrAfter != null) { + expiresAt = notOnOrAfter.getTime(); + } else { + expiresAt = currentTime + getStateTimeToLive(); + } + + ResponseState responseState = + new ResponseState(validatorResponse.getAssertion(), + relayState, + requestState.getWebAppContext(), + requestState.getWebAppDomain(), + currentTime, + expiresAt); + getStateProvider().setResponseState(securityContextKey, responseState); + + String contextCookie = createCookie(SSOConstants.SECURITY_CONTEXT_TOKEN, + securityContextKey, + requestState.getWebAppContext(), + requestState.getWebAppDomain()); + + return contextCookie; + + } + + protected RequestState processRelayState(String relayState) { + if (relayState == null) { + reportError("MISSING_RELAY_STATE"); + throw ExceptionUtils.toBadRequestException(null, null); + } + if (relayState.getBytes().length < 0 || relayState.getBytes().length > 80) { + reportError("INVALID_RELAY_STATE"); + throw ExceptionUtils.toBadRequestException(null, null); + } + RequestState requestState = getStateProvider().removeRequestState(relayState); + if (requestState == null) { + reportError("MISSING_REQUEST_STATE"); + throw ExceptionUtils.toBadRequestException(null, null); + } + if (isStateExpired(requestState.getCreatedAt(), 0)) { + reportError("EXPIRED_REQUEST_STATE"); + throw ExceptionUtils.toBadRequestException(null, null); + } + return requestState; + } + + private org.opensaml.saml2.core.Response readSAMLResponse( + boolean postBinding, + String samlResponse + ) { + if (StringUtils.isEmpty(samlResponse)) { + reportError("MISSING_SAML_RESPONSE"); + throw ExceptionUtils.toBadRequestException(null, null); + } + + String samlResponseDecoded = samlResponse; + /* + // URL Decoding only applies for the re-direct binding + if (!postBinding) { + try { + samlResponseDecoded = URLDecoder.decode(samlResponse, "UTF-8"); + } catch (UnsupportedEncodingException e) { + throw ExceptionUtils.toBadRequestException(null, null); + } + } + */ + InputStream tokenStream = null; + if (isSupportBase64Encoding()) { + try { + byte[] deflatedToken = Base64Utility.decode(samlResponseDecoded); + tokenStream = !postBinding && isSupportDeflateEncoding() + ? new DeflateEncoderDecoder().inflateToken(deflatedToken) + : new ByteArrayInputStream(deflatedToken); + } catch (Base64Exception ex) { + throw ExceptionUtils.toBadRequestException(ex, null); + } catch (DataFormatException ex) { + throw ExceptionUtils.toBadRequestException(ex, null); + } + } else { + try { + tokenStream = new ByteArrayInputStream(samlResponseDecoded.getBytes("UTF-8")); + } catch (UnsupportedEncodingException ex) { + throw ExceptionUtils.toBadRequestException(ex, null); + } + } + + Document responseDoc = null; + try { + responseDoc = StaxUtils.read(new InputStreamReader(tokenStream, "UTF-8")); + } catch (Exception ex) { + throw new WebApplicationException(400); + } + + LOG.fine("Received response: " + DOM2Writer.nodeToString(responseDoc.getDocumentElement())); + + XMLObject responseObject = null; + try { + responseObject = OpenSAMLUtil.fromDom(responseDoc.getDocumentElement()); + } catch (WSSecurityException ex) { + throw ExceptionUtils.toBadRequestException(ex, null); + } + if (!(responseObject instanceof org.opensaml.saml2.core.Response)) { + throw ExceptionUtils.toBadRequestException(null, null); + } + return (org.opensaml.saml2.core.Response)responseObject; + } + + /** + * Validate the received SAML Response as per the protocol + */ + protected void validateSamlResponseProtocol( + org.opensaml.saml2.core.Response samlResponse + ) { + try { + SAMLProtocolResponseValidator protocolValidator = new SAMLProtocolResponseValidator(); + protocolValidator.setKeyInfoMustBeAvailable(keyInfoMustBeAvailable); + protocolValidator.validateSamlResponse(samlResponse, getSignatureCrypto(), getCallbackHandler()); + } catch (WSSecurityException ex) { + LOG.log(Level.FINE, ex.getMessage(), ex); + reportError("INVALID_SAML_RESPONSE"); + throw ExceptionUtils.toBadRequestException(null, null); + } + } + + /** + * Validate the received SAML Response as per the Web SSO profile + */ + protected SSOValidatorResponse validateSamlSSOResponse( + boolean postBinding, + org.opensaml.saml2.core.Response samlResponse, + RequestState requestState + ) { + try { + SAMLSSOResponseValidator ssoResponseValidator = new SAMLSSOResponseValidator(); + ssoResponseValidator.setAssertionConsumerURL( + messageContext.getUriInfo().getAbsolutePath().toString()); + + ssoResponseValidator.setClientAddress( + messageContext.getHttpServletRequest().getRemoteAddr()); + + ssoResponseValidator.setIssuerIDP(requestState.getIdpServiceAddress()); + ssoResponseValidator.setRequestId(requestState.getSamlRequestId()); + ssoResponseValidator.setSpIdentifier(requestState.getIssuerId()); + ssoResponseValidator.setEnforceAssertionsSigned(enforceAssertionsSigned); + ssoResponseValidator.setEnforceKnownIssuer(enforceKnownIssuer); + ssoResponseValidator.setReplayCache(getReplayCache()); + + return ssoResponseValidator.validateSamlResponse(samlResponse, postBinding); + } catch (WSSecurityException ex) { + reportError("INVALID_SAML_RESPONSE"); + throw ExceptionUtils.toBadRequestException(ex, null); + } + } + + protected void reportError(String code) { + org.apache.cxf.common.i18n.Message errorMsg = + new org.apache.cxf.common.i18n.Message(code, BUNDLE); + LOG.warning(errorMsg.toString()); + } + + public void setKeyInfoMustBeAvailable(boolean keyInfoMustBeAvailable) { + this.keyInfoMustBeAvailable = keyInfoMustBeAvailable; + } +} http://git-wip-us.apache.org/repos/asf/cxf/blob/1026909f/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/AbstractServiceProviderFilter.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/AbstractServiceProviderFilter.java b/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/AbstractServiceProviderFilter.java index 2d4ec04..597d6a8 100644 --- a/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/AbstractServiceProviderFilter.java +++ b/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/AbstractServiceProviderFilter.java @@ -31,6 +31,8 @@ import java.util.logging.Level; import java.util.logging.Logger; import javax.annotation.PreDestroy; +import javax.annotation.Priority; +import javax.ws.rs.Priorities; import javax.ws.rs.container.ContainerRequestFilter; import javax.ws.rs.container.PreMatching; import javax.ws.rs.core.Cookie; @@ -61,6 +63,7 @@ import org.apache.wss4j.common.saml.SamlAssertionWrapper; import org.opensaml.saml2.core.AuthnRequest; @PreMatching +@Priority(Priorities.AUTHENTICATION + 1) public abstract class AbstractServiceProviderFilter extends AbstractSSOSpHandler implements ContainerRequestFilter { http://git-wip-us.apache.org/repos/asf/cxf/blob/1026909f/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/RequestAssertionConsumerFilter.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/RequestAssertionConsumerFilter.java b/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/RequestAssertionConsumerFilter.java new file mode 100644 index 0000000..49f572d --- /dev/null +++ b/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/RequestAssertionConsumerFilter.java @@ -0,0 +1,83 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.cxf.rs.security.saml.sso; + +import java.io.IOException; + +import javax.annotation.Priority; +import javax.ws.rs.HttpMethod; +import javax.ws.rs.Priorities; +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerRequestFilter; +import javax.ws.rs.container.PreMatching; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.Response; + +import org.apache.cxf.helpers.IOUtils; +import org.apache.cxf.jaxrs.utils.JAXRSUtils; +import org.apache.cxf.rs.security.saml.sso.state.RequestState; + +@PreMatching +@Priority(Priorities.AUTHENTICATION) +public class RequestAssertionConsumerFilter extends AbstractRequestAssertionConsumerHandler + implements ContainerRequestFilter { + + @Override + public void filter(ContainerRequestContext ct) throws IOException { + String httpMethod = ct.getMethod(); + if (HttpMethod.GET.equals(httpMethod)) { + MultivaluedMap<String, String> params = ct.getUriInfo().getQueryParameters(); + processParams(ct, params, false); + } else if (HttpMethod.POST.equals(httpMethod) + && MediaType.APPLICATION_FORM_URLENCODED_TYPE.isCompatible(ct.getMediaType())) { + String strForm = IOUtils.toString(ct.getEntityStream()); + MultivaluedMap<String, String> params = JAXRSUtils.getStructuredParams(strForm, "&", false, false); + processParams(ct, params, true); + } else { + ct.abortWith(Response.status(400).build()); + } + + } + + protected void processParams(ContainerRequestContext ct, + MultivaluedMap<String, String> params, + boolean postBinding) { + String encodedSamlResponse = params.getFirst(SSOConstants.SAML_RESPONSE); + String relayState = params.getFirst(SSOConstants.RELAY_STATE); + RequestState requestState = processRelayState(relayState); + String targetUri = requestState.getTargetAddress(); + if (targetUri != null + && targetUri.startsWith(ct.getUriInfo().getRequestUri().toString())) { + reportError("INVALID_TARGET_URI"); + ct.abortWith(Response.status(400).build()); + return; + } + + + String contextCookie = createSecurityContext(requestState, + encodedSamlResponse, + relayState, + postBinding); + ct.getHeaders().add(HttpHeaders.COOKIE, contextCookie); + + } + +} http://git-wip-us.apache.org/repos/asf/cxf/blob/1026909f/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/RequestAssertionConsumerService.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/RequestAssertionConsumerService.java b/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/RequestAssertionConsumerService.java index 15c62c2..5b594e4 100644 --- a/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/RequestAssertionConsumerService.java +++ b/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/RequestAssertionConsumerService.java @@ -18,112 +18,22 @@ */ package org.apache.cxf.rs.security.saml.sso; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.UnsupportedEncodingException; import java.net.URI; -import java.util.Date; -import java.util.ResourceBundle; -import java.util.UUID; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.zip.DataFormatException; -import javax.annotation.PreDestroy; import javax.ws.rs.FormParam; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; -import org.w3c.dom.Document; - -import org.apache.cxf.Bus; -import org.apache.cxf.common.i18n.BundleUtils; -import org.apache.cxf.common.logging.LogUtils; -import org.apache.cxf.common.util.Base64Exception; -import org.apache.cxf.common.util.Base64Utility; -import org.apache.cxf.common.util.StringUtils; -import org.apache.cxf.jaxrs.ext.MessageContext; import org.apache.cxf.jaxrs.utils.ExceptionUtils; -import org.apache.cxf.rs.security.saml.DeflateEncoderDecoder; import org.apache.cxf.rs.security.saml.sso.state.RequestState; -import org.apache.cxf.rs.security.saml.sso.state.ResponseState; -import org.apache.cxf.staxutils.StaxUtils; -import org.apache.wss4j.common.ext.WSSecurityException; -import org.apache.wss4j.common.saml.OpenSAMLUtil; -import org.apache.wss4j.common.util.DOM2Writer; -import org.opensaml.xml.XMLObject; @Path("sso") -public class RequestAssertionConsumerService extends AbstractSSOSpHandler { - private static final Logger LOG = - LogUtils.getL7dLogger(RequestAssertionConsumerService.class); - private static final ResourceBundle BUNDLE = - BundleUtils.getBundle(RequestAssertionConsumerService.class); - - private boolean supportDeflateEncoding = true; - private boolean supportBase64Encoding = true; - private boolean enforceAssertionsSigned = true; - private boolean enforceKnownIssuer = true; - private boolean keyInfoMustBeAvailable = true; - private TokenReplayCache<String> replayCache; - - private MessageContext messageContext; - - @Context - public void setMessageContext(MessageContext mc) { - this.messageContext = mc; - } - - public void setSupportDeflateEncoding(boolean deflate) { - supportDeflateEncoding = deflate; - } - public boolean isSupportDeflateEncoding() { - return supportDeflateEncoding; - } - - public void setReplayCache(TokenReplayCache<String> replayCache) { - this.replayCache = replayCache; - } - - public TokenReplayCache<String> getReplayCache() { - if (replayCache == null) { - Bus bus = (Bus)messageContext.getContextualProperty(Bus.class.getName()); - replayCache = new EHCacheTokenReplayCache(bus); - } - return replayCache; - } - - /** - * Enforce that Assertions must be signed if the POST binding was used. The default is true. - */ - public void setEnforceAssertionsSigned(boolean enforceAssertionsSigned) { - this.enforceAssertionsSigned = enforceAssertionsSigned; - } - - /** - * Enforce that the Issuer of the received Response/Assertion is known to this RACS. The - * default is true. - */ - public void setEnforceKnownIssuer(boolean enforceKnownIssuer) { - this.enforceKnownIssuer = enforceKnownIssuer; - } - - public void setSupportBase64Encoding(boolean supportBase64Encoding) { - this.supportBase64Encoding = supportBase64Encoding; - } - public boolean isSupportBase64Encoding() { - return supportBase64Encoding; - } - +public class RequestAssertionConsumerService extends AbstractRequestAssertionConsumerHandler { @POST @Produces(MediaType.APPLICATION_FORM_URLENCODED) public Response processSamlResponse(@FormParam(SSOConstants.SAML_RESPONSE) String encodedSamlResponse, @@ -138,191 +48,19 @@ public class RequestAssertionConsumerService extends AbstractSSOSpHandler { return doProcessSamlResponse(encodedSamlResponse, relayState, false); } - @PreDestroy - @Override - public void close() { - if (replayCache != null) { - try { - replayCache.close(); - } catch (IOException ex) { - LOG.warning("Replay cache can not be closed: " + ex.getMessage()); - } - } - super.close(); - } - protected Response doProcessSamlResponse(String encodedSamlResponse, - String relayState, - boolean postBinding) { + String relayState, + boolean postBinding) { RequestState requestState = processRelayState(relayState); - URI targetURI = getTargetURI(requestState.getTargetAddress()); - - org.opensaml.saml2.core.Response samlResponse = - readSAMLResponse(postBinding, encodedSamlResponse); - - // Validate the Response - validateSamlResponseProtocol(samlResponse); - SSOValidatorResponse validatorResponse = - validateSamlSSOResponse(postBinding, samlResponse, requestState); - - // Set the security context - String securityContextKey = UUID.randomUUID().toString(); - - long currentTime = System.currentTimeMillis(); - Date notOnOrAfter = validatorResponse.getSessionNotOnOrAfter(); - long expiresAt = 0; - if (notOnOrAfter != null) { - expiresAt = notOnOrAfter.getTime(); - } else { - expiresAt = currentTime + getStateTimeToLive(); - } - - ResponseState responseState = - new ResponseState(validatorResponse.getAssertion(), - relayState, - requestState.getWebAppContext(), - requestState.getWebAppDomain(), - currentTime, - expiresAt); - getStateProvider().setResponseState(securityContextKey, responseState); - - String contextCookie = createCookie(SSOConstants.SECURITY_CONTEXT_TOKEN, - securityContextKey, - requestState.getWebAppContext(), - requestState.getWebAppDomain()); - + + String contextCookie = createSecurityContext(requestState, + encodedSamlResponse, + relayState, + postBinding); + // Finally, redirect to the service provider endpoint + URI targetURI = getTargetURI(requestState.getTargetAddress()); return Response.seeOther(targetURI).header("Set-Cookie", contextCookie).build(); - - } - - private RequestState processRelayState(String relayState) { - if (relayState == null) { - reportError("MISSING_RELAY_STATE"); - throw ExceptionUtils.toBadRequestException(null, null); - } - if (relayState.getBytes().length < 0 || relayState.getBytes().length > 80) { - reportError("INVALID_RELAY_STATE"); - throw ExceptionUtils.toBadRequestException(null, null); - } - RequestState requestState = getStateProvider().removeRequestState(relayState); - if (requestState == null) { - reportError("MISSING_REQUEST_STATE"); - throw ExceptionUtils.toBadRequestException(null, null); - } - if (isStateExpired(requestState.getCreatedAt(), 0)) { - reportError("EXPIRED_REQUEST_STATE"); - throw ExceptionUtils.toBadRequestException(null, null); - } - return requestState; - } - - private org.opensaml.saml2.core.Response readSAMLResponse( - boolean postBinding, - String samlResponse - ) { - if (StringUtils.isEmpty(samlResponse)) { - reportError("MISSING_SAML_RESPONSE"); - throw ExceptionUtils.toBadRequestException(null, null); - } - - String samlResponseDecoded = samlResponse; - /* - // URL Decoding only applies for the re-direct binding - if (!postBinding) { - try { - samlResponseDecoded = URLDecoder.decode(samlResponse, "UTF-8"); - } catch (UnsupportedEncodingException e) { - throw ExceptionUtils.toBadRequestException(null, null); - } - } - */ - InputStream tokenStream = null; - if (isSupportBase64Encoding()) { - try { - byte[] deflatedToken = Base64Utility.decode(samlResponseDecoded); - tokenStream = !postBinding && isSupportDeflateEncoding() - ? new DeflateEncoderDecoder().inflateToken(deflatedToken) - : new ByteArrayInputStream(deflatedToken); - } catch (Base64Exception ex) { - throw ExceptionUtils.toBadRequestException(ex, null); - } catch (DataFormatException ex) { - throw ExceptionUtils.toBadRequestException(ex, null); - } - } else { - try { - tokenStream = new ByteArrayInputStream(samlResponseDecoded.getBytes("UTF-8")); - } catch (UnsupportedEncodingException ex) { - throw ExceptionUtils.toBadRequestException(ex, null); - } - } - - Document responseDoc = null; - try { - responseDoc = StaxUtils.read(new InputStreamReader(tokenStream, "UTF-8")); - } catch (Exception ex) { - throw new WebApplicationException(400); - } - - LOG.fine("Received response: " + DOM2Writer.nodeToString(responseDoc.getDocumentElement())); - - XMLObject responseObject = null; - try { - responseObject = OpenSAMLUtil.fromDom(responseDoc.getDocumentElement()); - } catch (WSSecurityException ex) { - throw ExceptionUtils.toBadRequestException(ex, null); - } - if (!(responseObject instanceof org.opensaml.saml2.core.Response)) { - throw ExceptionUtils.toBadRequestException(null, null); - } - return (org.opensaml.saml2.core.Response)responseObject; - } - - /** - * Validate the received SAML Response as per the protocol - */ - protected void validateSamlResponseProtocol( - org.opensaml.saml2.core.Response samlResponse - ) { - try { - SAMLProtocolResponseValidator protocolValidator = new SAMLProtocolResponseValidator(); - protocolValidator.setKeyInfoMustBeAvailable(keyInfoMustBeAvailable); - protocolValidator.validateSamlResponse(samlResponse, getSignatureCrypto(), getCallbackHandler()); - } catch (WSSecurityException ex) { - LOG.log(Level.FINE, ex.getMessage(), ex); - reportError("INVALID_SAML_RESPONSE"); - throw ExceptionUtils.toBadRequestException(null, null); - } - } - - /** - * Validate the received SAML Response as per the Web SSO profile - */ - protected SSOValidatorResponse validateSamlSSOResponse( - boolean postBinding, - org.opensaml.saml2.core.Response samlResponse, - RequestState requestState - ) { - try { - SAMLSSOResponseValidator ssoResponseValidator = new SAMLSSOResponseValidator(); - ssoResponseValidator.setAssertionConsumerURL( - messageContext.getUriInfo().getAbsolutePath().toString()); - - ssoResponseValidator.setClientAddress( - messageContext.getHttpServletRequest().getRemoteAddr()); - - ssoResponseValidator.setIssuerIDP(requestState.getIdpServiceAddress()); - ssoResponseValidator.setRequestId(requestState.getSamlRequestId()); - ssoResponseValidator.setSpIdentifier(requestState.getIssuerId()); - ssoResponseValidator.setEnforceAssertionsSigned(enforceAssertionsSigned); - ssoResponseValidator.setEnforceKnownIssuer(enforceKnownIssuer); - ssoResponseValidator.setReplayCache(getReplayCache()); - - return ssoResponseValidator.validateSamlResponse(samlResponse, postBinding); - } catch (WSSecurityException ex) { - reportError("INVALID_SAML_RESPONSE"); - throw ExceptionUtils.toBadRequestException(ex, null); - } } private URI getTargetURI(String targetAddress) { @@ -337,14 +75,4 @@ public class RequestAssertionConsumerService extends AbstractSSOSpHandler { } throw ExceptionUtils.toBadRequestException(null, null); } - - private void reportError(String code) { - org.apache.cxf.common.i18n.Message errorMsg = - new org.apache.cxf.common.i18n.Message(code, BUNDLE); - LOG.warning(errorMsg.toString()); - } - - public void setKeyInfoMustBeAvailable(boolean keyInfoMustBeAvailable) { - this.keyInfoMustBeAvailable = keyInfoMustBeAvailable; - } }
