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/11 15:51:55 UTC
[cxf-fediz] branch master updated: FEDIZ-221 - Got basic logout
functionality working for SAML SSO
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 <co...@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);