[FEDIZ-165] - SAML SSO redirection on ForceAuthn or token expiry not working
Project: http://git-wip-us.apache.org/repos/asf/cxf-fediz/repo Commit: http://git-wip-us.apache.org/repos/asf/cxf-fediz/commit/a616dc6c Tree: http://git-wip-us.apache.org/repos/asf/cxf-fediz/tree/a616dc6c Diff: http://git-wip-us.apache.org/repos/asf/cxf-fediz/diff/a616dc6c Branch: refs/heads/master Commit: a616dc6ca15d38c82d3939cf6eb754c0b2da34c2 Parents: ec486bf Author: Colm O hEigeartaigh <[email protected]> Authored: Wed Apr 13 15:48:34 2016 +0100 Committer: Colm O hEigeartaigh <[email protected]> Committed: Wed Apr 13 15:48:34 2016 +0100 ---------------------------------------------------------------------- .../WEB-INF/flows/saml-validate-request.xml | 4 +- .../apache/cxf/fediz/systests/idp/IdpTest.java | 246 +++++++++++++++++++ 2 files changed, 249 insertions(+), 1 deletion(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/a616dc6c/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 4479a5b..6c156ac 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 @@ -147,10 +147,12 @@ <end-state id="redirectToLocalIDP" view="externalRedirect:${flowScope.localIdpUrl}"> <on-entry> + <evaluate expression="@java.net.URLEncoder@encode(flowScope.SAMLRequest)" + result="flowScope.encodedRequest"/> <set name="flowScope.localIdpUrl" value="flowScope.idpConfig.idpUrl +'?RelayState='+flowScope.RelayState - +'&SAMLRequest='+flowScope.SAMLRequest"> + +'&SAMLRequest='+flowScope.encodedRequest"> </set> </on-entry> </end-state> http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/a616dc6c/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 91a82d1..c8356ee 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 @@ -396,6 +396,176 @@ public class IdpTest { webClient.close(); } + @org.junit.Test + public void testSuccessfulSSOInvokeOnIdP() throws Exception { + OpenSAMLUtil.initSamlEngine(); + + // 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"; + + final WebClient webClient = new WebClient(); + webClient.getOptions().setUseInsecureSSL(true); + webClient.addRequestHeader("Authorization", "Basic " + Base64.encode((user + ":" + password).getBytes())); + + // + // First invocation + // + + 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()); + + // Check claims + String parsedResponse = DOM2Writer.nodeToString(samlResponse.getDOM().getOwnerDocument()); + String claim = ClaimTypes.FIRSTNAME.toString(); + Assert.assertTrue(parsedResponse.contains(claim)); + claim = ClaimTypes.LASTNAME.toString(); + Assert.assertTrue(parsedResponse.contains(claim)); + claim = ClaimTypes.EMAILADDRESS.toString(); + Assert.assertTrue(parsedResponse.contains(claim)); + + // + // Second invocation - change the credentials to make sure the session is set up correctly + // + + webClient.removeRequestHeader("Authorization"); + webClient.addRequestHeader("Authorization", "Basic " + Base64.encode(("mallory" + ":" + password).getBytes())); + + webClient.getOptions().setJavaScriptEnabled(false); + idpPage = webClient.getPage(url); + webClient.getOptions().setJavaScriptEnabled(true); + Assert.assertEquals("IDP SignIn Response Form", idpPage.getTitleText()); + + samlResponse = parseSAMLResponse(idpPage, relayState, consumerURL, authnRequest.getID()); + expected = "urn:oasis:names:tc:SAML:2.0:status:Success"; + Assert.assertEquals(expected, samlResponse.getStatus().getStatusCode().getValue()); + + // Check claims + parsedResponse = DOM2Writer.nodeToString(samlResponse.getDOM().getOwnerDocument()); + claim = ClaimTypes.FIRSTNAME.toString(); + Assert.assertTrue(parsedResponse.contains(claim)); + claim = ClaimTypes.LASTNAME.toString(); + Assert.assertTrue(parsedResponse.contains(claim)); + claim = ClaimTypes.EMAILADDRESS.toString(); + Assert.assertTrue(parsedResponse.contains(claim)); + + webClient.close(); + } + + @org.junit.Test + public void testSuccessfulSSOInvokeOnIdPWithForceAuthn() throws Exception { + OpenSAMLUtil.initSamlEngine(); + + // 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.setForceAuthn(Boolean.TRUE); + 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"; + + final WebClient webClient = new WebClient(); + webClient.getOptions().setUseInsecureSSL(true); + webClient.getCredentialsProvider().setCredentials( + new AuthScope("localhost", Integer.parseInt(getIdpHttpsPort())), + new UsernamePasswordCredentials(user, password)); + + // + // First invocation + // + + 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()); + + // Check claims + String parsedResponse = DOM2Writer.nodeToString(samlResponse.getDOM().getOwnerDocument()); + String claim = ClaimTypes.FIRSTNAME.toString(); + Assert.assertTrue(parsedResponse.contains(claim)); + claim = ClaimTypes.LASTNAME.toString(); + Assert.assertTrue(parsedResponse.contains(claim)); + claim = ClaimTypes.EMAILADDRESS.toString(); + Assert.assertTrue(parsedResponse.contains(claim)); + + // + // Second invocation + // + + webClient.getOptions().setJavaScriptEnabled(false); + idpPage = webClient.getPage(url); + webClient.getOptions().setJavaScriptEnabled(true); + Assert.assertEquals("IDP SignIn Response Form", idpPage.getTitleText()); + + samlResponse = parseSAMLResponse(idpPage, relayState, consumerURL, authnRequest.getID()); + expected = "urn:oasis:names:tc:SAML:2.0:status:Success"; + Assert.assertEquals(expected, samlResponse.getStatus().getStatusCode().getValue()); + + // Check claims + parsedResponse = DOM2Writer.nodeToString(samlResponse.getDOM().getOwnerDocument()); + claim = ClaimTypes.FIRSTNAME.toString(); + Assert.assertTrue(parsedResponse.contains(claim)); + claim = ClaimTypes.LASTNAME.toString(); + Assert.assertTrue(parsedResponse.contains(claim)); + claim = ClaimTypes.EMAILADDRESS.toString(); + Assert.assertTrue(parsedResponse.contains(claim)); + + webClient.close(); + } + // // Negative tests // @@ -1068,6 +1238,82 @@ public class IdpTest { webClient.close(); } + @org.junit.Test + public void testForceAuthnWrongCredentials() throws Exception { + OpenSAMLUtil.initSamlEngine(); + + // 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.setForceAuthn(Boolean.TRUE); + 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"; + + final WebClient webClient = new WebClient(); + webClient.getOptions().setUseInsecureSSL(true); + webClient.addRequestHeader("Authorization", "Basic " + Base64.encode((user + ":" + password).getBytes())); + + // + // First invocation + // + + 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()); + + // Check claims + String parsedResponse = DOM2Writer.nodeToString(samlResponse.getDOM().getOwnerDocument()); + String claim = ClaimTypes.FIRSTNAME.toString(); + Assert.assertTrue(parsedResponse.contains(claim)); + claim = ClaimTypes.LASTNAME.toString(); + Assert.assertTrue(parsedResponse.contains(claim)); + claim = ClaimTypes.EMAILADDRESS.toString(); + Assert.assertTrue(parsedResponse.contains(claim)); + + // + // Second invocation - change the credentials, this should fail + // + + webClient.removeRequestHeader("Authorization"); + webClient.addRequestHeader("Authorization", "Basic " + Base64.encode(("mallory" + ":" + password).getBytes())); + + webClient.getOptions().setJavaScriptEnabled(false); + try { + webClient.getPage(url); + Assert.fail("Authentication failure expected"); + } catch (FailingHttpStatusCodeException ex) { + Assert.assertEquals(ex.getStatusCode(), 401); + } + + webClient.close(); + } + private String encodeAuthnRequest(Element authnRequest) throws IOException { String requestMessage = DOM2Writer.nodeToString(authnRequest);
