Repository: cxf-fediz Updated Branches: refs/heads/master 35ab83126 -> 728e7bb80
[FEDIZ-141] Adding SAML Post Binding Systest Project: http://git-wip-us.apache.org/repos/asf/cxf-fediz/repo Commit: http://git-wip-us.apache.org/repos/asf/cxf-fediz/commit/728e7bb8 Tree: http://git-wip-us.apache.org/repos/asf/cxf-fediz/tree/728e7bb8 Diff: http://git-wip-us.apache.org/repos/asf/cxf-fediz/diff/728e7bb8 Branch: refs/heads/master Commit: 728e7bb801c6a5b749ede68e002cc51f2bea6005 Parents: 35ab831 Author: Jan Bernhardt <jbernha...@talend.com> Authored: Mon Jan 4 18:25:36 2016 +0100 Committer: Jan Bernhardt <jbernha...@talend.com> Committed: Mon Jan 4 19:56:17 2016 +0100 ---------------------------------------------------------------------- .../cxf/fediz/samlsso/example/SamlSso.java | 83 +++++++++++++++----- .../src/main/resources/TemplateSAMLResponse.xml | 29 +++++++ systests/federation/samlsso/pom.xml | 8 ++ .../cxf/fediz/integrationtests/SAMLSSOTest.java | 48 ++++++++++- .../src/test/resources/entities-realma.xml | 24 +++++- .../test/resources/fediz_config_saml_sso.xml | 30 +++++++ 6 files changed, 197 insertions(+), 25 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/728e7bb8/systests/federation/samlIdpWebapp/src/main/java/org/apache/cxf/fediz/samlsso/example/SamlSso.java ---------------------------------------------------------------------- diff --git a/systests/federation/samlIdpWebapp/src/main/java/org/apache/cxf/fediz/samlsso/example/SamlSso.java b/systests/federation/samlIdpWebapp/src/main/java/org/apache/cxf/fediz/samlsso/example/SamlSso.java index cf43784..4d62d87 100644 --- a/systests/federation/samlIdpWebapp/src/main/java/org/apache/cxf/fediz/samlsso/example/SamlSso.java +++ b/systests/federation/samlIdpWebapp/src/main/java/org/apache/cxf/fediz/samlsso/example/SamlSso.java @@ -23,24 +23,34 @@ package org.apache.cxf.fediz.samlsso.example; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.io.UnsupportedEncodingException; import java.util.Collections; +import java.util.zip.DataFormatException; +import javax.ws.rs.FormParam; import javax.ws.rs.GET; +import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.QueryParam; import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; import javax.ws.rs.core.UriBuilder; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.stream.XMLStreamException; import org.w3c.dom.Document; import org.w3c.dom.Element; + +import org.apache.cxf.common.util.Base64Exception; import org.apache.cxf.common.util.Base64Utility; +import org.apache.cxf.helpers.IOUtils; import org.apache.cxf.jaxrs.ext.MessageContext; import org.apache.cxf.rs.security.saml.DeflateEncoderDecoder; import org.apache.cxf.staxutils.StaxUtils; import org.apache.wss4j.common.crypto.Crypto; import org.apache.wss4j.common.crypto.CryptoFactory; +import org.apache.wss4j.common.ext.WSSecurityException; import org.apache.wss4j.common.saml.OpenSAMLUtil; import org.apache.wss4j.common.saml.SAMLCallback; import org.apache.wss4j.common.saml.SAMLUtil; @@ -72,17 +82,18 @@ public class SamlSso { docBuilderFactory.setNamespaceAware(true); } + @POST + public javax.ws.rs.core.Response login(@FormParam("SAMLRequest") String samlRequest, + @FormParam("RelayState") String relayState) throws Exception { + + return login(samlRequest, relayState, "POST"); + } + @GET public javax.ws.rs.core.Response login(@QueryParam("SAMLRequest") String samlRequest, - @QueryParam("RelayState") String relayState) throws Exception { - - byte[] deflatedToken = Base64Utility.decode(samlRequest); - InputStream tokenStream = new DeflateEncoderDecoder().inflateToken(deflatedToken); + @QueryParam("RelayState") String relayState, @QueryParam("binding") String binding) throws Exception { - Document responseDoc = StaxUtils.read(new InputStreamReader(tokenStream, "UTF-8")); - AuthnRequest request = - (AuthnRequest)OpenSAMLUtil.fromDom(responseDoc.getDocumentElement()); - System.out.println(DOM2Writer.nodeToString(responseDoc)); + AuthnRequest request = extractRequest(samlRequest); String racs = request.getAssertionConsumerServiceURL(); String requestIssuer = request.getIssuer().getValue(); @@ -91,20 +102,19 @@ public class SamlSso { Element response = createResponse(request.getID(), racs, requestIssuer); String responseStr = encodeResponse(response); - // Perform Redirect to RACS - UriBuilder ub = UriBuilder.fromUri(racs); - ub.queryParam("SAMLResponse", responseStr); - ub.queryParam("RelayState", relayState); - - return javax.ws.rs.core.Response.seeOther(ub.build()).build(); + if ("REDIRECT".equals(binding)) { + return redirectResponse(relayState, racs, responseStr); + } else { + return postBindingResponse(relayState, racs, responseStr); + } } - + @Context public void setMessageContext(MessageContext mc) { this.messageContext = mc; } - - private Element createResponse(String requestID, String racs, String requestIssuer) throws Exception { + + protected Element createResponse(String requestID, String racs, String requestIssuer) throws Exception { DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder(); Document doc = docBuilder.newDocument(); @@ -153,8 +163,8 @@ public class SamlSso { return policyElement; } - - private String encodeResponse(Element response) throws IOException { + + protected String encodeResponse(Element response) throws IOException { String responseMessage = DOM2Writer.nodeToString(response); System.out.println("RESP: " + responseMessage); @@ -163,6 +173,41 @@ public class SamlSso { return Base64Utility.encode(deflatedBytes); } + + protected AuthnRequest extractRequest(String samlRequest) throws Base64Exception, DataFormatException, + XMLStreamException, UnsupportedEncodingException, WSSecurityException { + byte[] deflatedToken = Base64Utility.decode(samlRequest); + InputStream tokenStream = new DeflateEncoderDecoder().inflateToken(deflatedToken); + + Document responseDoc = StaxUtils.read(new InputStreamReader(tokenStream, "UTF-8")); + AuthnRequest request = + (AuthnRequest)OpenSAMLUtil.fromDom(responseDoc.getDocumentElement()); + System.out.println(DOM2Writer.nodeToString(responseDoc)); + return request; + } + + protected javax.ws.rs.core.Response postBindingResponse(String relayState, String racs, String responseStr) + throws IOException { + InputStream inputStream = this.getClass().getResourceAsStream("/TemplateSAMLResponse.xml"); + String responseTemplate = IOUtils.toString(inputStream, "UTF-8"); + inputStream.close(); + + // Perform Redirect to RACS + responseTemplate = responseTemplate.replace("%RESPONSE_URL%", racs); + responseTemplate = responseTemplate.replace("%SAMLResponse%", responseStr); + responseTemplate = responseTemplate.replace("%RelayState%", relayState); + + return javax.ws.rs.core.Response.ok(responseTemplate).type(MediaType.TEXT_HTML).build(); + } + + protected javax.ws.rs.core.Response redirectResponse(String relayState, String racs, String responseStr) { + // Perform Redirect to RACS + UriBuilder ub = UriBuilder.fromUri(racs); + ub.queryParam("SAMLResponse", responseStr); + ub.queryParam("RelayState", relayState); + + return javax.ws.rs.core.Response.seeOther(ub.build()).build(); + } } http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/728e7bb8/systests/federation/samlIdpWebapp/src/main/resources/TemplateSAMLResponse.xml ---------------------------------------------------------------------- diff --git a/systests/federation/samlIdpWebapp/src/main/resources/TemplateSAMLResponse.xml b/systests/federation/samlIdpWebapp/src/main/resources/TemplateSAMLResponse.xml new file mode 100644 index 0000000..027a712 --- /dev/null +++ b/systests/federation/samlIdpWebapp/src/main/resources/TemplateSAMLResponse.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" +"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> + <head> + <title>SAML IDP Response Form</title> + </head> + <body onload="document.forms[0].submit()"> + <noscript> + <p> + <strong>Note:</strong> + Since your browser does not support JavaScript, + you must press the Continue button once to proceed. + </p> + </noscript> + <form name="signinresponseform" action="%RESPONSE_URL%" method="post"> + <div> + <input type="hidden" name="RelayState" value="%RelayState%" /> + <input type="hidden" name="SAMLResponse" + value="%SAMLResponse%" /> + </div> + <noscript> + <div> + <input type="submit" name="_eventId_submit" value="Continue" /> + </div> + </noscript> + </form> + </body> +</html> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/728e7bb8/systests/federation/samlsso/pom.xml ---------------------------------------------------------------------- diff --git a/systests/federation/samlsso/pom.xml b/systests/federation/samlsso/pom.xml index e604932..116527c 100644 --- a/systests/federation/samlsso/pom.xml +++ b/systests/federation/samlsso/pom.xml @@ -178,6 +178,14 @@ <overWrite>true</overWrite> <outputDirectory>target/tomcat/rp/webapps/simpleWebapp</outputDirectory> </artifactItem> + <artifactItem> + <groupId>org.apache.cxf.fediz.systests.webapps</groupId> + <artifactId>fediz-systests-webapps-simple</artifactId> + <version>${project.version}</version> + <type>war</type> + <overWrite>true</overWrite> + <outputDirectory>target/tomcat/rp/webapps/simpleWebapp2</outputDirectory> + </artifactItem> </artifactItems> <outputAbsoluteArtifactFilename>true</outputAbsoluteArtifactFilename> <overWriteSnapshots>true</overWriteSnapshots> http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/728e7bb8/systests/federation/samlsso/src/test/java/org/apache/cxf/fediz/integrationtests/SAMLSSOTest.java ---------------------------------------------------------------------- diff --git a/systests/federation/samlsso/src/test/java/org/apache/cxf/fediz/integrationtests/SAMLSSOTest.java b/systests/federation/samlsso/src/test/java/org/apache/cxf/fediz/integrationtests/SAMLSSOTest.java index 39311a6..8c0510a 100644 --- a/systests/federation/samlsso/src/test/java/org/apache/cxf/fediz/integrationtests/SAMLSSOTest.java +++ b/systests/federation/samlsso/src/test/java/org/apache/cxf/fediz/integrationtests/SAMLSSOTest.java @@ -186,6 +186,8 @@ public class SAMLSSOTest { + "test-classes" + File.separator + "fediz_config_saml_sso.xml"); cxt.getPipeline().addValve(fa); + cxt = rpServer.addWebapp("/fedizhelloworld-post-binding", "simpleWebapp2"); + cxt.getPipeline().addValve(fa); rpServer.start(); } catch (Exception e) { @@ -241,7 +243,38 @@ public class SAMLSSOTest { String password = "ECILA"; final String bodyTextContent = - login(url, user, password, idpSamlSSOHttpsPort, idpHttpsPort); + login(url, user, password, idpSamlSSOHttpsPort, idpHttpsPort, false); + + Assert.assertTrue("Principal not alice", + bodyTextContent.contains("userPrincipal=alice")); + Assert.assertTrue("User " + user + " does not have role Admin", + bodyTextContent.contains("role:Admin=false")); + Assert.assertTrue("User " + user + " does not have role Manager", + bodyTextContent.contains("role:Manager=false")); + Assert.assertTrue("User " + user + " must have role User", + bodyTextContent.contains("role:User=true")); + + String claim = ClaimTypes.FIRSTNAME.toString(); + Assert.assertTrue("User " + user + " claim " + claim + " is not 'Alice'", + bodyTextContent.contains(claim + "=Alice")); + claim = ClaimTypes.LASTNAME.toString(); + Assert.assertTrue("User " + user + " claim " + claim + " is not 'Smith'", + bodyTextContent.contains(claim + "=Smith")); + claim = ClaimTypes.EMAILADDRESS.toString(); + Assert.assertTrue("User " + user + " claim " + claim + " is not 'al...@realma.org'", + bodyTextContent.contains(claim + "=al...@realma.org")); + } + + @org.junit.Test + public void testSAMLSSOPostBinding() throws Exception { + String url = "https://localhost:" + getRpHttpsPort() + "/fedizhelloworld-post-binding/secure/fedservlet"; + // System.out.println("URL: " + url); + // Thread.sleep(60 * 2 * 1000); + String user = "ALICE"; // realm b credentials + String password = "ECILA"; + + final String bodyTextContent = + login(url, user, password, idpSamlSSOHttpsPort, idpHttpsPort, true); Assert.assertTrue("Principal not alice", bodyTextContent.contains("userPrincipal=alice")); @@ -264,7 +297,7 @@ public class SAMLSSOTest { } private static String login(String url, String user, String password, - String idpPort, String rpIdpPort) throws IOException { + String idpPort, String rpIdpPort, boolean postBinding) throws IOException { // // Access the RP + get redirected to the IdP for "realm a". Then get redirected to the IdP for // "realm b". @@ -278,8 +311,15 @@ public class SAMLSSOTest { new UsernamePasswordCredentials(user, password)); webClient.getOptions().setJavaScriptEnabled(false); - final HtmlPage idpPage = webClient.getPage(url); - webClient.getOptions().setJavaScriptEnabled(true); + HtmlPage idpPage = webClient.getPage(url); + + if (postBinding) { + Assert.assertEquals("SAML IDP Response Form", idpPage.getTitleText()); + final HtmlForm form = idpPage.getFormByName("signinresponseform"); + final HtmlSubmitInput button = form.getInputByName("_eventId_submit"); + idpPage = button.click(); + } + Assert.assertEquals("IDP SignIn Response Form", idpPage.getTitleText()); // Now redirect back to the RP http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/728e7bb8/systests/federation/samlsso/src/test/resources/entities-realma.xml ---------------------------------------------------------------------- diff --git a/systests/federation/samlsso/src/test/resources/entities-realma.xml b/systests/federation/samlsso/src/test/resources/entities-realma.xml index 86fd540..145d1a8 100644 --- a/systests/federation/samlsso/src/test/resources/entities-realma.xml +++ b/systests/federation/samlsso/src/test/resources/entities-realma.xml @@ -71,6 +71,7 @@ <property name="trustedIdps"> <util:list> <ref bean="trusted-idp-realmB" /> + <ref bean="trusted-idp-realmC" /> </util:list> </property> <property name="claimTypesOffered"> @@ -87,7 +88,7 @@ class="org.apache.cxf.fediz.service.idp.service.jpa.TrustedIdpEntity"> <property name="realm" value="urn:org:apache:cxf:fediz:idp:realm-B" /> <property name="cacheTokens" value="true" /> - <property name="url" value="https://localhost:${idp.samlsso.https.port}/idp/samlsso" /> + <property name="url" value="https://localhost:${idp.samlsso.https.port}/idp/samlsso?binding=REDIRECT" /> <property name="certificate" value="realmb.cert" /> <property name="trustType" value="PEER_TRUST" /> <property name="protocol" value="urn:oasis:names:tc:SAML:2.0:profiles:SSO:browser" /> @@ -101,6 +102,25 @@ </util:map> </property> </bean> + + <bean id="trusted-idp-realmC" + class="org.apache.cxf.fediz.service.idp.service.jpa.TrustedIdpEntity"> + <property name="realm" value="urn:org:apache:cxf:fediz:idp:realm-C" /> + <property name="cacheTokens" value="true" /> + <property name="url" value="https://localhost:${idp.samlsso.https.port}/idp/samlsso" /> + <property name="certificate" value="realmb.cert" /> + <property name="trustType" value="PEER_TRUST" /> + <property name="protocol" value="urn:oasis:names:tc:SAML:2.0:profiles:SSO:browser" /> + <property name="federationType" value="FEDERATE_IDENTITY" /> + <property name="name" value="Realm C" /> + <property name="description" value="SAML Web Profile - Response POST Binding" /> + <property name="parameters"> + <util:map> + <entry key="sign.request" value="true" /> + <entry key="support.deflate.encoding" value="true" /> + </util:map> + </property> + </bean> <bean id="srv-fedizhelloworld" class="org.apache.cxf.fediz.service.idp.service.jpa.ApplicationEntity"> <property name="realm" value="urn:org:apache:cxf:fediz:fedizhelloworld" /> @@ -111,7 +131,7 @@ <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="passiveRequestorEndpointConstraint" - value="https://localhost:(\d)*/(\w)*helloworld(\w)*/secure/.*" /> + value="https://localhost:(\d)*/(\w)*helloworld.*/secure/.*" /> </bean> <bean class="org.apache.cxf.fediz.service.idp.service.jpa.ApplicationClaimEntity"> http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/728e7bb8/systests/federation/samlsso/src/test/resources/fediz_config_saml_sso.xml ---------------------------------------------------------------------- diff --git a/systests/federation/samlsso/src/test/resources/fediz_config_saml_sso.xml b/systests/federation/samlsso/src/test/resources/fediz_config_saml_sso.xml index 7bd3cb7..89f158b 100644 --- a/systests/federation/samlsso/src/test/resources/fediz_config_saml_sso.xml +++ b/systests/federation/samlsso/src/test/resources/fediz_config_saml_sso.xml @@ -52,5 +52,35 @@ <logoutURL>/secure/logout</logoutURL> <logoutRedirectTo>/index.html</logoutRedirectTo> </contextConfig> + <contextConfig name="/fedizhelloworld-post-binding"> + <audienceUris> + <audienceItem>urn:org:apache:cxf:fediz:fedizhelloworld</audienceItem> + </audienceUris> + <certificateStores> + <trustManager> + <keyStore file="test-classes/clienttrust.jks" + password="storepass" type="JKS" /> + </trustManager> + </certificateStores> + <trustedIssuers> + <issuer certificateValidation="PeerTrust" /> + </trustedIssuers> + <maximumClockSkew>1000</maximumClockSkew> + <protocol xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:type="federationProtocolType" version="1.0.0"> + <realm>urn:org:apache:cxf:fediz:fedizhelloworld</realm> + <issuer>https://localhost:${idp.https.port}/fediz-idp/federation</issuer> + <roleDelimiter>,</roleDelimiter> + <roleURI>http://schemas.xmlsoap.org/ws/2005/05/identity/claims/role</roleURI> + <freshness>10</freshness> + <homeRealm type="String">urn:org:apache:cxf:fediz:idp:realm-C</homeRealm> + <claimTypesRequested> + <claimType type="a particular claim type" + optional="true" /> + </claimTypesRequested> + </protocol> + <logoutURL>/secure/logout</logoutURL> + <logoutRedirectTo>/index.html</logoutRedirectTo> + </contextConfig> </FedizConfig>