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

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


The following commit(s) were added to refs/heads/master by this push:
     new 2f71f20  FEDIZ-221 - Got basic logout functionality working for SAML 
SSO
2f71f20 is described below

commit 2f71f20100d4bbbbed7d4cb913023fc983288977
Author: Colm O hEigeartaigh <cohei...@apache.org>
AuthorDate: Wed Jul 11 16:51:33 2018 +0100

    FEDIZ-221 - Got basic logout functionality working for SAML SSO
---
 .../idp/beans/SigninParametersCacheAction.java     |  40 +++----
 .../idp/beans/samlsso/AuthnRequestParser.java      |   6 +-
 .../WEB-INF/flows/federation-validate-request.xml  |   2 +-
 .../webapp/WEB-INF/flows/saml-validate-request.xml |   3 +-
 .../apache/cxf/fediz/systests/samlsso/IdpTest.java | 119 ++++++++++++++++++++-
 5 files changed, 144 insertions(+), 26 deletions(-)

diff --git 
a/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/beans/SigninParametersCacheAction.java
 
b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/beans/SigninParametersCacheAction.java
index bbecc5a..ac69ce6 100644
--- 
a/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/beans/SigninParametersCacheAction.java
+++ 
b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/beans/SigninParametersCacheAction.java
@@ -62,10 +62,6 @@ public class SigninParametersCacheAction {
         if (value != null) {
             signinParams.put(IdpConstants.RETURN_ADDRESS, value);
         }
-        value = WebUtils.getAttributeFromFlowScope(context, 
IdpConstants.RETURN_ADDRESS);
-        if (value != null) {
-            signinParams.put(IdpConstants.RETURN_ADDRESS, value);
-        }
 
         if ("samlsso".equals(protocol)) {
             value = WebUtils.getAttributeFromFlowScope(context, 
IdpConstants.SAML_AUTHN_REQUEST);
@@ -132,18 +128,21 @@ public class SigninParametersCacheAction {
         }
     }
 
-    public void storeRPConfigInSession(RequestContext context) throws 
ProcessingException {
+    public void storeRPConfigInSession(RequestContext context, String 
replyAddress) throws ProcessingException {
 
-        String wtrealm = (String)WebUtils.getAttributeFromFlowScope(context, 
FederationConstants.PARAM_TREALM);
+        String realm = (String)WebUtils.getAttributeFromFlowScope(context, 
IdpConstants.REALM);
+        if (realm == null) {
+            realm = (String)WebUtils.getAttributeFromFlowScope(context, 
FederationConstants.PARAM_TREALM);
+        }
         Idp idpConfig = (Idp) WebUtils.getAttributeFromFlowScope(context, 
IdpConstants.IDP_CONFIG);
-        if (wtrealm == null || idpConfig == null) {
+        if (realm == null || idpConfig == null) {
             return;
         }
 
-        Application serviceConfig = idpConfig.findApplication(wtrealm);
+        Application serviceConfig = idpConfig.findApplication(realm);
         if (serviceConfig != null) {
             if (serviceConfig.getPassiveRequestorEndpoint() == null) {
-                String url = guessPassiveRequestorURL(context, wtrealm);
+                String url = guessPassiveRequestorURL(context, replyAddress, 
realm);
                 serviceConfig.setPassiveRequestorEndpoint(url);
             }
 
@@ -157,22 +156,25 @@ public class SigninParametersCacheAction {
                 WebUtils.putAttributeInExternalContext(context, 
ACTIVE_APPLICATIONS, realmConfigMap);
             }
 
-            if (realmConfigMap.get(wtrealm) == null) {
-                realmConfigMap.put(wtrealm, serviceConfig);
+            if (realmConfigMap.get(realm) == null) {
+                realmConfigMap.put(realm, serviceConfig);
             }
         }
     }
 
-    protected String guessPassiveRequestorURL(RequestContext context, String 
wtrealm) throws ProcessingException {
-        String url = (String)WebUtils.getAttributeFromFlowScope(context, 
FederationConstants.PARAM_REPLY);
-        try {
-            //basic check if the url is correctly formed
-            new URL(url);
-        } catch (Exception e) {
-            url = null;
+    protected String guessPassiveRequestorURL(RequestContext context, String 
replyAddress,
+                                              String realm) throws 
ProcessingException {
+        String url = replyAddress;
+        if (url != null) {
+            try {
+                //basic check if the url is correctly formed
+                new URL(url);
+            } catch (Exception e) {
+                url = null;
+            }
         }
         if (url == null) {
-            url = wtrealm;
+            url = realm;
             try {
                 //basic check if the url is correctly formed
                 new URL(url);
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 74fac7f..852926b 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
@@ -135,7 +135,7 @@ public class AuthnRequestParser {
                     X509Certificate validatingCert = 
                         getValidatingCertificate(idp, 
parsedRequest.getIssuer().getValue());
                     Crypto issuerCrypto = new CertificateStore(new 
X509Certificate[] {validatingCert});
-                    
validateAuthnRequestSignature(parsedRequest.getSignature(), issuerCrypto);
+                    validateRequestSignature(parsedRequest.getSignature(), 
issuerCrypto);
                 } else if (signature != null) {
                     // Check destination
                     checkDestination(context, parsedRequest);
@@ -339,9 +339,9 @@ public class AuthnRequestParser {
     }
 
     /**
-     * Validate the AuthnRequest signature
+     * Validate the AuthnRequest or LogoutRequest signature
      */
-    private void validateAuthnRequestSignature(
+    private void validateRequestSignature(
         Signature signature,
         Crypto sigCrypto
     ) throws WSSecurityException {
diff --git 
a/services/idp/src/main/webapp/WEB-INF/flows/federation-validate-request.xml 
b/services/idp/src/main/webapp/WEB-INF/flows/federation-validate-request.xml
index 0e24af6..7ab8967 100644
--- a/services/idp/src/main/webapp/WEB-INF/flows/federation-validate-request.xml
+++ b/services/idp/src/main/webapp/WEB-INF/flows/federation-validate-request.xml
@@ -167,7 +167,7 @@
             <evaluate 
expression="tokenSerializer.serialize(flowRequestContext, 
flowScope.rpTokenElement)"
                       result="flowScope.rpToken"/>
         </on-entry>
-        <evaluate 
expression="signinParametersCacheAction.storeRPConfigInSession(flowRequestContext)"
 />
+        <evaluate 
expression="signinParametersCacheAction.storeRPConfigInSession(flowRequestContext,
 flowScope.wreply)" />
         <transition to="isWReplyProvided" />
         <transition 
on-exception="org.apache.cxf.fediz.core.exception.ProcessingException" 
to="viewBadRequest" />
         <transition on-exception="java.lang.Throwable" 
to="scInternalServerError" />
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 2319ee6..616786b 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
@@ -164,6 +164,7 @@
             <set name="flowScope.idpToken" 
value="currentEvent.attributes.idpToken" />
             <set name="flowScope.saml_authn_request" 
value="currentEvent.attributes.saml_authn_request" />
             <set name="flowScope.RelayState" 
value="currentEvent.attributes.request_context" />
+            <set name="flowScope.consumerURL" 
value="currentEvent.attributes.return_address" />
         </transition>
         <transition on="viewBadRequest" to="viewBadRequest" />
         <transition on="scInternalServerError" to="scInternalServerError" />
@@ -177,7 +178,7 @@
             <evaluate 
expression="stsClientForRpAction.submit(flowRequestContext, flowScope.realm, 
flowScope.home_realm)"
                       result="flowScope.rpTokenElement"/>
         </on-entry>
-        <evaluate 
expression="signinParametersCacheAction.storeRPConfigInSession(flowRequestContext)"/>
+        <evaluate 
expression="signinParametersCacheAction.storeRPConfigInSession(flowRequestContext,
 flowScope.consumerURL)"/>
         <transition to="produceSAMLResponse" />
         <transition 
on-exception="org.apache.cxf.fediz.core.exception.ProcessingException" 
to="viewBadRequest" />
         <transition on-exception="java.lang.Throwable" 
to="scInternalServerError" />
diff --git 
a/systests/samlsso/src/test/java/org/apache/cxf/fediz/systests/samlsso/IdpTest.java
 
b/systests/samlsso/src/test/java/org/apache/cxf/fediz/systests/samlsso/IdpTest.java
index fcf824f..a2236c1 100644
--- 
a/systests/samlsso/src/test/java/org/apache/cxf/fediz/systests/samlsso/IdpTest.java
+++ 
b/systests/samlsso/src/test/java/org/apache/cxf/fediz/systests/samlsso/IdpTest.java
@@ -47,7 +47,9 @@ import com.gargoylesoftware.htmlunit.WebClient;
 import com.gargoylesoftware.htmlunit.WebRequest;
 import com.gargoylesoftware.htmlunit.html.DomElement;
 import com.gargoylesoftware.htmlunit.html.DomNodeList;
+import com.gargoylesoftware.htmlunit.html.HtmlForm;
 import com.gargoylesoftware.htmlunit.html.HtmlPage;
+import com.gargoylesoftware.htmlunit.html.HtmlSubmitInput;
 import com.gargoylesoftware.htmlunit.util.NameValuePair;
 import com.gargoylesoftware.htmlunit.xml.XmlPage;
 
@@ -83,6 +85,8 @@ import org.opensaml.saml.saml2.core.AuthnContextClassRef;
 import org.opensaml.saml.saml2.core.AuthnContextComparisonTypeEnumeration;
 import org.opensaml.saml.saml2.core.AuthnRequest;
 import org.opensaml.saml.saml2.core.Issuer;
+import org.opensaml.saml.saml2.core.LogoutRequest;
+import org.opensaml.saml.saml2.core.NameID;
 import org.opensaml.saml.saml2.core.NameIDPolicy;
 import org.opensaml.saml.saml2.core.RequestedAuthnContext;
 import org.opensaml.security.x509.BasicX509Credential;
@@ -1614,6 +1618,118 @@ public class IdpTest {
         webClient.close();
     }
 
+    @org.junit.Test
+    public void testIdPLogout() 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";
+        LogoutRequest logoutRequest =
+            
SamlpRequestComponentBuilder.createLogoutRequest(SAMLVersion.VERSION_20, 
issuer, destination,
+                                                             null, null, 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);
+        idpPage = webClient.getPage(logoutURL);
+        webClient.getOptions().setJavaScriptEnabled(true);
+
+        Assert.assertEquals("IDP SignOut Confirmation Response Page", 
idpPage.getTitleText());
+
+        HtmlForm form = 
idpPage.getFormByName("signoutconfirmationresponseform");
+        HtmlSubmitInput button = form.getInputByName("_eventId_submit");
+        button.click();
+
+        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
+        webClient = new WebClient();
+        webClient.setCookieManager(cookieManager);
+        webClient.getOptions().setUseInsecureSSL(true);
+        webClient.getOptions().setThrowExceptionOnFailingStatusCode(false);
+        idpPage = webClient.getPage(url);
+
+        Assert.assertEquals(401, idpPage.getWebResponse().getStatusCode());
+
+        webClient.close();
+    }
+
     private String encodeAuthnRequest(Element authnRequest) throws IOException 
{
         String requestMessage = DOM2Writer.nodeToString(authnRequest);
 
@@ -1623,7 +1739,7 @@ public class IdpTest {
         return Base64Utility.encode(deflatedBytes);
     }
 
-    private void signAuthnRequest(AuthnRequest authnRequest) throws Exception {
+    private void signAuthnRequest(SignableSAMLObject signableObject) throws 
Exception {
         Crypto crypto = CryptoFactory.getInstance("stsKeystoreA.properties");
 
         CryptoType cryptoType = new CryptoType(CryptoType.TYPE.ALIAS);
@@ -1655,7 +1771,6 @@ public class IdpTest {
                     "Error generating KeyInfo from signing credential", ex);
         }
 
-        SignableSAMLObject signableObject = (SignableSAMLObject) authnRequest;
         signableObject.setSignature(signature);
         signableObject.releaseDOM();
         signableObject.releaseChildrenDOM(true);

Reply via email to