You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cxf.apache.org by co...@apache.org on 2018/07/13 17:04:07 UTC

[cxf-fediz] branch 1.4.x-fixes updated (698cb07 -> d668514)

This is an automated email from the ASF dual-hosted git repository.

coheigea pushed a change to branch 1.4.x-fixes
in repository https://gitbox.apache.org/repos/asf/cxf-fediz.git.


    from 698cb07  Fixed a few grammatical things in the examples READMEs
     new d380248  FEDIZ-221 - Redirecting back to RP logout endpoint
     new e11a731  FEDIZ-221 - Check NonOnOrAfter with LogoutRequests
     new 0865db0  FEDIZ-221 - Add destination to LogoutResponse
     new d668514  FEDIZ-221 - Return errors in a LogoutResponse as well

The 4 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../idp/beans/samlsso/AuthnRequestParser.java      |  27 +++-
 .../idp/beans/samlsso/SamlResponseCreator.java     |   8 +-
 .../beans/samlsso/SamlResponseErrorCreator.java    |  48 ++++++--
 .../samlsso/SAML2PResponseComponentBuilder.java    |   4 +-
 .../service/idp/samlsso/SAMLLogoutRequest.java     |  11 ++
 .../webapp/WEB-INF/flows/saml-validate-request.xml |  40 ++++--
 .../main/webapp/WEB-INF/views/signoutresponse.jsp  |   2 +-
 .../org/apache/cxf/fediz/systests/idp/IdpTest.java | 137 ++++++++++++++++++++-
 .../src/test/resources/realma/entities-realma.xml  |   6 +-
 9 files changed, 253 insertions(+), 30 deletions(-)


[cxf-fediz] 02/04: FEDIZ-221 - Check NonOnOrAfter with LogoutRequests

Posted by co...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

coheigea pushed a commit to branch 1.4.x-fixes
in repository https://gitbox.apache.org/repos/asf/cxf-fediz.git

commit e11a731235dd6a4873c5a146fcd9fe5805843c6b
Author: Colm O hEigeartaigh <co...@apache.org>
AuthorDate: Fri Jul 13 15:57:22 2018 +0100

    FEDIZ-221 - Check NonOnOrAfter with LogoutRequests
---
 .../idp/beans/samlsso/AuthnRequestParser.java      |   5 +
 .../service/idp/samlsso/SAMLLogoutRequest.java     |  11 ++
 .../org/apache/cxf/fediz/systests/idp/IdpTest.java | 112 +++++++++++++++++++++
 3 files changed, 128 insertions(+)

diff --git a/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/AuthnRequestParser.java b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/AuthnRequestParser.java
index b120d89..4d5ce10 100644
--- a/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/AuthnRequestParser.java
+++ b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/AuthnRequestParser.java
@@ -26,6 +26,7 @@ import java.nio.charset.StandardCharsets;
 import java.security.cert.X509Certificate;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.Date;
 import java.util.List;
 
 import org.w3c.dom.Document;
@@ -121,6 +122,10 @@ public class AuthnRequestParser {
                 WebUtils.putAttributeInFlowScope(context, IdpConstants.SAML_AUTHN_REQUEST, authnRequest);
             } else if (parsedRequest instanceof LogoutRequest) {
                 SAMLLogoutRequest logoutRequest = new SAMLLogoutRequest((LogoutRequest)parsedRequest);
+                if (logoutRequest.getNotOnOrAfter() != null && (new Date()).after(logoutRequest.getNotOnOrAfter())) {
+                    LOG.debug("The LogoutRequest is expired");
+                    throw new ProcessingException(TYPE.BAD_REQUEST);
+                }
                 WebUtils.putAttributeInFlowScope(context, IdpConstants.SAML_LOGOUT_REQUEST, logoutRequest);
             }
 
diff --git a/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/samlsso/SAMLLogoutRequest.java b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/samlsso/SAMLLogoutRequest.java
index 9e47764..c894190 100644
--- a/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/samlsso/SAMLLogoutRequest.java
+++ b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/samlsso/SAMLLogoutRequest.java
@@ -19,6 +19,8 @@
 
 package org.apache.cxf.fediz.service.idp.samlsso;
 
+import java.util.Date;
+
 import org.opensaml.saml.saml2.core.LogoutRequest;
 
 /**
@@ -32,9 +34,13 @@ public class SAMLLogoutRequest extends SAMLAbstractRequest {
     private static final long serialVersionUID = 4353024755428346545L;
 
     private String subjectNameId;
+    private Date notOnOrAfter;
 
     public SAMLLogoutRequest(LogoutRequest logoutRequest) {
         super(logoutRequest);
+        if (logoutRequest.getNotOnOrAfter() != null) {
+            notOnOrAfter = logoutRequest.getNotOnOrAfter().toDate();
+        }
 
         if (logoutRequest.getNameID() != null) {
             subjectNameId = logoutRequest.getNameID().getValue();
@@ -44,4 +50,9 @@ public class SAMLLogoutRequest extends SAMLAbstractRequest {
     public String getSubjectNameId() {
         return subjectNameId;
     }
+
+    public Date getNotOnOrAfter() {
+        return notOnOrAfter;
+    }
+
 }
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 d3b5636..eaf25dd 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
@@ -31,6 +31,7 @@ import java.security.PrivateKey;
 import java.security.cert.X509Certificate;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Date;
 import java.util.UUID;
 
 import javax.servlet.ServletException;
@@ -1752,6 +1753,117 @@ public class IdpTest {
         webClient.close();
     }
 
+    @org.junit.Test
+    public void testIdpLogoutRequestExpired() throws Exception {
+        OpenSAMLUtil.initSamlEngine();
+
+        // 1. First let's login to the IdP
+
+        // Create SAML AuthnRequest
+        Document doc = DOMUtils.createDocument();
+        doc.appendChild(doc.createElement("root"));
+        // Create the AuthnRequest
+        String consumerURL = "https://localhost:" + getRpHttpsPort() + "/"
+            + getServletContextName() + "/secure/fedservlet";
+        AuthnRequest authnRequest =
+            new DefaultAuthnRequestBuilder().createAuthnRequest(
+                null, "urn:org:apache:cxf:fediz:fedizhelloworld", consumerURL
+            );
+        authnRequest.setDestination("https://localhost:" + getIdpHttpsPort() + "/fediz-idp/saml");
+        signAuthnRequest(authnRequest);
+
+        Element authnRequestElement = OpenSAMLUtil.toDom(authnRequest, doc);
+        String authnRequestEncoded = encodeAuthnRequest(authnRequestElement);
+
+        String urlEncodedRequest = URLEncoder.encode(authnRequestEncoded, "UTF-8");
+
+        String relayState = UUID.randomUUID().toString();
+        String url = "https://localhost:" + getIdpHttpsPort() + "/fediz-idp/saml?";
+        url += SSOConstants.RELAY_STATE + "=" + relayState;
+        url += "&" + SSOConstants.SAML_REQUEST + "=" + urlEncodedRequest;
+
+        String user = "alice";
+        String password = "ecila";
+
+        CookieManager cookieManager = new CookieManager();
+
+        WebClient webClient = new WebClient();
+        webClient.setCookieManager(cookieManager);
+        webClient.getOptions().setUseInsecureSSL(true);
+        webClient.getCredentialsProvider().setCredentials(
+            new AuthScope("localhost", Integer.parseInt(getIdpHttpsPort())),
+            new UsernamePasswordCredentials(user, password));
+
+        webClient.getOptions().setJavaScriptEnabled(false);
+        HtmlPage idpPage = webClient.getPage(url);
+        webClient.getOptions().setJavaScriptEnabled(true);
+        Assert.assertEquals("IDP SignIn Response Form", idpPage.getTitleText());
+
+        org.opensaml.saml.saml2.core.Response samlResponse =
+            parseSAMLResponse(idpPage, relayState, consumerURL, authnRequest.getID());
+        String expected = "urn:oasis:names:tc:SAML:2.0:status:Success";
+        Assert.assertEquals(expected, samlResponse.getStatus().getStatusCode().getValue());
+        NameID nameID = samlResponse.getAssertions().get(0).getSubject().getNameID();
+        Assert.assertNotNull(nameID);
+        nameID.detach();
+
+        webClient.close();
+
+        // 2. now we logout from IdP
+
+        // Create SAML LogoutRequest
+        doc = DOMUtils.createDocument();
+        doc.appendChild(doc.createElement("root"));
+
+        Issuer issuer = SamlpRequestComponentBuilder.createIssuer("urn:org:apache:cxf:fediz:fedizhelloworld");
+        String destination = "https://localhost:" + getIdpHttpsPort() + "/fediz-idp/saml";
+        Date now = new Date();
+        now.setTime(now.getTime() - (60L * 1000L));
+        LogoutRequest logoutRequest =
+            SamlpRequestComponentBuilder.createLogoutRequest(SAMLVersion.VERSION_20, issuer, destination,
+                                                             null, now, null, nameID);
+
+        signAuthnRequest(logoutRequest);
+
+        Element logoutRequestElement = OpenSAMLUtil.toDom(logoutRequest, doc);
+        String logoutRequestEncoded = encodeAuthnRequest(logoutRequestElement);
+
+        urlEncodedRequest = URLEncoder.encode(logoutRequestEncoded, "UTF-8");
+
+        relayState = UUID.randomUUID().toString();
+        String logoutURL = "https://localhost:" + getIdpHttpsPort() + "/fediz-idp/saml?";
+        logoutURL += SSOConstants.RELAY_STATE + "=" + relayState;
+        logoutURL += "&" + SSOConstants.SAML_REQUEST + "=" + urlEncodedRequest;
+
+        webClient = new WebClient();
+        webClient.setCookieManager(cookieManager);
+        webClient.getOptions().setUseInsecureSSL(true);
+        webClient.getCredentialsProvider().setCredentials(
+            new AuthScope("localhost", Integer.parseInt(getIdpHttpsPort())),
+            new UsernamePasswordCredentials(user, password));
+
+        webClient.getOptions().setJavaScriptEnabled(false);
+        try {
+            webClient.getPage(logoutURL);
+            Assert.fail("Authentication failure expected");
+        }  catch (FailingHttpStatusCodeException ex) {
+            Assert.assertEquals(ex.getStatusCode(), 400);
+        }
+        webClient.close();
+
+        // 3. now we try to access the idp without authentication but with the existing cookies
+        // to see if we are really logged out - it should work OK as our LogoutRequest was expired
+        webClient = new WebClient();
+        webClient.setCookieManager(cookieManager);
+        webClient.getOptions().setUseInsecureSSL(true);
+        webClient.getOptions().setThrowExceptionOnFailingStatusCode(false);
+        idpPage = webClient.getPage(url);
+
+        Assert.assertEquals(200, idpPage.getWebResponse().getStatusCode());
+
+        webClient.close();
+    }
+
     private String encodeAuthnRequest(Element authnRequest) throws IOException {
         String requestMessage = DOM2Writer.nodeToString(authnRequest);
 


[cxf-fediz] 01/04: FEDIZ-221 - Redirecting back to RP logout endpoint

Posted by co...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

coheigea pushed a commit to branch 1.4.x-fixes
in repository https://gitbox.apache.org/repos/asf/cxf-fediz.git

commit d380248927b3f4b0e8a57d059e2c5e0d8a1f9c67
Author: Colm O hEigeartaigh <co...@apache.org>
AuthorDate: Fri Jul 13 15:21:03 2018 +0100

    FEDIZ-221 - Redirecting back to RP logout endpoint
---
 .../idp/beans/samlsso/AuthnRequestParser.java      | 22 +++++++++++++++++++---
 .../webapp/WEB-INF/flows/saml-validate-request.xml | 14 +++++++++-----
 .../main/webapp/WEB-INF/views/signoutresponse.jsp  |  2 +-
 .../org/apache/cxf/fediz/systests/idp/IdpTest.java |  3 +++
 .../src/test/resources/realma/entities-realma.xml  |  6 ++++--
 5 files changed, 36 insertions(+), 11 deletions(-)

diff --git a/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/AuthnRequestParser.java b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/AuthnRequestParser.java
index b0730d3..b120d89 100644
--- a/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/AuthnRequestParser.java
+++ b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/AuthnRequestParser.java
@@ -178,6 +178,24 @@ public class AuthnRequestParser {
     }
 
     public String retrieveConsumerURL(RequestContext context) {
+        // If it's a LogoutRequest we just want to get the logout endpoint from the configuration
+        SAMLLogoutRequest logoutRequest =
+            (SAMLLogoutRequest)WebUtils.getAttributeFromFlowScope(context, IdpConstants.SAML_LOGOUT_REQUEST);
+        if (logoutRequest != null) {
+            Idp idpConfig = (Idp) WebUtils.getAttributeFromFlowScope(context, "idpConfig");
+            String realm = retrieveRealm(context);
+            Application serviceConfig = idpConfig.findApplication(realm);
+            if (serviceConfig != null) {
+                String logoutEndpoint = serviceConfig.getLogoutEndpoint();
+                if (logoutEndpoint != null) {
+                    LOG.debug("Attempting to use the configured logout endpoint: {}", logoutEndpoint);
+                    return logoutEndpoint;
+                }
+            }
+            LOG.debug("No LogoutEndpoint has been configured for this application");
+            return "/";
+        }
+
         SAMLAuthnRequest authnRequest =
             (SAMLAuthnRequest)WebUtils.getAttributeFromFlowScope(context, IdpConstants.SAML_AUTHN_REQUEST);
 
@@ -195,9 +213,7 @@ public class AuthnRequestParser {
         if (serviceConfig != null) {
             String racs = serviceConfig.getPassiveRequestorEndpoint();
             LOG.debug("Attempting to use the configured passive requestor endpoint instead: {}", racs);
-            if (racs != null) {
-                return racs;
-            }
+            return racs;
         }
 
         return null;
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 3122fcf..4e62885 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
@@ -223,7 +223,7 @@
     
     <view-state id="viewSignoutConfirmation" view="signoutconfirmationresponse">
         <transition on="submit" to="produceSAMLLogoutResponse"/>
-        <transition on="cancel" to="redirect" />
+        <!-- <transition on="cancel" to="redirect" /> -->
     </view-state>
     
     <action-state id="produceSAMLLogoutResponse">
@@ -238,17 +238,18 @@
         <transition to="invalidateSessionAction" />
     </action-state>
     
-    <action-state id="invalidateSessionAction">
+    <decision-state id="invalidateSessionAction">
         <on-entry>
             <!-- store the realmConfigMap in the request map before we invalidate the session below.
             Its needed in the signoutresponse.jsp page -->
             <set name="externalContext.requestMap.realmConfigMap" 
                 value="externalContext.sessionMap.realmConfigMap"/>
             <evaluate expression="homeRealmReminder.removeCookie(flowRequestContext)" />
+            <evaluate expression="logoutAction.submit(flowRequestContext)" />
         </on-entry>
-        <evaluate expression="logoutAction.submit(flowRequestContext)" />
-        <transition to="signOutFormResponseView" />
-    </action-state>
+        <if test="flowScope.idpConfig.isAutomaticRedirectToRpAfterLogout()"
+            then="signOutFormResponseView" else="showLogoutResponsePage" />
+    </decision-state>
     
     <!-- normal exit point for logout -->
     <!-- browser redirection (self-submitted form 'samlsignoutresponseform.jsp') -->
@@ -259,6 +260,9 @@
             <evaluate expression="flowScope.logoutResponse" result="requestScope.samlResponse" />
         </on-entry>
     </end-state>
+    
+    <!-- normal exit point for logout -->
+    <end-state id="showLogoutResponsePage" view="signoutresponse" />
 
     <!-- abnormal exit point -->
     <decision-state id="viewBadRequest">
diff --git a/services/idp/src/main/webapp/WEB-INF/views/signoutresponse.jsp b/services/idp/src/main/webapp/WEB-INF/views/signoutresponse.jsp
index 429c026..c75a7f1 100644
--- a/services/idp/src/main/webapp/WEB-INF/views/signoutresponse.jsp
+++ b/services/idp/src/main/webapp/WEB-INF/views/signoutresponse.jsp
@@ -33,7 +33,7 @@
             
             while (iterator.hasNext()) {
                 Application next = iterator.next().getValue();
-                if (next != null) {
+                if (next != null && "http://docs.oasis-open.org/wsfed/federation/200706".equals(next.getProtocol())) {
     %>
                     <%= next.getServiceDisplayName() %> 
                     <img src="<%=next.getPassiveRequestorEndpoint() + "?" + FederationConstants.PARAM_ACTION 
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 41ed6bc..d3b5636 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
@@ -1723,8 +1723,11 @@ public class IdpTest {
 
         // Check Response
         HtmlForm responseForm = signoutPage.getFormByName("samlsignoutresponseform");
+        Assert.assertEquals("https://localhost:8080/logout", responseForm.getActionAttribute());
         String responseValue = responseForm.getInputByName("SAMLResponse").getAttributeNS(null, "value");
         Assert.assertNotNull(responseValue);
+        String receivedRelayState = responseForm.getInputByName("RelayState").getAttributeNS(null, "value");
+        Assert.assertEquals(relayState, receivedRelayState);
 
         byte[] deflatedToken = Base64Utility.decode(responseValue);
         InputStream tokenStream = new ByteArrayInputStream(deflatedToken);
diff --git a/systests/samlsso/src/test/resources/realma/entities-realma.xml b/systests/samlsso/src/test/resources/realma/entities-realma.xml
index 2948e39..7bbc430 100644
--- a/systests/samlsso/src/test/resources/realma/entities-realma.xml
+++ b/systests/samlsso/src/test/resources/realma/entities-realma.xml
@@ -93,6 +93,7 @@
         <property name="stsUrl" value="https://localhost:${idp.https.port}/fediz-idp-sts/REALMA" />
         <property name="idpUrl" value="https://localhost:${idp.https.port}/fediz-idp/saml" />
         <property name="rpSingleSignOutConfirmation" value="true"/>
+        <property name="automaticRedirectToRpAfterLogout" value="true"/>
         <property name="supportedProtocols">
             <util:list>
                 <value>urn:oasis:names:tc:SAML:2.0:profiles:SSO:browser
@@ -151,12 +152,13 @@
 
     <bean id="srv-fedizhelloworld" class="org.apache.cxf.fediz.service.idp.service.jpa.ApplicationEntity">
         <property name="realm" value="urn:org:apache:cxf:fediz:fedizhelloworld" />
-        <property name="protocol" value="http://docs.oasis-open.org/wsfed/federation/200706" />
+        <property name="protocol" value="urn:oasis:names:tc:SAML:2.0:profiles:SSO:browser" />
         <property name="serviceDisplayName" value="Fedizhelloworld" />
-        <property name="serviceDescription" value="Web Application to illustrate WS-Federation" />
+        <property name="serviceDescription" value="Web Application to illustrate SAML SSO" />
         <property name="role" value="ApplicationServiceType" />
         <property name="tokenType" value="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0" />
         <property name="lifeTime" value="3600" />
+        <property name="logoutEndpoint" value="https://localhost:8080/logout" />
         <property name="passiveRequestorEndpointConstraint" 
                   value="https://localhost:(\d)*/(\w)*helloworld(\w)*/secure/.*" />
         <property name="validatingCertificate" value="realma.cert" />


[cxf-fediz] 04/04: FEDIZ-221 - Return errors in a LogoutResponse as well

Posted by co...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

coheigea pushed a commit to branch 1.4.x-fixes
in repository https://gitbox.apache.org/repos/asf/cxf-fediz.git

commit d668514a2349c5a5bc01a841228c15a7d4bb02cf
Author: Colm O hEigeartaigh <co...@apache.org>
AuthorDate: Fri Jul 13 17:48:03 2018 +0100

    FEDIZ-221 - Return errors in a LogoutResponse as well
---
 .../idp/beans/samlsso/AuthnRequestParser.java      |  2 +-
 .../beans/samlsso/SamlResponseErrorCreator.java    | 48 ++++++++++++++++++----
 .../webapp/WEB-INF/flows/saml-validate-request.xml | 23 +++++++++--
 .../org/apache/cxf/fediz/systests/idp/IdpTest.java | 32 ++++++++++++---
 4 files changed, 86 insertions(+), 19 deletions(-)

diff --git a/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/AuthnRequestParser.java b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/AuthnRequestParser.java
index 4d5ce10..68f808f 100644
--- a/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/AuthnRequestParser.java
+++ b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/AuthnRequestParser.java
@@ -122,11 +122,11 @@ public class AuthnRequestParser {
                 WebUtils.putAttributeInFlowScope(context, IdpConstants.SAML_AUTHN_REQUEST, authnRequest);
             } else if (parsedRequest instanceof LogoutRequest) {
                 SAMLLogoutRequest logoutRequest = new SAMLLogoutRequest((LogoutRequest)parsedRequest);
+                WebUtils.putAttributeInFlowScope(context, IdpConstants.SAML_LOGOUT_REQUEST, logoutRequest);
                 if (logoutRequest.getNotOnOrAfter() != null && (new Date()).after(logoutRequest.getNotOnOrAfter())) {
                     LOG.debug("The LogoutRequest is expired");
                     throw new ProcessingException(TYPE.BAD_REQUEST);
                 }
-                WebUtils.putAttributeInFlowScope(context, IdpConstants.SAML_LOGOUT_REQUEST, logoutRequest);
             }
 
             validateRequest(parsedRequest);
diff --git a/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/SamlResponseErrorCreator.java b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/SamlResponseErrorCreator.java
index 761883a..d201d0a 100644
--- a/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/SamlResponseErrorCreator.java
+++ b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/SamlResponseErrorCreator.java
@@ -33,6 +33,7 @@ import org.apache.cxf.helpers.DOMUtils;
 import org.apache.cxf.rs.security.saml.DeflateEncoderDecoder;
 import org.apache.wss4j.common.saml.OpenSAMLUtil;
 import org.apache.wss4j.common.util.DOM2Writer;
+import org.opensaml.saml.saml2.core.LogoutResponse;
 import org.opensaml.saml.saml2.core.Response;
 import org.opensaml.saml.saml2.core.Status;
 import org.slf4j.Logger;
@@ -48,25 +49,32 @@ public class SamlResponseErrorCreator {
 
     private static final Logger LOG = LoggerFactory.getLogger(SamlResponseErrorCreator.class);
     private boolean supportDeflateEncoding;
+    private boolean useRealmForIssuer;
 
-    public String createSAMLResponse(RequestContext context, boolean requestor,
-                                     Idp idp, String requestID) throws ProcessingException {
+    public String createSAMLResponse(RequestContext context, boolean logout, boolean requestor,
+                                     Idp idp, String requestID, String destination) throws ProcessingException {
         Document doc = DOMUtils.newDocument();
 
         String statusValue = "urn:oasis:names:tc:SAML:2.0:status:Responder";
         if (requestor) {
             statusValue = "urn:oasis:names:tc:SAML:2.0:status:Requester";
         }
+
         Status status =
             SAML2PResponseComponentBuilder.createStatus(statusValue, null);
-        Response response =
-            SAML2PResponseComponentBuilder.createSAMLResponse(requestID, idp.getRealm(), status);
-
+        Element responseElement = null;
         try {
-            Element policyElement = OpenSAMLUtil.toDom(response, doc);
-            doc.appendChild(policyElement);
+            if (logout) {
+                responseElement = createLogoutResponse(idp, statusValue, destination, requestID);
+            } else {
+                Response response =
+                    SAML2PResponseComponentBuilder.createSAMLResponse(requestID, idp.getRealm(), status);
+                Element policyElement = OpenSAMLUtil.toDom(response, doc);
+                doc.appendChild(policyElement);
+
+                responseElement = policyElement;
+            }
 
-            Element responseElement = policyElement;
             return encodeResponse(responseElement);
         } catch (Exception e) {
             LOG.warn("Error marshalling SAML Token: {}", e.getMessage());
@@ -74,6 +82,22 @@ public class SamlResponseErrorCreator {
         }
     }
 
+    protected Element createLogoutResponse(Idp idp, String statusValue,
+                                           String destination, String requestID) throws Exception {
+        Document doc = DOMUtils.newDocument();
+
+        Status status =
+            SAML2PResponseComponentBuilder.createStatus(statusValue, null);
+        String issuer = useRealmForIssuer ? idp.getRealm() : idp.getIdpUrl().toString();
+        LogoutResponse response =
+            SAML2PResponseComponentBuilder.createSAMLLogoutResponse(requestID, issuer, status, destination);
+
+        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);
@@ -95,4 +119,12 @@ public class SamlResponseErrorCreator {
     public void setSupportDeflateEncoding(boolean supportDeflateEncoding) {
         this.supportDeflateEncoding = supportDeflateEncoding;
     }
+
+    public boolean isUseRealmForIssuer() {
+        return useRealmForIssuer;
+    }
+
+    public void setUseRealmForIssuer(boolean useRealmForIssuer) {
+        this.useRealmForIssuer = useRealmForIssuer;
+    }
 }
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 61cdadc..ce6c253 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
@@ -272,8 +272,10 @@
                       result="requestScope.samlAction"/>
         </on-entry>
         <!-- See if we managed to at least parse the request to get the response URL -->
-        <if test="requestScope.samlAction == null or requestScope.samlAction.isEmpty()"
-            then="viewBadRequestParsingError" else="viewBadRequestResponse"/>
+        <if test="requestScope.samlAction == null or requestScope.samlAction.isEmpty() or requestScope.samlAction == '/'"
+            then="viewBadRequestParsingError"/>
+        <if test="flowScope.saml_logout_request != null"
+            then="viewBadLogoutRequestResponse" else="viewBadRequestResponse"/>
     </decision-state>
     
     <end-state id="viewBadRequestResponse" view="samlsigninresponseform">
@@ -283,8 +285,21 @@
             <evaluate expression="authnRequestParser.retrieveRequestId(flowRequestContext)" 
                       result="flowScope.requestId"/>
             <evaluate expression="flowScope.RelayState" result="requestScope.relayState" />
-            <evaluate expression="samlResponseErrorCreator.createSAMLResponse(flowRequestContext, true, flowScope.idpConfig, 
-                                                                     flowScope.requestId)"
+            <evaluate expression="samlResponseErrorCreator.createSAMLResponse(flowRequestContext, false,
+            	true, flowScope.idpConfig, flowScope.requestId, requestScope.samlAction)"
+                      result="requestScope.samlResponse"/>     
+        </on-entry>
+    </end-state>
+    
+    <end-state id="viewBadLogoutRequestResponse" view="samlsignoutresponseform">
+        <on-entry>
+            <evaluate expression="authnRequestParser.retrieveConsumerURL(flowRequestContext)" 
+                      result="requestScope.samlAction"/>
+            <evaluate expression="authnRequestParser.retrieveRequestId(flowRequestContext)" 
+                      result="flowScope.requestId"/>
+            <evaluate expression="flowScope.RelayState" result="requestScope.relayState" />
+            <evaluate expression="samlResponseErrorCreator.createSAMLResponse(flowRequestContext, true,
+            	true, flowScope.idpConfig, flowScope.requestId, requestScope.samlAction)"
                       result="requestScope.samlResponse"/>     
         </on-entry>
     </end-state>
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 9ab9f85..068724e 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
@@ -1737,6 +1737,10 @@ public class IdpTest {
         LogoutResponse logoutResponse = (LogoutResponse)OpenSAMLUtil.fromDom(responseDoc.getDocumentElement());
         Assert.assertNotNull(logoutResponse);
         Assert.assertEquals("https://localhost:8080/logout", logoutResponse.getDestination());
+        String expectedIssuer = "https://localhost:" + getIdpHttpsPort() + "/fediz-idp/saml";
+        Assert.assertEquals(expectedIssuer, logoutResponse.getIssuer().getValue());
+        String success = "urn:oasis:names:tc:SAML:2.0:status:Success";
+        Assert.assertEquals(success, logoutResponse.getStatus().getStatusCode().getValue());
 
         webClient.close();
 
@@ -1843,12 +1847,28 @@ public class IdpTest {
             new UsernamePasswordCredentials(user, password));
 
         webClient.getOptions().setJavaScriptEnabled(false);
-        try {
-            webClient.getPage(logoutURL);
-            Assert.fail("Authentication failure expected");
-        }  catch (FailingHttpStatusCodeException ex) {
-            Assert.assertEquals(ex.getStatusCode(), 400);
-        }
+        idpPage = webClient.getPage(logoutURL);
+        webClient.getOptions().setJavaScriptEnabled(true);
+
+        // Check Response
+        HtmlForm responseForm = idpPage.getFormByName("samlsignoutresponseform");
+        Assert.assertEquals("https://localhost:8080/logout", responseForm.getActionAttribute());
+        String responseValue = responseForm.getInputByName("SAMLResponse").getAttributeNS(null, "value");
+        Assert.assertNotNull(responseValue);
+        String receivedRelayState = responseForm.getInputByName("RelayState").getAttributeNS(null, "value");
+        Assert.assertEquals(relayState, receivedRelayState);
+
+        byte[] deflatedToken = Base64Utility.decode(responseValue);
+        InputStream tokenStream = new ByteArrayInputStream(deflatedToken);
+        Document responseDoc = StaxUtils.read(new InputStreamReader(tokenStream, StandardCharsets.UTF_8));
+
+        LogoutResponse logoutResponse = (LogoutResponse)OpenSAMLUtil.fromDom(responseDoc.getDocumentElement());
+        Assert.assertNotNull(logoutResponse);
+        Assert.assertEquals("https://localhost:8080/logout", logoutResponse.getDestination());
+        String expectedIssuer = "https://localhost:" + getIdpHttpsPort() + "/fediz-idp/saml";
+        Assert.assertEquals(expectedIssuer, logoutResponse.getIssuer().getValue());
+        String success = "urn:oasis:names:tc:SAML:2.0:status:Requester";
+        Assert.assertEquals(success, logoutResponse.getStatus().getStatusCode().getValue());
         webClient.close();
 
         // 3. now we try to access the idp without authentication but with the existing cookies


[cxf-fediz] 03/04: FEDIZ-221 - Add destination to LogoutResponse

Posted by co...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

coheigea pushed a commit to branch 1.4.x-fixes
in repository https://gitbox.apache.org/repos/asf/cxf-fediz.git

commit 0865db0fb836d588b080cd7729a1ae5062477436
Author: Colm O hEigeartaigh <co...@apache.org>
AuthorDate: Fri Jul 13 16:09:14 2018 +0100

    FEDIZ-221 - Add destination to LogoutResponse
---
 .../cxf/fediz/service/idp/beans/samlsso/SamlResponseCreator.java  | 8 ++++----
 .../fediz/service/idp/samlsso/SAML2PResponseComponentBuilder.java | 4 +++-
 .../idp/src/main/webapp/WEB-INF/flows/saml-validate-request.xml   | 3 ++-
 .../src/test/java/org/apache/cxf/fediz/systests/idp/IdpTest.java  | 4 ++--
 4 files changed, 11 insertions(+), 8 deletions(-)

diff --git a/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/SamlResponseCreator.java b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/SamlResponseCreator.java
index 9ca6b4b..426bbf7 100644
--- a/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/SamlResponseCreator.java
+++ b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/SamlResponseCreator.java
@@ -97,10 +97,10 @@ public class SamlResponseCreator {
         }
     }
 
-    public String createSAMLLogoutResponse(RequestContext context, Idp idp, String requestId)
+    public String createSAMLLogoutResponse(RequestContext context, Idp idp, String destination, String requestId)
                                          throws ProcessingException {
         try {
-            Element response = createLogoutResponse(idp, requestId);
+            Element response = createLogoutResponse(idp, destination, requestId);
             return encodeResponse(response);
         } catch (Exception ex) {
             LOG.warn("Error marshalling SAML Token: {}", ex.getMessage());
@@ -179,7 +179,7 @@ public class SamlResponseCreator {
         return policyElement;
     }
 
-    protected Element createLogoutResponse(Idp idp, String requestID) throws Exception {
+    protected Element createLogoutResponse(Idp idp, String destination, String requestID) throws Exception {
         Document doc = DOMUtils.newDocument();
 
         Status status =
@@ -188,7 +188,7 @@ public class SamlResponseCreator {
             );
         String issuer = useRealmForIssuer ? idp.getRealm() : idp.getIdpUrl().toString();
         LogoutResponse response =
-            SAML2PResponseComponentBuilder.createSAMLLogoutResponse(requestID, issuer, status);
+            SAML2PResponseComponentBuilder.createSAMLLogoutResponse(requestID, issuer, status, destination);
 
         Element policyElement = OpenSAMLUtil.toDom(response, doc);
         doc.appendChild(policyElement);
diff --git a/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/samlsso/SAML2PResponseComponentBuilder.java b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/samlsso/SAML2PResponseComponentBuilder.java
index 7e48340..bba31a8 100644
--- a/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/samlsso/SAML2PResponseComponentBuilder.java
+++ b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/samlsso/SAML2PResponseComponentBuilder.java
@@ -82,7 +82,8 @@ public final class SAML2PResponseComponentBuilder {
     public static LogoutResponse createSAMLLogoutResponse(
         String inResponseTo,
         String issuer,
-        Status status
+        Status status,
+        String destination
     ) {
         if (logoutResponseBuilder == null) {
             logoutResponseBuilder = (SAMLObjectBuilder<LogoutResponse>)
@@ -96,6 +97,7 @@ public final class SAML2PResponseComponentBuilder {
         response.setIssuer(createIssuer(issuer));
         response.setStatus(status);
         response.setVersion(SAMLVersion.VERSION_20);
+        response.setDestination(destination);
 
         return response;
     }
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 4e62885..61cdadc 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
@@ -233,7 +233,8 @@
             <evaluate expression="authnRequestParser.retrieveRequestIssuer(flowRequestContext)" 
                       result="flowScope.requestIssuer"/>
         </on-entry>
-        <evaluate expression="samlResponseCreator.createSAMLLogoutResponse(flowRequestContext, flowScope.idpConfig, flowScope.requestId)"
+        <evaluate expression="samlResponseCreator.createSAMLLogoutResponse(flowRequestContext, flowScope.idpConfig, 
+        		flowScope.consumerURL, flowScope.requestId)"
                   result="flowScope.logoutResponse"/>                                               
         <transition to="invalidateSessionAction" />
     </action-state>
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 eaf25dd..9ab9f85 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
@@ -1736,7 +1736,7 @@ public class IdpTest {
 
         LogoutResponse logoutResponse = (LogoutResponse)OpenSAMLUtil.fromDom(responseDoc.getDocumentElement());
         Assert.assertNotNull(logoutResponse);
-        // TODO further checks
+        Assert.assertEquals("https://localhost:8080/logout", logoutResponse.getDestination());
 
         webClient.close();
 
@@ -1852,7 +1852,7 @@ public class IdpTest {
         webClient.close();
 
         // 3. now we try to access the idp without authentication but with the existing cookies
-        // to see if we are really logged out - it should work OK as our LogoutRequest was expired
+        // to see if we are really logged out - we should still be logged in as our LogoutRequest was expired
         webClient = new WebClient();
         webClient.setCookieManager(cookieManager);
         webClient.getOptions().setUseInsecureSSL(true);