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 2014/07/23 18:24:06 UTC
[2/4] Adding unit tests for SAML SSO
http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/617fd4e9/plugins/core/src/test/java/org/apache/cxf/fediz/core/federation/SAMLTokenValidatorOldTest.java
----------------------------------------------------------------------
diff --git a/plugins/core/src/test/java/org/apache/cxf/fediz/core/federation/SAMLTokenValidatorOldTest.java b/plugins/core/src/test/java/org/apache/cxf/fediz/core/federation/SAMLTokenValidatorOldTest.java
new file mode 100644
index 0000000..fb21c21
--- /dev/null
+++ b/plugins/core/src/test/java/org/apache/cxf/fediz/core/federation/SAMLTokenValidatorOldTest.java
@@ -0,0 +1,399 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.fediz.core.federation;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.util.Collections;
+import java.util.List;
+
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.UnsupportedCallbackException;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.apache.cxf.fediz.common.STSUtil;
+import org.apache.cxf.fediz.common.SecurityTestUtil;
+import org.apache.cxf.fediz.core.AbstractSAMLCallbackHandler;
+import org.apache.cxf.fediz.core.Claim;
+import org.apache.cxf.fediz.core.ClaimTypes;
+import org.apache.cxf.fediz.core.FederationConstants;
+import org.apache.cxf.fediz.core.KeystoreCallbackHandler;
+import org.apache.cxf.fediz.core.SAML1CallbackHandler;
+import org.apache.cxf.fediz.core.SAML2CallbackHandler;
+import org.apache.cxf.fediz.core.config.FedizConfigurator;
+import org.apache.cxf.fediz.core.config.FedizContext;
+import org.apache.cxf.fediz.core.processor.FederationProcessorImpl;
+import org.apache.cxf.fediz.core.processor.FedizProcessor;
+import org.apache.cxf.fediz.core.processor.FedizRequest;
+import org.apache.cxf.fediz.core.processor.FedizResponse;
+import org.apache.wss4j.common.crypto.Crypto;
+import org.apache.wss4j.common.crypto.CryptoFactory;
+import org.apache.wss4j.common.ext.WSPasswordCallback;
+import org.apache.wss4j.common.ext.WSSecurityException;
+import org.apache.wss4j.common.saml.SAMLCallback;
+import org.apache.wss4j.common.saml.SAMLUtil;
+import org.apache.wss4j.common.saml.SamlAssertionWrapper;
+import org.apache.wss4j.common.saml.bean.AudienceRestrictionBean;
+import org.apache.wss4j.common.saml.bean.ConditionsBean;
+import org.apache.wss4j.common.saml.builder.SAML1Constants;
+import org.apache.wss4j.common.saml.builder.SAML2Constants;
+import org.apache.wss4j.common.util.DOM2Writer;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+
+
+// This testcases tests the encoding implemented before CXF-4484
+public class SAMLTokenValidatorOldTest {
+ static final String TEST_USER = "alice";
+ static final String TEST_RSTR_ISSUER = "FedizSTSIssuer";
+ static final String TEST_AUDIENCE = "https://localhost/fedizhelloworld";
+
+ private static final String CONFIG_FILE = "fediz_test_config.xml";
+
+ private static Crypto crypto;
+ private static CallbackHandler cbPasswordHandler;
+ private static FedizConfigurator configurator;
+
+
+ @BeforeClass
+ public static void init() {
+ try {
+ crypto = CryptoFactory.getInstance("signature.properties");
+ cbPasswordHandler = new KeystoreCallbackHandler();
+ getFederationConfigurator();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ Assert.assertNotNull(configurator);
+
+ }
+
+ @AfterClass
+ public static void cleanup() {
+ SecurityTestUtil.cleanup();
+ }
+
+
+ private static FedizConfigurator getFederationConfigurator() {
+ if (configurator != null) {
+ return configurator;
+ }
+ try {
+ configurator = new FedizConfigurator();
+ final URL resource = Thread.currentThread().getContextClassLoader()
+ .getResource(CONFIG_FILE);
+ File f = new File(resource.toURI());
+ configurator.loadConfig(f);
+ return configurator;
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ /**
+ * Validate SAML 2 token which includes the role attribute with 2 values
+ * Roles are encoded as a multi-value saml attribute
+ */
+ @org.junit.Test
+ public void validateSAML2Token() throws Exception {
+ SAML2CallbackHandler callbackHandler = new SAML2CallbackHandler();
+ callbackHandler.setStatement(SAML2CallbackHandler.Statement.ATTR);
+ callbackHandler.setConfirmationMethod(SAML2Constants.CONF_BEARER);
+ callbackHandler.setIssuer(TEST_RSTR_ISSUER);
+ callbackHandler.setSubjectName(TEST_USER);
+ callbackHandler.setAttributeNameFormat(ClaimTypes.URI_BASE.toString());
+ callbackHandler.setCountryClaimName("country");
+ callbackHandler.setRoleAttributeName("role");
+
+ ConditionsBean cp = new ConditionsBean();
+ AudienceRestrictionBean audienceRestriction = new AudienceRestrictionBean();
+ audienceRestriction.getAudienceURIs().add(TEST_AUDIENCE);
+ cp.setAudienceRestrictions(Collections.singletonList(audienceRestriction));
+ callbackHandler.setConditions(cp);
+
+ SAMLCallback samlCallback = new SAMLCallback();
+ SAMLUtil.doSAMLCallback(callbackHandler, samlCallback);
+ SamlAssertionWrapper assertion = new SamlAssertionWrapper(samlCallback);
+
+ String rstr = createSamlToken(assertion, "mystskey", true);
+
+ FedizRequest wfReq = new FedizRequest();
+ wfReq.setAction(FederationConstants.ACTION_SIGNIN);
+ wfReq.setResponseToken(rstr);
+
+ configurator = null;
+ FedizContext config = getFederationConfigurator().getFedizContext("ROOT");
+
+ FedizProcessor wfProc = new FederationProcessorImpl();
+ FedizResponse wfRes = wfProc.processRequest(wfReq, config);
+
+ Assert.assertEquals("Principal name wrong", TEST_USER,
+ wfRes.getUsername());
+ Assert.assertEquals("Issuer wrong", TEST_RSTR_ISSUER, wfRes.getIssuer());
+ Assert.assertEquals("Two roles must be found", 2, wfRes.getRoles()
+ .size());
+ Assert.assertEquals("Audience wrong", TEST_AUDIENCE, wfRes.getAudience());
+ assertClaims(wfRes.getClaims(), callbackHandler.getRoleAttributeName());
+
+ }
+
+ /**
+ * Validate SAML 2 token where role information is provided
+ * within another SAML attribute
+ */
+ @org.junit.Test
+ public void validateSAML2TokenDifferentRoleURI() throws Exception {
+ SAML2CallbackHandler callbackHandler = new SAML2CallbackHandler();
+ callbackHandler.setStatement(SAML2CallbackHandler.Statement.ATTR);
+ callbackHandler.setConfirmationMethod(SAML2Constants.CONF_BEARER);
+ callbackHandler.setIssuer(TEST_RSTR_ISSUER);
+ callbackHandler.setSubjectName(TEST_USER);
+ callbackHandler.setAttributeNameFormat(ClaimTypes.URI_BASE.toString());
+ callbackHandler.setCountryClaimName("country");
+ callbackHandler.setRoleAttributeName("http://schemas.mycompany.com/claims/role");
+
+ ConditionsBean cp = new ConditionsBean();
+ AudienceRestrictionBean audienceRestriction = new AudienceRestrictionBean();
+ audienceRestriction.getAudienceURIs().add(TEST_AUDIENCE);
+ cp.setAudienceRestrictions(Collections.singletonList(audienceRestriction));
+ callbackHandler.setConditions(cp);
+
+ SAMLCallback samlCallback = new SAMLCallback();
+ SAMLUtil.doSAMLCallback(callbackHandler, samlCallback);
+ SamlAssertionWrapper assertion = new SamlAssertionWrapper(samlCallback);
+
+ String rstr = createSamlToken(assertion, "mystskey", true);
+
+ FedizRequest wfReq = new FedizRequest();
+ wfReq.setAction(FederationConstants.ACTION_SIGNIN);
+ wfReq.setResponseToken(rstr);
+
+ configurator = null;
+ FedizContext config = getFederationConfigurator().getFedizContext("CUSTOMROLEURI");
+
+ FedizProcessor wfProc = new FederationProcessorImpl();
+ FedizResponse wfRes = wfProc.processRequest(wfReq, config);
+
+ Assert.assertEquals("Principal name wrong", TEST_USER, wfRes.getUsername());
+ Assert.assertEquals("Issuer wrong", TEST_RSTR_ISSUER, wfRes.getIssuer());
+ Assert.assertEquals("Two roles must be found", 2, wfRes.getRoles().size());
+ Assert.assertEquals("Audience wrong", TEST_AUDIENCE, wfRes.getAudience());
+ assertClaims(wfRes.getClaims(), callbackHandler.getRoleAttributeName());
+ }
+
+ /**
+ * Validate SAML 2 token where role information is provided
+ * within another SAML attribute
+ */
+ @org.junit.Test
+ public void validateSAML1TokenDifferentRoleURI() throws Exception {
+ SAML1CallbackHandler callbackHandler = new SAML1CallbackHandler();
+ callbackHandler.setStatement(SAML1CallbackHandler.Statement.ATTR);
+ callbackHandler.setConfirmationMethod(SAML2Constants.CONF_BEARER);
+ callbackHandler.setIssuer(TEST_RSTR_ISSUER);
+ callbackHandler.setSubjectName(TEST_USER);
+ callbackHandler.setUseNameFormatAsNamespace(true);
+ callbackHandler.setAttributeNameFormat(ClaimTypes.URI_BASE.toString());
+ callbackHandler.setRoleAttributeName("http://schemas.mycompany.com/claims/role");
+ ConditionsBean cp = new ConditionsBean();
+ AudienceRestrictionBean audienceRestriction = new AudienceRestrictionBean();
+ audienceRestriction.getAudienceURIs().add(TEST_AUDIENCE);
+ cp.setAudienceRestrictions(Collections.singletonList(audienceRestriction));
+ callbackHandler.setConditions(cp);
+
+ SAMLCallback samlCallback = new SAMLCallback();
+ SAMLUtil.doSAMLCallback(callbackHandler, samlCallback);
+ SamlAssertionWrapper assertion = new SamlAssertionWrapper(samlCallback);
+
+ String rstr = createSamlToken(assertion, "mystskey", true);
+
+ FedizRequest wfReq = new FedizRequest();
+ wfReq.setAction(FederationConstants.ACTION_SIGNIN);
+ wfReq.setResponseToken(rstr);
+
+ configurator = null;
+ FedizContext config = getFederationConfigurator().getFedizContext("CUSTOMROLEURI");
+
+ FedizProcessor wfProc = new FederationProcessorImpl();
+ FedizResponse wfRes = wfProc.processRequest(wfReq, config);
+
+ Assert.assertEquals("Principal name wrong", TEST_USER, wfRes.getUsername());
+ Assert.assertEquals("Issuer wrong", TEST_RSTR_ISSUER, wfRes.getIssuer());
+ Assert.assertEquals("Two roles must be found", 2, wfRes.getRoles().size());
+ Assert.assertEquals("Audience wrong", TEST_AUDIENCE, wfRes.getAudience());
+ assertClaims(wfRes.getClaims(), callbackHandler.getRoleAttributeName());
+ }
+
+ /**
+ * Validate SAML 1.1 token which includes the role attribute with 2 values
+ * Roles are encoded as a multi-value saml attribute
+ */
+ @org.junit.Test
+ public void validateSAML1Token() throws Exception {
+ SAML1CallbackHandler callbackHandler = new SAML1CallbackHandler();
+ callbackHandler.setStatement(SAML1CallbackHandler.Statement.ATTR);
+ callbackHandler.setConfirmationMethod(SAML1Constants.CONF_BEARER);
+ callbackHandler.setIssuer(TEST_RSTR_ISSUER);
+ callbackHandler.setSubjectName(TEST_USER);
+ callbackHandler.setUseNameFormatAsNamespace(true);
+ callbackHandler.setAttributeNameFormat(ClaimTypes.URI_BASE.toString());
+ callbackHandler.setRoleAttributeName("role");
+
+ ConditionsBean cp = new ConditionsBean();
+ AudienceRestrictionBean audienceRestriction = new AudienceRestrictionBean();
+ audienceRestriction.getAudienceURIs().add(TEST_AUDIENCE);
+ cp.setAudienceRestrictions(Collections.singletonList(audienceRestriction));
+ callbackHandler.setConditions(cp);
+
+ SAMLCallback samlCallback = new SAMLCallback();
+ SAMLUtil.doSAMLCallback(callbackHandler, samlCallback);
+ SamlAssertionWrapper assertion = new SamlAssertionWrapper(samlCallback);
+
+ String rstr = createSamlToken(assertion, "mystskey", true);
+
+ FedizRequest wfReq = new FedizRequest();
+ wfReq.setAction(FederationConstants.ACTION_SIGNIN);
+ wfReq.setResponseToken(rstr);
+
+ configurator = null;
+ FedizContext config = getFederationConfigurator().getFedizContext("ROOT");
+
+ FedizProcessor wfProc = new FederationProcessorImpl();
+ FedizResponse wfRes = wfProc.processRequest(wfReq, config);
+
+ Assert.assertEquals("Principal name wrong", TEST_USER,
+ wfRes.getUsername());
+ Assert.assertEquals("Issuer wrong", TEST_RSTR_ISSUER, wfRes.getIssuer());
+ Assert.assertEquals("Two roles must be found", 2, wfRes.getRoles()
+ .size());
+ Assert.assertEquals("Audience wrong", TEST_AUDIENCE, wfRes.getAudience());
+ assertClaims(wfRes.getClaims(), callbackHandler.getRoleAttributeName());
+ }
+
+
+ private String createSamlToken(SamlAssertionWrapper assertion, String alias, boolean sign)
+ throws IOException, UnsupportedCallbackException, WSSecurityException, Exception {
+ return createSamlToken(assertion, alias, sign, STSUtil.SAMPLE_RSTR_COLL_MSG);
+ }
+
+ private String createSamlToken(SamlAssertionWrapper assertion, String alias, boolean sign, String rstr)
+ throws IOException, UnsupportedCallbackException, WSSecurityException, Exception {
+ WSPasswordCallback[] cb = {
+ new WSPasswordCallback(alias, WSPasswordCallback.SIGNATURE)
+ };
+ cbPasswordHandler.handle(cb);
+ String password = cb[0].getPassword();
+
+ if (sign) {
+ assertion.signAssertion(alias, password, crypto, false);
+ }
+ Document doc = STSUtil.toSOAPPart(rstr);
+ Element token = assertion.toDOM(doc);
+
+ Element e = SAMLTokenValidatorOldTest.findElement(doc, "RequestedSecurityToken",
+ FederationConstants.WS_TRUST_13_NS);
+ if (e == null) {
+ e = SAMLTokenValidatorOldTest.findElement(doc, "RequestedSecurityToken",
+ FederationConstants.WS_TRUST_2005_02_NS);
+ }
+ e.appendChild(token);
+ return DOM2Writer.nodeToString(doc);
+ }
+
+
+
+
+ /**
+ * Returns the first element that matches <code>name</code> and
+ * <code>namespace</code>. <p/> This is a replacement for a XPath lookup
+ * <code>//name</code> with the given namespace. It's somewhat faster than
+ * XPath, and we do not deal with prefixes, just with the real namespace URI
+ *
+ * @param startNode Where to start the search
+ * @param name Local name of the element
+ * @param namespace Namespace URI of the element
+ * @return The found element or <code>null</code>
+ */
+ public static Element findElement(Node startNode, String name, String namespace) {
+ //
+ // Replace the formerly recursive implementation with a depth-first-loop
+ // lookup
+ //
+ if (startNode == null) {
+ return null;
+ }
+ Node startParent = startNode.getParentNode();
+ Node processedNode = null;
+
+ while (startNode != null) {
+ // start node processing at this point
+ if (startNode.getNodeType() == Node.ELEMENT_NODE
+ && startNode.getLocalName().equals(name)) {
+ String ns = startNode.getNamespaceURI();
+ if (ns != null && ns.equals(namespace)) {
+ return (Element)startNode;
+ }
+
+ if ((namespace == null || namespace.length() == 0)
+ && (ns == null || ns.length() == 0)) {
+ return (Element)startNode;
+ }
+ }
+ processedNode = startNode;
+ startNode = startNode.getFirstChild();
+
+ // no child, this node is done.
+ if (startNode == null) {
+ // close node processing, get sibling
+ startNode = processedNode.getNextSibling();
+ }
+ // no more siblings, get parent, all children
+ // of parent are processed.
+ while (startNode == null) {
+ processedNode = processedNode.getParentNode();
+ if (processedNode == startParent) {
+ return null;
+ }
+ // close parent node processing (processed node now)
+ startNode = processedNode.getNextSibling();
+ }
+ }
+ return null;
+ }
+
+ private void assertClaims(List<Claim> claims, String roleClaimType) {
+ for (Claim c : claims) {
+ Assert.assertTrue("Invalid ClaimType URI: " + c.getClaimType(),
+ c.getClaimType().equals(roleClaimType)
+ || c.getClaimType().equals(ClaimTypes.COUNTRY)
+ || c.getClaimType().equals(AbstractSAMLCallbackHandler.CLAIM_TYPE_LANGUAGE)
+ );
+ }
+ }
+
+
+
+
+}
http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/617fd4e9/plugins/core/src/test/java/org/apache/cxf/fediz/core/samlsso/SAML2PResponseComponentBuilder.java
----------------------------------------------------------------------
diff --git a/plugins/core/src/test/java/org/apache/cxf/fediz/core/samlsso/SAML2PResponseComponentBuilder.java b/plugins/core/src/test/java/org/apache/cxf/fediz/core/samlsso/SAML2PResponseComponentBuilder.java
new file mode 100644
index 0000000..ffaade7
--- /dev/null
+++ b/plugins/core/src/test/java/org/apache/cxf/fediz/core/samlsso/SAML2PResponseComponentBuilder.java
@@ -0,0 +1,126 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.fediz.core.samlsso;
+
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.opensaml.Configuration;
+import org.opensaml.common.SAMLObjectBuilder;
+import org.opensaml.common.SAMLVersion;
+import org.opensaml.saml2.core.Issuer;
+import org.opensaml.saml2.core.Response;
+import org.opensaml.saml2.core.Status;
+import org.opensaml.saml2.core.StatusCode;
+import org.opensaml.saml2.core.StatusMessage;
+import org.opensaml.xml.XMLObjectBuilderFactory;
+
+/**
+* A (basic) set of utility methods to construct SAML 2.0 Protocol Response statements
+*/
+public final class SAML2PResponseComponentBuilder {
+
+ private static SAMLObjectBuilder<Response> responseBuilder;
+
+ private static SAMLObjectBuilder<Issuer> issuerBuilder;
+
+ private static SAMLObjectBuilder<Status> statusBuilder;
+
+ private static SAMLObjectBuilder<StatusCode> statusCodeBuilder;
+
+ private static SAMLObjectBuilder<StatusMessage> statusMessageBuilder;
+
+ private static XMLObjectBuilderFactory builderFactory = Configuration.getBuilderFactory();
+
+ private SAML2PResponseComponentBuilder() {
+
+ }
+
+ @SuppressWarnings("unchecked")
+ public static Response createSAMLResponse(
+ String inResponseTo,
+ String issuer,
+ Status status
+ ) {
+ if (responseBuilder == null) {
+ responseBuilder = (SAMLObjectBuilder<Response>)
+ builderFactory.getBuilder(Response.DEFAULT_ELEMENT_NAME);
+ }
+ Response response = responseBuilder.buildObject();
+
+ response.setID(UUID.randomUUID().toString());
+ response.setIssueInstant(new DateTime());
+ response.setInResponseTo(inResponseTo);
+ response.setIssuer(createIssuer(issuer));
+ response.setStatus(status);
+ response.setVersion(SAMLVersion.VERSION_20);
+
+ return response;
+ }
+
+ @SuppressWarnings("unchecked")
+ public static Issuer createIssuer(
+ String issuerValue
+ ) {
+ if (issuerBuilder == null) {
+ issuerBuilder = (SAMLObjectBuilder<Issuer>)
+ builderFactory.getBuilder(Issuer.DEFAULT_ELEMENT_NAME);
+ }
+ Issuer issuer = issuerBuilder.buildObject();
+ issuer.setValue(issuerValue);
+
+ return issuer;
+ }
+
+ @SuppressWarnings("unchecked")
+ public static Status createStatus(
+ String statusCodeValue,
+ String statusMessage
+ ) {
+ if (statusBuilder == null) {
+ statusBuilder = (SAMLObjectBuilder<Status>)
+ builderFactory.getBuilder(Status.DEFAULT_ELEMENT_NAME);
+ }
+ if (statusCodeBuilder == null) {
+ statusCodeBuilder = (SAMLObjectBuilder<StatusCode>)
+ builderFactory.getBuilder(StatusCode.DEFAULT_ELEMENT_NAME);
+ }
+ if (statusMessageBuilder == null) {
+ statusMessageBuilder = (SAMLObjectBuilder<StatusMessage>)
+ builderFactory.getBuilder(StatusMessage.DEFAULT_ELEMENT_NAME);
+ }
+
+ Status status = statusBuilder.buildObject();
+
+ StatusCode statusCode = statusCodeBuilder.buildObject();
+ statusCode.setValue(statusCodeValue);
+ status.setStatusCode(statusCode);
+
+ if (statusMessage != null) {
+ StatusMessage statusMessageObject = statusMessageBuilder.buildObject();
+ statusMessageObject.setMessage(statusMessage);
+ status.setStatusMessage(statusMessageObject);
+ }
+
+ return status;
+ }
+
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/617fd4e9/plugins/core/src/test/java/org/apache/cxf/fediz/core/samlsso/SAMLRequestTest.java
----------------------------------------------------------------------
diff --git a/plugins/core/src/test/java/org/apache/cxf/fediz/core/samlsso/SAMLRequestTest.java b/plugins/core/src/test/java/org/apache/cxf/fediz/core/samlsso/SAMLRequestTest.java
new file mode 100644
index 0000000..cfbc8d2
--- /dev/null
+++ b/plugins/core/src/test/java/org/apache/cxf/fediz/core/samlsso/SAMLRequestTest.java
@@ -0,0 +1,205 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.fediz.core.samlsso;
+
+import java.io.File;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.net.URLDecoder;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.w3c.dom.Document;
+
+import org.apache.cxf.fediz.common.SecurityTestUtil;
+import org.apache.cxf.fediz.core.config.FedizConfigurator;
+import org.apache.cxf.fediz.core.config.FedizContext;
+import org.apache.cxf.fediz.core.config.SAMLProtocol;
+import org.apache.cxf.fediz.core.processor.FedizProcessor;
+import org.apache.cxf.fediz.core.processor.RedirectionResponse;
+import org.apache.cxf.fediz.core.processor.SAMLProcessorImpl;
+import org.apache.cxf.fediz.core.util.DOMUtils;
+import org.apache.wss4j.common.saml.OpenSAMLUtil;
+import org.apache.xml.security.utils.Base64;
+import org.easymock.EasyMock;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.opensaml.saml2.core.AuthnRequest;
+
+/**
+ * Some tests for creating SAMLRequests using the SAMLProcessorImpl
+ */
+public class SAMLRequestTest {
+ static final String TEST_USER = "alice";
+ static final String TEST_REQUEST_URL = "https://localhost/fedizhelloworld/";
+ static final String TEST_REQUEST_URI = "/fedizhelloworld";
+ static final String TEST_IDP_ISSUER = "http://url_to_the_issuer";
+ static final String TEST_CLIENT_ADDRESS = "https://127.0.0.1";
+
+ private static final String CONFIG_FILE = "fediz_test_config_saml.xml";
+
+ private static FedizConfigurator configurator;
+ private static DocumentBuilderFactory docBuilderFactory;
+
+ static {
+ docBuilderFactory = DocumentBuilderFactory.newInstance();
+ docBuilderFactory.setNamespaceAware(true);
+ }
+
+
+ @BeforeClass
+ public static void init() {
+ getFederationConfigurator();
+ Assert.assertNotNull(configurator);
+ }
+
+ @AfterClass
+ public static void cleanup() {
+ SecurityTestUtil.cleanup();
+ }
+
+
+ private static FedizConfigurator getFederationConfigurator() {
+ if (configurator != null) {
+ return configurator;
+ }
+ try {
+ configurator = new FedizConfigurator();
+ final URL resource = Thread.currentThread().getContextClassLoader()
+ .getResource(CONFIG_FILE);
+ File f = new File(resource.toURI());
+ configurator.loadConfig(f);
+ return configurator;
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ @org.junit.Test
+ public void createSAMLRequest() throws Exception {
+ // Mock up a Request
+ FedizContext config = getFederationConfigurator().getFedizContext("ROOT");
+
+ HttpServletRequest req = EasyMock.createMock(HttpServletRequest.class);
+ EasyMock.expect(req.getRequestURL()).andReturn(new StringBuffer(TEST_REQUEST_URL)).times(1, 2);
+ EasyMock.expect(req.getContextPath()).andReturn(TEST_REQUEST_URI);
+ EasyMock.expect(req.getRequestURI()).andReturn(TEST_REQUEST_URI).times(1, 2);
+ EasyMock.replay(req);
+
+ FedizProcessor wfProc = new SAMLProcessorImpl();
+ RedirectionResponse response = wfProc.createSignInRequest(req, config);
+
+ String redirectionURL = response.getRedirectionURL();
+ Assert.assertTrue(redirectionURL.startsWith(TEST_IDP_ISSUER));
+ Assert.assertTrue(redirectionURL.contains("SAMLRequest="));
+ Assert.assertTrue(redirectionURL.contains("RelayState="));
+
+ Map<String, String> headers = response.getHeaders();
+ Assert.assertNotNull(headers);
+ Assert.assertFalse(headers.isEmpty());
+ Assert.assertTrue(headers.containsKey("Set-Cookie"));
+ Assert.assertTrue("no-cache, no-store".equals(headers.get("Cache-Control")));
+ Assert.assertTrue("no-cache".equals(headers.get("Pragma")));
+ }
+
+ @org.junit.Test
+ public void testRelayState() throws Exception {
+ // Mock up a Request
+ FedizContext config = getFederationConfigurator().getFedizContext("ROOT");
+
+ HttpServletRequest req = EasyMock.createMock(HttpServletRequest.class);
+ EasyMock.expect(req.getRequestURL()).andReturn(new StringBuffer(TEST_REQUEST_URL)).times(1, 2);
+ EasyMock.expect(req.getContextPath()).andReturn(TEST_REQUEST_URI);
+ EasyMock.expect(req.getRequestURI()).andReturn(TEST_REQUEST_URI).times(1, 2);
+ EasyMock.replay(req);
+
+ FedizProcessor wfProc = new SAMLProcessorImpl();
+ RedirectionResponse response = wfProc.createSignInRequest(req, config);
+
+ String redirectionURL = response.getRedirectionURL();
+ String relayState =
+ redirectionURL.substring(redirectionURL.indexOf("RelayState=") + "RelayState=".length());
+
+ // Now retrieve the RequestState corresponding to the RelayState
+ RequestState requestState =
+ ((SAMLProtocol)config.getProtocol()).getStateManager().removeRequestState(relayState);
+ Assert.assertNotNull(requestState);
+
+ Assert.assertEquals(TEST_IDP_ISSUER, requestState.getIdpServiceAddress());
+ Assert.assertEquals(TEST_REQUEST_URL, requestState.getIssuerId());
+ Assert.assertEquals(TEST_REQUEST_URL, requestState.getTargetAddress());
+ }
+
+ @org.junit.Test
+ public void testSAMLRequest() throws Exception {
+ // Mock up a Request
+ FedizContext config = getFederationConfigurator().getFedizContext("ROOT");
+
+ HttpServletRequest req = EasyMock.createMock(HttpServletRequest.class);
+ EasyMock.expect(req.getRequestURL()).andReturn(new StringBuffer(TEST_REQUEST_URL)).times(1, 2);
+ EasyMock.expect(req.getContextPath()).andReturn(TEST_REQUEST_URI);
+ EasyMock.expect(req.getRequestURI()).andReturn(TEST_REQUEST_URI).times(1, 2);
+ EasyMock.replay(req);
+
+ FedizProcessor wfProc = new SAMLProcessorImpl();
+ RedirectionResponse response = wfProc.createSignInRequest(req, config);
+
+ String redirectionURL = response.getRedirectionURL();
+ String samlRequest =
+ redirectionURL.substring(redirectionURL.indexOf("SAMLRequest=") + "SAMLRequest=".length(),
+ redirectionURL.indexOf("RelayState=") - 1);
+
+ byte[] deflatedToken = Base64.decode(URLDecoder.decode(samlRequest, "UTF-8"));
+ InputStream tokenStream = CompressionUtils.inflate(deflatedToken);
+
+ Document requestDoc = DOMUtils.readXml(new InputStreamReader(tokenStream, "UTF-8"));
+ AuthnRequest request =
+ (AuthnRequest)OpenSAMLUtil.fromDom(requestDoc.getDocumentElement());
+
+ Assert.assertEquals(TEST_REQUEST_URL, request.getIssuer().getValue());
+ Assert.assertEquals(TEST_REQUEST_URL, request.getAssertionConsumerServiceURL());
+ }
+
+ @org.junit.Test
+ public void testSignedSAMLRequest() throws Exception {
+ // Mock up a Request
+ FedizContext config = getFederationConfigurator().getFedizContext("SIGNED_ROOT");
+
+ HttpServletRequest req = EasyMock.createMock(HttpServletRequest.class);
+ EasyMock.expect(req.getRequestURL()).andReturn(new StringBuffer(TEST_REQUEST_URL)).times(1, 2);
+ EasyMock.expect(req.getContextPath()).andReturn(TEST_REQUEST_URI);
+ EasyMock.expect(req.getRequestURI()).andReturn(TEST_REQUEST_URI).times(1, 2);
+ EasyMock.replay(req);
+
+ FedizProcessor wfProc = new SAMLProcessorImpl();
+ RedirectionResponse response = wfProc.createSignInRequest(req, config);
+
+ String redirectionURL = response.getRedirectionURL();
+ String signature =
+ redirectionURL.substring(redirectionURL.indexOf("Signature=") + "Signature=".length());
+ Assert.assertTrue(signature != null && signature.length() > 0);
+ }
+
+}
\ No newline at end of file