Create a proper SAML Response for SAML SSO
Project: http://git-wip-us.apache.org/repos/asf/cxf-fediz/repo Commit: http://git-wip-us.apache.org/repos/asf/cxf-fediz/commit/dcbbe664 Tree: http://git-wip-us.apache.org/repos/asf/cxf-fediz/tree/dcbbe664 Diff: http://git-wip-us.apache.org/repos/asf/cxf-fediz/diff/dcbbe664 Branch: refs/heads/master Commit: dcbbe664c01d78ca73b3a045292c5adce2d8d3e5 Parents: 5d8fb36 Author: Colm O hEigeartaigh <cohei...@apache.org> Authored: Fri Mar 18 16:50:29 2016 +0000 Committer: Colm O hEigeartaigh <cohei...@apache.org> Committed: Fri Mar 18 16:50:29 2016 +0000 ---------------------------------------------------------------------- .../idp/beans/samlsso/AuthnRequestParser.java | 28 +++ .../idp/beans/samlsso/SamlResponseCreator.java | 204 +++++++++++++++++++ .../idp/samlsso/SAML2CallbackHandler.java | 124 +++++++++++ .../samlsso/SAML2PResponseComponentBuilder.java | 127 ++++++++++++ .../WEB-INF/flows/saml-validate-request.xml | 17 +- .../apache/cxf/fediz/systests/idp/IdpTest.java | 16 +- 6 files changed, 510 insertions(+), 6 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/dcbbe664/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/AuthnRequestParser.java ---------------------------------------------------------------------- diff --git a/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/AuthnRequestParser.java b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/AuthnRequestParser.java index 52bfecd..db8a013 100644 --- a/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/AuthnRequestParser.java +++ b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/AuthnRequestParser.java @@ -89,6 +89,34 @@ public class AuthnRequestParser { return null; } + public String retrieveRequestId(RequestContext context) { + AuthnRequest authnRequest = + (AuthnRequest)WebUtils.getAttributeFromFlowScope(context, IdpConstants.SAML_AUTHN_REQUEST); + + if (authnRequest != null && authnRequest.getID() != null) { + String id = authnRequest.getID(); + LOG.debug("Parsed SAML AuthnRequest Id: {}", id); + return id; + } + + LOG.debug("No AuthnRequest available to be parsed"); + return null; + } + + public String retrieveRequestIssuer(RequestContext context) { + AuthnRequest authnRequest = + (AuthnRequest)WebUtils.getAttributeFromFlowScope(context, IdpConstants.SAML_AUTHN_REQUEST); + + if (authnRequest != null && authnRequest.getIssuer() != null) { + String issuer = authnRequest.getIssuer().getValue(); + LOG.debug("Parsed SAML AuthnRequest Issuer: {}", issuer); + return issuer; + } + + LOG.debug("No AuthnRequest available to be parsed"); + return null; + } + private AuthnRequest extractRequest(String samlRequest) throws Exception { byte[] deflatedToken = Base64Utility.decode(samlRequest); InputStream tokenStream = new DeflateEncoderDecoder().inflateToken(deflatedToken); http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/dcbbe664/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/SamlResponseCreator.java ---------------------------------------------------------------------- diff --git a/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/SamlResponseCreator.java b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/SamlResponseCreator.java new file mode 100644 index 0000000..2d8da15 --- /dev/null +++ b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/SamlResponseCreator.java @@ -0,0 +1,204 @@ +/** + * 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.fediz.service.idp.beans.samlsso; + +import java.io.IOException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.Collections; +import java.util.List; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import org.apache.cxf.common.util.Base64Utility; +import org.apache.cxf.fediz.core.exception.ProcessingException; +import org.apache.cxf.fediz.core.exception.ProcessingException.TYPE; +import org.apache.cxf.fediz.core.util.CertsUtils; +import org.apache.cxf.fediz.service.idp.domain.Idp; +import org.apache.cxf.fediz.service.idp.samlsso.SAML2CallbackHandler; +import org.apache.cxf.fediz.service.idp.samlsso.SAML2PResponseComponentBuilder; +import org.apache.cxf.fediz.service.idp.util.WebUtils; +import org.apache.cxf.helpers.DOMUtils; +import org.apache.cxf.rs.security.saml.DeflateEncoderDecoder; +import org.apache.wss4j.common.crypto.CertificateStore; +import org.apache.wss4j.common.crypto.Crypto; +import org.apache.wss4j.common.saml.OpenSAMLUtil; +import org.apache.wss4j.common.saml.SAMLCallback; +import org.apache.wss4j.common.saml.SAMLUtil; +import org.apache.wss4j.common.saml.SamlAssertionWrapper; +import org.apache.wss4j.common.saml.bean.AudienceRestrictionBean; +import org.apache.wss4j.common.saml.bean.ConditionsBean; +import org.apache.wss4j.common.saml.bean.SubjectConfirmationDataBean; +import org.apache.wss4j.common.util.DOM2Writer; +import org.apache.wss4j.dom.WSConstants; +import org.joda.time.DateTime; +import org.opensaml.saml.saml2.core.AttributeStatement; +import org.opensaml.saml.saml2.core.Response; +import org.opensaml.saml.saml2.core.Status; +import org.opensaml.saml.saml2.core.Subject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; +import org.springframework.webflow.execution.RequestContext; + +/** + * Insert the SAML Token received from the STS into a SAML Response + */ +@Component +public class SamlResponseCreator { + + private static final Logger LOG = LoggerFactory.getLogger(SamlResponseCreator.class); + private boolean supportDeflateEncoding; + + public String createSAMLResponse(RequestContext context, Idp idp, Element rpToken, + String consumerURL, String requestId, String requestIssuer) + throws ProcessingException { + List<Element> samlTokens = + DOMUtils.findAllElementsByTagNameNS(rpToken, WSConstants.SAML2_NS, "Assertion"); + if (samlTokens.isEmpty() || samlTokens.size() != 1) { + throw new ProcessingException(TYPE.BAD_REQUEST); + } + + try { + SamlAssertionWrapper wrapper = new SamlAssertionWrapper(samlTokens.get(0)); + Subject subject = wrapper.getSaml2().getSubject(); + List<AttributeStatement> attributeStatements = wrapper.getSaml2().getAttributeStatements(); + + Element response = createResponse(context, idp, requestId, consumerURL, requestIssuer, + subject, attributeStatements); + return encodeResponse(response); + } catch (Exception ex) { + ex.printStackTrace(); + LOG.warn("Error marshalling SAML Token: {}", ex.getMessage()); + throw new ProcessingException(TYPE.BAD_REQUEST); + } + } + + protected Element createResponse(RequestContext context, Idp idp, String requestID, + String racs, String requestIssuer, + Subject subject, + List<AttributeStatement> attributeStatements) throws Exception { + DocumentBuilderFactory docBuilderFactory; + docBuilderFactory = DocumentBuilderFactory.newInstance(); + docBuilderFactory.setNamespaceAware(true); + + DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder(); + Document doc = docBuilder.newDocument(); + + Status status = + SAML2PResponseComponentBuilder.createStatus( + "urn:oasis:names:tc:SAML:2.0:status:Success", null + ); + Response response = + SAML2PResponseComponentBuilder.createSAMLResponse(requestID, idp.getRealm(), status); + + // Create an AuthenticationAssertion + SAML2CallbackHandler callbackHandler = new SAML2CallbackHandler(); + callbackHandler.setIssuer(idp.getRealm()); + callbackHandler.setSubject(subject); + + // Subject Confirmation Data + SubjectConfirmationDataBean subjectConfirmationData = new SubjectConfirmationDataBean(); + subjectConfirmationData.setAddress(WebUtils.getHttpServletRequest(context).getRemoteAddr()); + subjectConfirmationData.setInResponseTo(requestID); + subjectConfirmationData.setNotAfter(new DateTime().plusMinutes(5)); + subjectConfirmationData.setRecipient(racs); + callbackHandler.setSubjectConfirmationData(subjectConfirmationData); + + // Audience Restriction + ConditionsBean conditions = new ConditionsBean(); + conditions.setTokenPeriodMinutes(5); + + AudienceRestrictionBean audienceRestriction = new AudienceRestrictionBean(); + audienceRestriction.setAudienceURIs(Collections.singletonList(requestIssuer)); + conditions.setAudienceRestrictions(Collections.singletonList(audienceRestriction)); + callbackHandler.setConditions(conditions); + + SAMLCallback samlCallback = new SAMLCallback(); + SAMLUtil.doSAMLCallback(callbackHandler, samlCallback); + SamlAssertionWrapper assertion = new SamlAssertionWrapper(samlCallback); + + Crypto issuerCrypto = getCrypto(idp.getCertificate()); + assertion.signAssertion(issuerCrypto.getDefaultX509Identifier(), idp.getCertificatePassword(), + issuerCrypto, false); + + response.getAssertions().add(assertion.getSaml2()); + + Element policyElement = OpenSAMLUtil.toDom(response, doc); + doc.appendChild(policyElement); + + return policyElement; + } + + protected String encodeResponse(Element response) throws IOException { + String responseMessage = DOM2Writer.nodeToString(response); + LOG.debug("Created Response: {}", responseMessage); + + if (supportDeflateEncoding) { + DeflateEncoderDecoder encoder = new DeflateEncoderDecoder(); + byte[] deflatedBytes = encoder.deflateToken(responseMessage.getBytes("UTF-8")); + + return Base64Utility.encode(deflatedBytes); + } + + return Base64Utility.encode(responseMessage.getBytes()); + } + + private Crypto getCrypto(String certificate) throws ProcessingException { + if (certificate == null) { + return null; + } + + boolean isCertificateLocation = !certificate.startsWith("-----BEGIN CERTIFICATE"); + if (isCertificateLocation) { + try { + X509Certificate cert = CertsUtils.getX509Certificate(certificate); + if (cert == null) { + return null; + } + return new CertificateStore(new X509Certificate[]{cert}); + } catch (CertificateException ex) { + // Maybe it's a WSS4J properties file... + return CertsUtils.createCrypto(certificate); + } + } + + // Here the certificate is encoded in the configuration file + X509Certificate cert; + try { + cert = CertsUtils.parseCertificate(certificate); + } catch (Exception ex) { + LOG.error("Failed to parse trusted certificate", ex); + throw new ProcessingException("Failed to parse trusted certificate"); + } + return new CertificateStore(Collections.singletonList(cert).toArray(new X509Certificate[0])); + } + + public boolean isSupportDeflateEncoding() { + return supportDeflateEncoding; + } + + public void setSupportDeflateEncoding(boolean supportDeflateEncoding) { + this.supportDeflateEncoding = supportDeflateEncoding; + } +} http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/dcbbe664/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/samlsso/SAML2CallbackHandler.java ---------------------------------------------------------------------- diff --git a/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/samlsso/SAML2CallbackHandler.java b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/samlsso/SAML2CallbackHandler.java new file mode 100644 index 0000000..1475dd4 --- /dev/null +++ b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/samlsso/SAML2CallbackHandler.java @@ -0,0 +1,124 @@ +/** + * 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.fediz.service.idp.samlsso; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.UnsupportedCallbackException; + +import org.apache.wss4j.common.saml.SAMLCallback; +import org.apache.wss4j.common.saml.bean.AttributeBean; +import org.apache.wss4j.common.saml.bean.AttributeStatementBean; +import org.apache.wss4j.common.saml.bean.AuthenticationStatementBean; +import org.apache.wss4j.common.saml.bean.ConditionsBean; +import org.apache.wss4j.common.saml.bean.SubjectBean; +import org.apache.wss4j.common.saml.bean.SubjectConfirmationDataBean; +import org.apache.wss4j.common.saml.bean.Version; +import org.apache.wss4j.common.saml.builder.SAML2Constants; +import org.opensaml.saml.saml2.core.Subject; + +/** + * A Callback Handler implementation for a SAML 2 assertion. By default it creates a SAML 2.0 Assertion with + * an AuthenticationStatement. If a list of roles are also supplied, it will insert them as part of an + * AttributeStatement. + */ +public class SAML2CallbackHandler implements CallbackHandler { + + private Subject subject; + private String confirmationMethod = SAML2Constants.CONF_BEARER; + private String issuer; + private ConditionsBean conditions; + private SubjectConfirmationDataBean subjectConfirmationData; + private List<Object> roles = new ArrayList<>(); + + private void createAndSetStatement(SAMLCallback callback) { + AuthenticationStatementBean authBean = new AuthenticationStatementBean(); + authBean.setAuthenticationMethod("Password"); + callback.setAuthenticationStatementData(Collections.singletonList(authBean)); + + if (!roles.isEmpty()) { + AttributeStatementBean attrBean = new AttributeStatementBean(); + AttributeBean attributeBean = new AttributeBean(); + attributeBean.setQualifiedName("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/role"); + attributeBean.setNameFormat(SAML2Constants.ATTRNAME_FORMAT_UNSPECIFIED); + attributeBean.setAttributeValues(roles); + + attrBean.setSamlAttributes(Collections.singletonList(attributeBean)); + callback.setAttributeStatementData(Collections.singletonList(attrBean)); + } + } + + public void handle(Callback[] callbacks) + throws IOException, UnsupportedCallbackException { + for (int i = 0; i < callbacks.length; i++) { + if (callbacks[i] instanceof SAMLCallback) { + SAMLCallback callback = (SAMLCallback) callbacks[i]; + callback.setSamlVersion(Version.SAML_20); + callback.setIssuer(issuer); + if (conditions != null) { + callback.setConditions(conditions); + } + + SubjectBean subjectBean = + new SubjectBean( + subject.getNameID().getValue(), subject.getNameID().getNameQualifier(), confirmationMethod + ); + subjectBean.setSubjectNameIDFormat(subject.getNameID().getFormat()); + subjectBean.setSubjectConfirmationData(subjectConfirmationData); + + callback.setSubject(subjectBean); + createAndSetStatement(callback); + } else { + throw new UnsupportedCallbackException(callbacks[i], "Unrecognized Callback"); + } + } + } + + public void setSubjectConfirmationData(SubjectConfirmationDataBean subjectConfirmationData) { + this.subjectConfirmationData = subjectConfirmationData; + } + + public void setConditions(ConditionsBean conditionsBean) { + this.conditions = conditionsBean; + } + + public void setConfirmationMethod(String confMethod) { + confirmationMethod = confMethod; + } + + public void setIssuer(String issuer) { + this.issuer = issuer; + } + + public Subject getSubject() { + return subject; + } + + public void setSubject(Subject subject) { + this.subject = subject; + } + + +} http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/dcbbe664/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/samlsso/SAML2PResponseComponentBuilder.java ---------------------------------------------------------------------- diff --git a/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/samlsso/SAML2PResponseComponentBuilder.java b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/samlsso/SAML2PResponseComponentBuilder.java new file mode 100644 index 0000000..7e64cfa --- /dev/null +++ b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/samlsso/SAML2PResponseComponentBuilder.java @@ -0,0 +1,127 @@ +/** + * 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.fediz.service.idp.samlsso; + +import java.util.UUID; + +import org.joda.time.DateTime; +import org.opensaml.core.xml.XMLObjectBuilderFactory; +import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport; +import org.opensaml.saml.common.SAMLObjectBuilder; +import org.opensaml.saml.common.SAMLVersion; +import org.opensaml.saml.saml2.core.Issuer; +import org.opensaml.saml.saml2.core.Response; +import org.opensaml.saml.saml2.core.Status; +import org.opensaml.saml.saml2.core.StatusCode; +import org.opensaml.saml.saml2.core.StatusMessage; + +/** +* A (basic) set of utility methods to construct SAML 2.0 Protocol Response statements +*/ +public final class SAML2PResponseComponentBuilder { + + private static SAMLObjectBuilder<Response> responseBuilder; + + private static SAMLObjectBuilder<Issuer> issuerBuilder; + + private static SAMLObjectBuilder<Status> statusBuilder; + + private static SAMLObjectBuilder<StatusCode> statusCodeBuilder; + + private static SAMLObjectBuilder<StatusMessage> statusMessageBuilder; + + private static XMLObjectBuilderFactory builderFactory = + XMLObjectProviderRegistrySupport.getBuilderFactory(); + + private SAML2PResponseComponentBuilder() { + + } + + @SuppressWarnings("unchecked") + public static Response createSAMLResponse( + String inResponseTo, + String issuer, + Status status + ) { + if (responseBuilder == null) { + responseBuilder = (SAMLObjectBuilder<Response>) + builderFactory.getBuilder(Response.DEFAULT_ELEMENT_NAME); + } + Response response = responseBuilder.buildObject(); + + response.setID(UUID.randomUUID().toString()); + response.setIssueInstant(new DateTime()); + response.setInResponseTo(inResponseTo); + response.setIssuer(createIssuer(issuer)); + response.setStatus(status); + response.setVersion(SAMLVersion.VERSION_20); + + return response; + } + + @SuppressWarnings("unchecked") + public static Issuer createIssuer( + String issuerValue + ) { + if (issuerBuilder == null) { + issuerBuilder = (SAMLObjectBuilder<Issuer>) + builderFactory.getBuilder(Issuer.DEFAULT_ELEMENT_NAME); + } + Issuer issuer = issuerBuilder.buildObject(); + issuer.setValue(issuerValue); + + return issuer; + } + + @SuppressWarnings("unchecked") + public static Status createStatus( + String statusCodeValue, + String statusMessage + ) { + if (statusBuilder == null) { + statusBuilder = (SAMLObjectBuilder<Status>) + builderFactory.getBuilder(Status.DEFAULT_ELEMENT_NAME); + } + if (statusCodeBuilder == null) { + statusCodeBuilder = (SAMLObjectBuilder<StatusCode>) + builderFactory.getBuilder(StatusCode.DEFAULT_ELEMENT_NAME); + } + if (statusMessageBuilder == null) { + statusMessageBuilder = (SAMLObjectBuilder<StatusMessage>) + builderFactory.getBuilder(StatusMessage.DEFAULT_ELEMENT_NAME); + } + + Status status = statusBuilder.buildObject(); + + StatusCode statusCode = statusCodeBuilder.buildObject(); + statusCode.setValue(statusCodeValue); + status.setStatusCode(statusCode); + + if (statusMessage != null) { + StatusMessage statusMessageObject = statusMessageBuilder.buildObject(); + statusMessageObject.setMessage(statusMessage); + status.setStatusMessage(statusMessageObject); + } + + return status; + } + + +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/dcbbe664/services/idp/src/main/webapp/WEB-INF/flows/saml-validate-request.xml ---------------------------------------------------------------------- diff --git a/services/idp/src/main/webapp/WEB-INF/flows/saml-validate-request.xml b/services/idp/src/main/webapp/WEB-INF/flows/saml-validate-request.xml index 1054cbb..701db0b 100644 --- a/services/idp/src/main/webapp/WEB-INF/flows/saml-validate-request.xml +++ b/services/idp/src/main/webapp/WEB-INF/flows/saml-validate-request.xml @@ -71,18 +71,31 @@ result="flowScope.consumerURL"/> </on-entry> <evaluate expression="signinParametersCacheAction.storeRPConfigInSession(flowRequestContext)"/> - <transition to="formResponseView" /> + <transition to="produceSAMLResponse" /> <transition on-exception="org.apache.cxf.fediz.core.exception.ProcessingException" to="viewBadRequest" /> <transition on-exception="java.lang.Throwable" to="scInternalServerError" /> </action-state> + <action-state id="produceSAMLResponse"> + <on-entry> + <evaluate expression="authnRequestParser.retrieveRequestId(flowRequestContext)" + result="flowScope.requestId"/> + <evaluate expression="authnRequestParser.retrieveRequestIssuer(flowRequestContext)" + result="flowScope.requestIssuer"/> + </on-entry> + <evaluate expression="samlResponseCreator.createSAMLResponse(flowRequestContext, flowScope.idpConfig, flowScope.rpTokenElement, + flowScope.consumerURL, flowScope.requestId, flowScope.requestIssuer)" + result="flowScope.rpResponse"/> + <transition to="formResponseView" /> + </action-state> + <!-- normal exit point for login --> <!-- browser redirection (self-submitted form 'samlsigninresponseform.jsp') --> <end-state id="formResponseView" view="samlsigninresponseform"> <on-entry> <evaluate expression="flowScope.consumerURL" result="requestScope.samlAction" /> <evaluate expression="flowScope.RelayState" result="requestScope.relayState" /> - <evaluate expression="flowScope.rpToken" result="requestScope.samlResponse" /> + <evaluate expression="flowScope.rpResponse" result="requestScope.samlResponse" /> </on-entry> </end-state> http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/dcbbe664/systests/samlsso/src/test/java/org/apache/cxf/fediz/systests/idp/IdpTest.java ---------------------------------------------------------------------- diff --git a/systests/samlsso/src/test/java/org/apache/cxf/fediz/systests/idp/IdpTest.java b/systests/samlsso/src/test/java/org/apache/cxf/fediz/systests/idp/IdpTest.java index ce6d541..c245bb1 100644 --- a/systests/samlsso/src/test/java/org/apache/cxf/fediz/systests/idp/IdpTest.java +++ b/systests/samlsso/src/test/java/org/apache/cxf/fediz/systests/idp/IdpTest.java @@ -19,8 +19,11 @@ package org.apache.cxf.fediz.systests.idp; +import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; import java.net.URLEncoder; import java.util.UUID; @@ -41,6 +44,7 @@ import org.apache.cxf.fediz.core.util.DOMUtils; import org.apache.cxf.rs.security.saml.DeflateEncoderDecoder; import org.apache.cxf.rs.security.saml.sso.DefaultAuthnRequestBuilder; import org.apache.cxf.rs.security.saml.sso.SSOConstants; +import org.apache.cxf.staxutils.StaxUtils; import org.apache.http.auth.AuthScope; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.wss4j.common.saml.OpenSAMLUtil; @@ -49,6 +53,7 @@ import org.apache.wss4j.dom.engine.WSSConfig; import org.junit.AfterClass; import org.junit.Assert; import org.junit.BeforeClass; +import org.opensaml.core.xml.XMLObject; import org.opensaml.saml.saml2.core.AuthnRequest; /** @@ -182,7 +187,7 @@ public class IdpTest { final HtmlPage idpPage = webClient.getPage(url); webClient.getOptions().setJavaScriptEnabled(true); Assert.assertEquals("IDP SignIn Response Form", idpPage.getTitleText()); - + // Parse the form to get the token (SAMLResponse) DomNodeList<DomElement> results = idpPage.getElementsByTagName("input"); @@ -208,8 +213,7 @@ public class IdpTest { String action = formResult.getAttributeNS(null, "action"); Assert.assertTrue(action.equals(consumerURL)); - /* - // Decode response + // Decode + verify response byte[] deflatedToken = Base64Utility.decode(samlResponse); InputStream inputStream = new ByteArrayInputStream(deflatedToken); @@ -217,7 +221,11 @@ public class IdpTest { XMLObject responseObject = OpenSAMLUtil.fromDom(responseDoc.getDocumentElement()); Assert.assertTrue((responseObject instanceof org.opensaml.saml.saml2.core.Response)); -*/ + + org.opensaml.saml.saml2.core.Response samlResponseObject = + (org.opensaml.saml.saml2.core.Response)responseObject; + Assert.assertTrue(authnRequest.getID().equals(samlResponseObject.getInResponseTo())); + webClient.close(); }