cxf-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From cohei...@apache.org
Subject cxf git commit: Adding lots of SAML SSO Negative tests
Date Tue, 13 Jan 2015 12:07:22 GMT
Repository: cxf
Updated Branches:
  refs/heads/master 3c3eaf41e -> d970de924


Adding lots of SAML SSO Negative tests


Project: http://git-wip-us.apache.org/repos/asf/cxf/repo
Commit: http://git-wip-us.apache.org/repos/asf/cxf/commit/d970de92
Tree: http://git-wip-us.apache.org/repos/asf/cxf/tree/d970de92
Diff: http://git-wip-us.apache.org/repos/asf/cxf/diff/d970de92

Branch: refs/heads/master
Commit: d970de924bf5be80e39092593d79c9318003b748
Parents: 3c3eaf4
Author: Colm O hEigeartaigh <coheigea@apache.org>
Authored: Tue Jan 13 12:01:58 2015 +0000
Committer: Colm O hEigeartaigh <coheigea@apache.org>
Committed: Tue Jan 13 12:07:17 2015 +0000

----------------------------------------------------------------------
 .../saml/sso/AbstractSAMLCallbackHandler.java   |  22 +-
 .../sso/SAML2PResponseComponentBuilder.java     |  15 +
 .../saml/sso/SAMLResponseValidatorTest.java     | 282 +++++++++++-
 .../saml/sso/SAMLSSOResponseValidatorTest.java  | 450 ++++++++++++++++++-
 4 files changed, 764 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cxf/blob/d970de92/rt/rs/security/sso/saml/src/test/java/org/apache/cxf/rs/security/saml/sso/AbstractSAMLCallbackHandler.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/sso/saml/src/test/java/org/apache/cxf/rs/security/saml/sso/AbstractSAMLCallbackHandler.java
b/rt/rs/security/sso/saml/src/test/java/org/apache/cxf/rs/security/saml/sso/AbstractSAMLCallbackHandler.java
index 721c42a..61653a1 100644
--- a/rt/rs/security/sso/saml/src/test/java/org/apache/cxf/rs/security/saml/sso/AbstractSAMLCallbackHandler.java
+++ b/rt/rs/security/sso/saml/src/test/java/org/apache/cxf/rs/security/saml/sso/AbstractSAMLCallbackHandler.java
@@ -29,7 +29,6 @@ import javax.xml.parsers.DocumentBuilderFactory;
 
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
-
 import org.apache.wss4j.common.saml.SAMLCallback;
 import org.apache.wss4j.common.saml.bean.ActionBean;
 import org.apache.wss4j.common.saml.bean.AttributeBean;
@@ -44,6 +43,7 @@ import org.apache.wss4j.common.saml.bean.SubjectConfirmationDataBean;
 import org.apache.wss4j.common.saml.bean.SubjectLocalityBean;
 import org.apache.wss4j.dom.WSConstants;
 import org.apache.wss4j.dom.message.WSSecEncryptedKey;
+import org.joda.time.DateTime;
 
 /**
  * A base implementation of a Callback Handler for a SAML assertion. By default it creates
an
@@ -70,7 +70,25 @@ public abstract class AbstractSAMLCallbackHandler implements CallbackHandler
{
     protected List<Object> customAttributeValues;
     protected ConditionsBean conditions;
     protected SubjectConfirmationDataBean subjectConfirmationData;
+    protected DateTime authnInstant;
+    protected DateTime sessionNotOnOrAfter;
     
+    public DateTime getSessionNotOnOrAfter() {
+        return sessionNotOnOrAfter;
+    }
+
+    public void setSessionNotOnOrAfter(DateTime sessionNotOnOrAfter) {
+        this.sessionNotOnOrAfter = sessionNotOnOrAfter;
+    }
+
+    public DateTime getAuthnInstant() {
+        return authnInstant;
+    }
+
+    public void setAuthnInstant(DateTime authnInstant) {
+        this.authnInstant = authnInstant;
+    }
+
     public void setSubjectConfirmationData(SubjectConfirmationDataBean subjectConfirmationData)
{
         this.subjectConfirmationData = subjectConfirmationData;
     }
@@ -135,6 +153,8 @@ public abstract class AbstractSAMLCallbackHandler implements CallbackHandler
{
                 subjectLocality.setDnsAddress(subjectLocalityDnsAddress);
                 authBean.setSubjectLocality(subjectLocality);
             }
+            authBean.setAuthenticationInstant(authnInstant);
+            authBean.setSessionNotOnOrAfter(sessionNotOnOrAfter);
             authBean.setAuthenticationMethod("Password");
             callback.setAuthenticationStatementData(Collections.singletonList(authBean));
         } else if (statement == Statement.ATTR) {

http://git-wip-us.apache.org/repos/asf/cxf/blob/d970de92/rt/rs/security/sso/saml/src/test/java/org/apache/cxf/rs/security/saml/sso/SAML2PResponseComponentBuilder.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/sso/saml/src/test/java/org/apache/cxf/rs/security/saml/sso/SAML2PResponseComponentBuilder.java
b/rt/rs/security/sso/saml/src/test/java/org/apache/cxf/rs/security/saml/sso/SAML2PResponseComponentBuilder.java
index ddadecf..a902cbc 100644
--- a/rt/rs/security/sso/saml/src/test/java/org/apache/cxf/rs/security/saml/sso/SAML2PResponseComponentBuilder.java
+++ b/rt/rs/security/sso/saml/src/test/java/org/apache/cxf/rs/security/saml/sso/SAML2PResponseComponentBuilder.java
@@ -25,6 +25,7 @@ import org.joda.time.DateTime;
 import org.opensaml.Configuration;
 import org.opensaml.common.SAMLObjectBuilder;
 import org.opensaml.common.SAMLVersion;
+import org.opensaml.saml2.core.AuthnContextClassRef;
 import org.opensaml.saml2.core.Issuer;
 import org.opensaml.saml2.core.Response;
 import org.opensaml.saml2.core.Status;
@@ -47,6 +48,8 @@ public final class SAML2PResponseComponentBuilder {
     
     private static SAMLObjectBuilder<StatusMessage> statusMessageBuilder;
     
+    private static SAMLObjectBuilder<AuthnContextClassRef> authnContextClassRefBuilder;
+    
     private static XMLObjectBuilderFactory builderFactory = Configuration.getBuilderFactory();
     
     private SAML2PResponseComponentBuilder() {
@@ -122,5 +125,17 @@ public final class SAML2PResponseComponentBuilder {
         return status;
     }
     
+    @SuppressWarnings("unchecked")
+    public static AuthnContextClassRef createAuthnContextClassRef(String newAuthnContextClassRef)
{
+        if (authnContextClassRefBuilder == null) {
+            authnContextClassRefBuilder = (SAMLObjectBuilder<AuthnContextClassRef>)
+                builderFactory.getBuilder(AuthnContextClassRef.DEFAULT_ELEMENT_NAME);
+        }
+        
+        AuthnContextClassRef authnContextClassRef = authnContextClassRefBuilder.buildObject();
+        authnContextClassRef.setAuthnContextClassRef(newAuthnContextClassRef);
+        
+        return authnContextClassRef;
+    }
     
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cxf/blob/d970de92/rt/rs/security/sso/saml/src/test/java/org/apache/cxf/rs/security/saml/sso/SAMLResponseValidatorTest.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/sso/saml/src/test/java/org/apache/cxf/rs/security/saml/sso/SAMLResponseValidatorTest.java
b/rt/rs/security/sso/saml/src/test/java/org/apache/cxf/rs/security/saml/sso/SAMLResponseValidatorTest.java
index be53b9c..3e20b96 100644
--- a/rt/rs/security/sso/saml/src/test/java/org/apache/cxf/rs/security/saml/sso/SAMLResponseValidatorTest.java
+++ b/rt/rs/security/sso/saml/src/test/java/org/apache/cxf/rs/security/saml/sso/SAMLResponseValidatorTest.java
@@ -23,13 +23,15 @@ import java.io.InputStream;
 import java.security.KeyStore;
 import java.security.PrivateKey;
 import java.security.cert.X509Certificate;
+import java.util.Collections;
+import java.util.List;
 
 import javax.xml.parsers.DocumentBuilder;
 import javax.xml.parsers.DocumentBuilderFactory;
 
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
-
+import org.apache.cxf.helpers.DOMUtils;
 import org.apache.wss4j.common.crypto.Crypto;
 import org.apache.wss4j.common.crypto.CryptoType;
 import org.apache.wss4j.common.crypto.Merlin;
@@ -38,10 +40,16 @@ import org.apache.wss4j.common.saml.OpenSAMLUtil;
 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.bean.SubjectConfirmationDataBean;
 import org.apache.wss4j.common.saml.builder.SAML2Constants;
 import org.apache.wss4j.common.util.Loader;
 import org.apache.wss4j.dom.WSSConfig;
+import org.joda.time.DateTime;
+import org.opensaml.common.SAMLVersion;
 import org.opensaml.common.SignableSAMLObject;
+import org.opensaml.common.xml.SAMLConstants;
 import org.opensaml.saml2.core.Response;
 import org.opensaml.saml2.core.Status;
 import org.opensaml.xml.security.x509.BasicX509Credential;
@@ -144,6 +152,50 @@ public class SAMLResponseValidatorTest extends org.junit.Assert {
     }
     
     @org.junit.Test
+    public void testRequestDeniedStatusCode() throws Exception {
+        DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
+        docBuilderFactory.setNamespaceAware(true);
+        DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
+        Document doc = docBuilder.newDocument();
+        
+        Status status = 
+            SAML2PResponseComponentBuilder.createStatus(
+                "urn:oasis:names:tc:SAML:2.0:status:RequestDenied", null
+            );
+        Response response = 
+            SAML2PResponseComponentBuilder.createSAMLResponse(
+                "http://cxf.apache.org/saml", "http://cxf.apache.org/issuer", status
+            );
+        
+        // Create an AuthenticationAssertion
+        SAML2CallbackHandler callbackHandler = new SAML2CallbackHandler();
+        callbackHandler.setStatement(SAML2CallbackHandler.Statement.AUTHN);
+        callbackHandler.setIssuer("http://cxf.apache.org/issuer");
+        callbackHandler.setConfirmationMethod(SAML2Constants.CONF_SENDER_VOUCHES);
+        
+        SAMLCallback samlCallback = new SAMLCallback();
+        SAMLUtil.doSAMLCallback(callbackHandler, samlCallback);
+        SamlAssertionWrapper assertion = new SamlAssertionWrapper(samlCallback);
+        
+        response.getAssertions().add(assertion.getSaml2());
+        
+        Element policyElement = OpenSAMLUtil.toDom(response, doc);
+        doc.appendChild(policyElement);
+        assertNotNull(policyElement);
+        
+        Response marshalledResponse = (Response)OpenSAMLUtil.fromDom(policyElement);
+        
+        // Validate the Response
+        SAMLProtocolResponseValidator validator = new SAMLProtocolResponseValidator();
+        try {
+            validator.validateSamlResponse(marshalledResponse, null, null);
+            fail("Expected failure on an invalid SAML code");
+        } catch (WSSecurityException ex) {
+            // expected
+        }
+    }
+    
+    @org.junit.Test
     public void testResponseSignedAssertion() throws Exception {
         DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
         docBuilderFactory.setNamespaceAware(true);
@@ -202,6 +254,69 @@ public class SAMLResponseValidatorTest extends org.junit.Assert {
     }
     
     @org.junit.Test
+    public void testResponseModifiedSignedAssertion() throws Exception {
+        DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
+        docBuilderFactory.setNamespaceAware(true);
+        DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
+        Document doc = docBuilder.newDocument();
+        
+        Status status = 
+            SAML2PResponseComponentBuilder.createStatus(
+                SAMLProtocolResponseValidator.SAML2_STATUSCODE_SUCCESS, null
+            );
+        Response response = 
+            SAML2PResponseComponentBuilder.createSAMLResponse(
+                "http://cxf.apache.org/saml", "http://cxf.apache.org/issuer", status
+            );
+        
+        // Create an AuthenticationAssertion
+        SAML2CallbackHandler callbackHandler = new SAML2CallbackHandler();
+        callbackHandler.setStatement(SAML2CallbackHandler.Statement.AUTHN);
+        callbackHandler.setIssuer("http://cxf.apache.org/issuer");
+        callbackHandler.setConfirmationMethod(SAML2Constants.CONF_SENDER_VOUCHES);
+        
+        SAMLCallback samlCallback = new SAMLCallback();
+        SAMLUtil.doSAMLCallback(callbackHandler, samlCallback);
+        SamlAssertionWrapper assertion = new SamlAssertionWrapper(samlCallback);
+        
+        Crypto issuerCrypto = new Merlin();
+        KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
+        ClassLoader loader = Loader.getClassLoader(SAMLResponseValidatorTest.class);
+        InputStream input = Merlin.loadInputStream(loader, "alice.jks");
+        keyStore.load(input, "password".toCharArray());
+        ((Merlin)issuerCrypto).setKeyStore(keyStore);
+        
+        assertion.signAssertion("alice", "password", issuerCrypto, false);
+        
+        response.getAssertions().add(assertion.getSaml2());
+        
+        Element policyElement = OpenSAMLUtil.toDom(response, doc);
+        doc.appendChild(policyElement);
+        assertNotNull(policyElement);
+        
+        List<Element> assertions = 
+            DOMUtils.findAllElementsByTagNameNS(policyElement, SAMLConstants.SAML20_NS, "Assertion");
+        assertNotNull(assertions);
+        assertTrue(assertions.size() == 1);
+        assertions.get(0).setAttributeNS(null, "newattr", "http://apache.org");
+        
+        Response marshalledResponse = (Response)OpenSAMLUtil.fromDom(policyElement);
+        
+        // Validate the Response
+        SAMLProtocolResponseValidator validator = new SAMLProtocolResponseValidator();
+        try {
+           // Validate the Response
+            validator.validateSamlResponse(
+                marshalledResponse, issuerCrypto, new KeystorePasswordCallback()
+            );
+            fail("Expected failure on a bad signature");
+        } catch (WSSecurityException ex) {
+            // expected
+        }
+        
+    }
+    
+    @org.junit.Test
     public void testSignedResponse() throws Exception {
         DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
         docBuilderFactory.setNamespaceAware(true);
@@ -259,6 +374,63 @@ public class SAMLResponseValidatorTest extends org.junit.Assert {
     }
     
     @org.junit.Test
+    public void testModifiedSignedResponse() throws Exception {
+        DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
+        docBuilderFactory.setNamespaceAware(true);
+        DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
+        Document doc = docBuilder.newDocument();
+        
+        Status status = 
+            SAML2PResponseComponentBuilder.createStatus(
+                SAMLProtocolResponseValidator.SAML2_STATUSCODE_SUCCESS, null
+            );
+        Response response = 
+            SAML2PResponseComponentBuilder.createSAMLResponse(
+                "http://cxf.apache.org/saml", "http://cxf.apache.org/issuer", status
+            );
+        
+        // Create an AuthenticationAssertion
+        SAML2CallbackHandler callbackHandler = new SAML2CallbackHandler();
+        callbackHandler.setStatement(SAML2CallbackHandler.Statement.AUTHN);
+        callbackHandler.setIssuer("http://cxf.apache.org/issuer");
+        callbackHandler.setConfirmationMethod(SAML2Constants.CONF_SENDER_VOUCHES);
+        
+        SAMLCallback samlCallback = new SAMLCallback();
+        SAMLUtil.doSAMLCallback(callbackHandler, samlCallback);
+        SamlAssertionWrapper assertion = new SamlAssertionWrapper(samlCallback);
+        
+        Crypto issuerCrypto = new Merlin();
+        KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
+        ClassLoader loader = Loader.getClassLoader(SAMLResponseValidatorTest.class);
+        InputStream input = Merlin.loadInputStream(loader, "alice.jks");
+        keyStore.load(input, "password".toCharArray());
+        ((Merlin)issuerCrypto).setKeyStore(keyStore);
+        
+        response.getAssertions().add(assertion.getSaml2());
+        signResponse(response, "alice", "password", issuerCrypto, true);
+        
+        Element policyElement = OpenSAMLUtil.toDom(response, doc);
+        doc.appendChild(policyElement);
+        assertNotNull(policyElement);
+        
+        policyElement.setAttributeNS(null, "newattr", "http://apache.org");
+        
+        Response marshalledResponse = (Response)OpenSAMLUtil.fromDom(policyElement);
+        
+        // Validate the Response
+        SAMLProtocolResponseValidator validator = new SAMLProtocolResponseValidator();
+        try {
+            // Validate the Response
+            validator.validateSamlResponse(
+                marshalledResponse, issuerCrypto, new KeystorePasswordCallback()
+            );
+            fail("Expected failure on a bad signature");
+        } catch (WSSecurityException ex) {
+            // expected
+        }
+    }
+    
+    @org.junit.Test
     public void testSignedResponseNoKeyInfo() throws Exception {
         DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
         docBuilderFactory.setNamespaceAware(true);
@@ -316,6 +488,83 @@ public class SAMLResponseValidatorTest extends org.junit.Assert {
         );
     }
 
+    @org.junit.Test
+    public void testResponseInvalidVersion() throws Exception {
+        SubjectConfirmationDataBean subjectConfirmationData = new SubjectConfirmationDataBean();
+        subjectConfirmationData.setAddress("http://apache.org");
+        subjectConfirmationData.setInResponseTo("12345");
+        subjectConfirmationData.setNotAfter(new DateTime().plusMinutes(5));
+        subjectConfirmationData.setRecipient("http://recipient.apache.org");
+        
+        // Create a AuthenticationAssertion
+        SAML2CallbackHandler callbackHandler = new SAML2CallbackHandler();
+        callbackHandler.setStatement(SAML2CallbackHandler.Statement.AUTHN);
+        callbackHandler.setIssuer("http://cxf.apache.org/issuer");
+        callbackHandler.setConfirmationMethod(SAML2Constants.CONF_SENDER_VOUCHES);
+        
+        callbackHandler.setSubjectConfirmationData(subjectConfirmationData);
+        
+        ConditionsBean conditions = new ConditionsBean();
+        conditions.setNotBefore(new DateTime());
+        conditions.setNotAfter(new DateTime().plusMinutes(5));
+        
+        AudienceRestrictionBean audienceRestriction = new AudienceRestrictionBean();
+        audienceRestriction.setAudienceURIs(Collections.singletonList("http://service.apache.org"));
+        conditions.setAudienceRestrictions(Collections.singletonList(audienceRestriction));
+        callbackHandler.setConditions(conditions);
+        
+        Response response = createResponse(subjectConfirmationData, callbackHandler);
+        response.setVersion(SAMLVersion.VERSION_10);
+        
+        // Validate the Response
+        SAMLProtocolResponseValidator protocolValidator = new SAMLProtocolResponseValidator();
+        
+        try {
+            protocolValidator.validateSamlResponse(response, null, null);
+            fail("Expected failure on bad response");
+        } catch (WSSecurityException ex) {
+            // expected
+        }
+    }
+    
+    @org.junit.Test
+    public void testAssertionBadSubjectConfirmationMethod() throws Exception {
+        SubjectConfirmationDataBean subjectConfirmationData = new SubjectConfirmationDataBean();
+        subjectConfirmationData.setAddress("http://apache.org");
+        subjectConfirmationData.setInResponseTo("12345");
+        subjectConfirmationData.setNotAfter(new DateTime().plusMinutes(5));
+        subjectConfirmationData.setRecipient("http://recipient.apache.org");
+        
+        // Create a AuthenticationAssertion
+        SAML2CallbackHandler callbackHandler = new SAML2CallbackHandler();
+        callbackHandler.setStatement(SAML2CallbackHandler.Statement.AUTHN);
+        callbackHandler.setIssuer("http://cxf.apache.org/issuer");
+        callbackHandler.setConfirmationMethod("xyz");
+        
+        callbackHandler.setSubjectConfirmationData(subjectConfirmationData);
+        
+        ConditionsBean conditions = new ConditionsBean();
+        conditions.setNotBefore(new DateTime());
+        conditions.setNotAfter(new DateTime().plusMinutes(5));
+        
+        AudienceRestrictionBean audienceRestriction = new AudienceRestrictionBean();
+        audienceRestriction.setAudienceURIs(Collections.singletonList("http://service.apache.org"));
+        conditions.setAudienceRestrictions(Collections.singletonList(audienceRestriction));
+        callbackHandler.setConditions(conditions);
+        
+        Response response = createResponse(subjectConfirmationData, callbackHandler);
+        
+        // Validate the Response
+        SAMLProtocolResponseValidator protocolValidator = new SAMLProtocolResponseValidator();
+        
+        try {
+            protocolValidator.validateSamlResponse(response, null, null);
+            fail("Expected failure on bad response");
+        } catch (WSSecurityException ex) {
+            // expected
+        }
+    }
+    
     
     /**
      * Sign a SAML Response
@@ -381,4 +630,35 @@ public class SAMLResponseValidatorTest extends org.junit.Assert {
         signableObject.releaseChildrenDOM(true);
     }
     
+    private Response createResponse(
+        SubjectConfirmationDataBean subjectConfirmationData,
+        SAML2CallbackHandler callbackHandler
+    ) throws Exception {
+        DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
+        docBuilderFactory.setNamespaceAware(true);
+        DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
+        Document doc = docBuilder.newDocument();
+
+        Status status = 
+            SAML2PResponseComponentBuilder.createStatus(
+                SAMLProtocolResponseValidator.SAML2_STATUSCODE_SUCCESS, null
+            );
+        Response response = 
+            SAML2PResponseComponentBuilder.createSAMLResponse(
+                "http://cxf.apache.org/saml", "http://cxf.apache.org/issuer", status
+            );
+
+        // Create an AuthenticationAssertion
+        SAMLCallback samlCallback = new SAMLCallback();
+        SAMLUtil.doSAMLCallback(callbackHandler, samlCallback);
+        SamlAssertionWrapper assertion = new SamlAssertionWrapper(samlCallback);
+
+        response.getAssertions().add(assertion.getSaml2());
+
+        Element policyElement = OpenSAMLUtil.toDom(response, doc);
+        doc.appendChild(policyElement);
+        assertNotNull(policyElement);
+
+        return (Response)OpenSAMLUtil.fromDom(policyElement);
+    }
 }

http://git-wip-us.apache.org/repos/asf/cxf/blob/d970de92/rt/rs/security/sso/saml/src/test/java/org/apache/cxf/rs/security/saml/sso/SAMLSSOResponseValidatorTest.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/sso/saml/src/test/java/org/apache/cxf/rs/security/saml/sso/SAMLSSOResponseValidatorTest.java
b/rt/rs/security/sso/saml/src/test/java/org/apache/cxf/rs/security/saml/sso/SAMLSSOResponseValidatorTest.java
index ebe08d5..6235532 100644
--- a/rt/rs/security/sso/saml/src/test/java/org/apache/cxf/rs/security/saml/sso/SAMLSSOResponseValidatorTest.java
+++ b/rt/rs/security/sso/saml/src/test/java/org/apache/cxf/rs/security/saml/sso/SAMLSSOResponseValidatorTest.java
@@ -19,13 +19,22 @@
 
 package org.apache.cxf.rs.security.saml.sso;
 
+import java.io.InputStream;
+import java.security.KeyStore;
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
 import java.util.Collections;
+import java.util.List;
 
 import javax.xml.parsers.DocumentBuilder;
 import javax.xml.parsers.DocumentBuilderFactory;
 
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
+import org.apache.wss4j.common.crypto.Crypto;
+import org.apache.wss4j.common.crypto.CryptoType;
+import org.apache.wss4j.common.crypto.Merlin;
 import org.apache.wss4j.common.ext.WSSecurityException;
 import org.apache.wss4j.common.saml.OpenSAMLUtil;
 import org.apache.wss4j.common.saml.SAMLCallback;
@@ -35,9 +44,17 @@ import org.apache.wss4j.common.saml.bean.AudienceRestrictionBean;
 import org.apache.wss4j.common.saml.bean.ConditionsBean;
 import org.apache.wss4j.common.saml.bean.SubjectConfirmationDataBean;
 import org.apache.wss4j.common.saml.builder.SAML2Constants;
+import org.apache.wss4j.common.util.Loader;
 import org.joda.time.DateTime;
+import org.opensaml.common.SignableSAMLObject;
+import org.opensaml.saml2.core.AuthnStatement;
 import org.opensaml.saml2.core.Response;
 import org.opensaml.saml2.core.Status;
+import org.opensaml.xml.security.x509.BasicX509Credential;
+import org.opensaml.xml.security.x509.X509KeyInfoGeneratorFactory;
+import org.opensaml.xml.signature.KeyInfo;
+import org.opensaml.xml.signature.Signature;
+import org.opensaml.xml.signature.SignatureConstants;
 
 /**
  * Some unit tests for the SAMLSSOResponseValidator.
@@ -194,9 +211,8 @@ public class SAMLSSOResponseValidatorTest extends org.junit.Assert {
         }
     }
     
-    private Response createResponse(
-        SubjectConfirmationDataBean subjectConfirmationData
-    ) throws Exception {
+    @org.junit.Test
+    public void testSignedResponseInvalidDestination() throws Exception {
         DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
         docBuilderFactory.setNamespaceAware(true);
         DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
@@ -217,6 +233,268 @@ public class SAMLSSOResponseValidatorTest extends org.junit.Assert {
         callbackHandler.setIssuer("http://cxf.apache.org/issuer");
         callbackHandler.setConfirmationMethod(SAML2Constants.CONF_BEARER);
         
+        ConditionsBean conditions = new ConditionsBean();
+        conditions.setNotBefore(new DateTime());
+        conditions.setNotAfter(new DateTime().plusMinutes(5));
+        AudienceRestrictionBean audienceRestriction = new AudienceRestrictionBean();
+        audienceRestriction.setAudienceURIs(Collections.singletonList("http://service.apache.org"));
+        conditions.setAudienceRestrictions(Collections.singletonList(audienceRestriction));
+        callbackHandler.setConditions(conditions);
+        
+        SubjectConfirmationDataBean subjectConfirmationData = new SubjectConfirmationDataBean();
+        subjectConfirmationData.setAddress("http://apache.org");
+        subjectConfirmationData.setInResponseTo("12345");
+        subjectConfirmationData.setNotAfter(new DateTime().plusMinutes(5));
+        subjectConfirmationData.setRecipient("http://recipient.apache.org");
+        callbackHandler.setSubjectConfirmationData(subjectConfirmationData);
+        
+        SAMLCallback samlCallback = new SAMLCallback();
+        SAMLUtil.doSAMLCallback(callbackHandler, samlCallback);
+        SamlAssertionWrapper assertion = new SamlAssertionWrapper(samlCallback);
+        
+        response.getAssertions().add(assertion.getSaml2());
+        response.setDestination("xyz");
+        
+        Crypto issuerCrypto = new Merlin();
+        KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
+        ClassLoader loader = Loader.getClassLoader(SAMLResponseValidatorTest.class);
+        InputStream input = Merlin.loadInputStream(loader, "alice.jks");
+        keyStore.load(input, "password".toCharArray());
+        ((Merlin)issuerCrypto).setKeyStore(keyStore);
+        
+        signResponse(response, "alice", "password", issuerCrypto, true);
+        
+        Element policyElement = OpenSAMLUtil.toDom(response, doc);
+        doc.appendChild(policyElement);
+        assertNotNull(policyElement);
+        
+        Response marshalledResponse = (Response)OpenSAMLUtil.fromDom(policyElement);
+        
+        // Validate the Response
+        SAMLSSOResponseValidator validator = new SAMLSSOResponseValidator();
+        validator.setIssuerIDP("http://cxf.apache.org/issuer");
+        validator.setAssertionConsumerURL("http://recipient.apache.org");
+        validator.setClientAddress("http://apache.org");
+        validator.setRequestId("12345");
+        validator.setSpIdentifier("http://service.apache.org");
+        try {
+            validator.validateSamlResponse(marshalledResponse, false);
+            fail("Expected failure on bad response");
+        } catch (WSSecurityException ex) {
+            // expected
+        }
+    }
+    
+    @org.junit.Test
+    public void testResponseInvalidIssuer() throws Exception {
+        SubjectConfirmationDataBean subjectConfirmationData = new SubjectConfirmationDataBean();
+        subjectConfirmationData.setAddress("http://apache.org");
+        subjectConfirmationData.setInResponseTo("12345");
+        subjectConfirmationData.setNotAfter(new DateTime().plusMinutes(5));
+        subjectConfirmationData.setRecipient("http://recipient.apache.org");
+        
+        Response response = createResponse(subjectConfirmationData);
+        response.setIssuer(SAML2PResponseComponentBuilder.createIssuer("xyz"));
+        
+        // Validate the Response
+        SAMLSSOResponseValidator validator = new SAMLSSOResponseValidator();
+        validator.setIssuerIDP("http://cxf.apache.org/issuer");
+        validator.setAssertionConsumerURL("http://recipient.apache.org");
+        validator.setClientAddress("http://apache.org");
+        validator.setRequestId("12345");
+        validator.setSpIdentifier("http://service.apache.org");
+        try {
+            validator.validateSamlResponse(response, false);
+            fail("Expected failure on bad response");
+        } catch (WSSecurityException ex) {
+            // expected
+        }
+    }
+    
+    @org.junit.Test
+    public void testMissingAuthnStatement() throws Exception {
+        SubjectConfirmationDataBean subjectConfirmationData = new SubjectConfirmationDataBean();
+        subjectConfirmationData.setAddress("http://apache.org");
+        subjectConfirmationData.setInResponseTo("12345");
+        subjectConfirmationData.setNotAfter(new DateTime().plusMinutes(5));
+        subjectConfirmationData.setRecipient("http://recipient.apache.org");
+        
+        Response response = createResponse(subjectConfirmationData);
+        response.getAssertions().get(0).getAuthnStatements().clear();
+        
+        // Validate the Response
+        SAMLSSOResponseValidator validator = new SAMLSSOResponseValidator();
+        validator.setIssuerIDP("http://cxf.apache.org/issuer");
+        validator.setAssertionConsumerURL("http://recipient.apache.org");
+        validator.setClientAddress("http://apache.org");
+        validator.setRequestId("12345");
+        validator.setSpIdentifier("http://service.apache.org");
+        try {
+            validator.validateSamlResponse(response, false);
+            fail("Expected failure on bad response");
+        } catch (WSSecurityException ex) {
+            // expected
+        }
+    }
+    
+    @org.junit.Test
+    public void testNoSubjectConfirmationData() throws Exception {
+        Response response = createResponse(null);
+        
+        // Validate the Response
+        SAMLSSOResponseValidator validator = new SAMLSSOResponseValidator();
+        validator.setIssuerIDP("http://cxf.apache.org/issuer");
+        validator.setAssertionConsumerURL("http://recipient.apache.org");
+        validator.setClientAddress("http://apache.org");
+        validator.setRequestId("12345");
+        validator.setSpIdentifier("http://service.apache.org");
+        try {
+            validator.validateSamlResponse(response, false);
+            fail("Expected failure on bad response");
+        } catch (WSSecurityException ex) {
+            // expected
+        }
+    }
+    
+    @org.junit.Test
+    public void testEmptyAudienceRestriction() throws Exception {
+        SubjectConfirmationDataBean subjectConfirmationData = new SubjectConfirmationDataBean();
+        subjectConfirmationData.setAddress("http://apache.org");
+        subjectConfirmationData.setInResponseTo("12345");
+        subjectConfirmationData.setNotAfter(new DateTime().plusMinutes(5));
+        subjectConfirmationData.setRecipient("http://recipient.apache.org");
+
+        AudienceRestrictionBean audienceRestriction = new AudienceRestrictionBean();
+        Response response = 
+            createResponse(subjectConfirmationData, 
+                           Collections.singletonList(audienceRestriction), 
+                           null);
+        
+        // Validate the Response
+        SAMLSSOResponseValidator validator = new SAMLSSOResponseValidator();
+        validator.setIssuerIDP("http://cxf.apache.org/issuer");
+        validator.setAssertionConsumerURL("http://recipient.apache.org");
+        validator.setClientAddress("http://apache.org");
+        validator.setRequestId("12345");
+        validator.setSpIdentifier("http://service.apache.org");
+        try {
+            validator.validateSamlResponse(response, false);
+            fail("Expected failure on bad response");
+        } catch (WSSecurityException ex) {
+            // expected
+        }
+    }
+    
+    @org.junit.Test
+    public void testBadAudienceRestriction() throws Exception {
+        SubjectConfirmationDataBean subjectConfirmationData = new SubjectConfirmationDataBean();
+        subjectConfirmationData.setAddress("http://apache.org");
+        subjectConfirmationData.setInResponseTo("12345");
+        subjectConfirmationData.setNotAfter(new DateTime().plusMinutes(5));
+        subjectConfirmationData.setRecipient("http://recipient.apache.org");
+
+        AudienceRestrictionBean audienceRestriction = new AudienceRestrictionBean();
+        audienceRestriction.setAudienceURIs(
+            Collections.singletonList("http://unknown-service.apache.org"));
+        Response response = 
+            createResponse(subjectConfirmationData, 
+                           Collections.singletonList(audienceRestriction), 
+                           null);
+        
+        // Validate the Response
+        SAMLSSOResponseValidator validator = new SAMLSSOResponseValidator();
+        validator.setIssuerIDP("http://cxf.apache.org/issuer");
+        validator.setAssertionConsumerURL("http://recipient.apache.org");
+        validator.setClientAddress("http://apache.org");
+        validator.setRequestId("12345");
+        validator.setSpIdentifier("http://service.apache.org");
+        try {
+            validator.validateSamlResponse(response, false);
+            fail("Expected failure on bad response");
+        } catch (WSSecurityException ex) {
+            // expected
+        }
+    }
+    
+    @org.junit.Test
+    public void testAudienceRestrictionMultipleValues() throws Exception {
+        SubjectConfirmationDataBean subjectConfirmationData = new SubjectConfirmationDataBean();
+        subjectConfirmationData.setAddress("http://apache.org");
+        subjectConfirmationData.setInResponseTo("12345");
+        subjectConfirmationData.setNotAfter(new DateTime().plusMinutes(5));
+        subjectConfirmationData.setRecipient("http://recipient.apache.org");
+
+        List<String> values = new ArrayList<String>();
+        values.add("http://unknown-service.apache.org");
+        values.add("http://service.apache.org");
+        
+        AudienceRestrictionBean audienceRestriction = new AudienceRestrictionBean();
+        audienceRestriction.setAudienceURIs(values);
+        Response response = 
+            createResponse(subjectConfirmationData, 
+                           Collections.singletonList(audienceRestriction), 
+                           null);
+        
+        // Validate the Response
+        SAMLSSOResponseValidator validator = new SAMLSSOResponseValidator();
+        validator.setIssuerIDP("http://cxf.apache.org/issuer");
+        validator.setAssertionConsumerURL("http://recipient.apache.org");
+        validator.setClientAddress("http://apache.org");
+        validator.setRequestId("12345");
+        validator.setSpIdentifier("http://service.apache.org");
+        
+        validator.validateSamlResponse(response, false);
+    }
+    
+    @org.junit.Test
+    public void testMultipleAudienceRestrictions() throws Exception {
+        SubjectConfirmationDataBean subjectConfirmationData = new SubjectConfirmationDataBean();
+        subjectConfirmationData.setAddress("http://apache.org");
+        subjectConfirmationData.setInResponseTo("12345");
+        subjectConfirmationData.setNotAfter(new DateTime().plusMinutes(5));
+        subjectConfirmationData.setRecipient("http://recipient.apache.org");
+
+        List<AudienceRestrictionBean> audienceRestrictions = 
+            new ArrayList<AudienceRestrictionBean>();
+        
+        AudienceRestrictionBean audienceRestriction = new AudienceRestrictionBean();
+        audienceRestriction.setAudienceURIs(
+            Collections.singletonList("http://unknown-service.apache.org"));
+        audienceRestrictions.add(audienceRestriction);
+        
+        audienceRestriction = new AudienceRestrictionBean();
+        audienceRestriction.setAudienceURIs(
+            Collections.singletonList("http://service.apache.org"));
+        audienceRestrictions.add(audienceRestriction);
+        
+        Response response = 
+            createResponse(subjectConfirmationData, audienceRestrictions, null);
+        
+        // Validate the Response
+        SAMLSSOResponseValidator validator = new SAMLSSOResponseValidator();
+        validator.setIssuerIDP("http://cxf.apache.org/issuer");
+        validator.setAssertionConsumerURL("http://recipient.apache.org");
+        validator.setClientAddress("http://apache.org");
+        validator.setRequestId("12345");
+        validator.setSpIdentifier("http://service.apache.org");
+        
+        validator.validateSamlResponse(response, false);
+    }
+    
+    @org.junit.Test
+    public void testAssertionBadIssuer() throws Exception {
+        SubjectConfirmationDataBean subjectConfirmationData = new SubjectConfirmationDataBean();
+        subjectConfirmationData.setAddress("http://apache.org");
+        subjectConfirmationData.setInResponseTo("12345");
+        subjectConfirmationData.setNotAfter(new DateTime().plusMinutes(5));
+        subjectConfirmationData.setRecipient("http://recipient.apache.org");
+        
+        // Create a AuthenticationAssertion
+        SAML2CallbackHandler callbackHandler = new SAML2CallbackHandler();
+        callbackHandler.setStatement(SAML2CallbackHandler.Statement.AUTHN);
+        callbackHandler.setIssuer("http://cxf.apache.org/bad-issuer");
+        callbackHandler.setConfirmationMethod(SAML2Constants.CONF_BEARER);
+        
         callbackHandler.setSubjectConfirmationData(subjectConfirmationData);
         
         ConditionsBean conditions = new ConditionsBean();
@@ -228,17 +506,183 @@ public class SAMLSSOResponseValidatorTest extends org.junit.Assert
{
         conditions.setAudienceRestrictions(Collections.singletonList(audienceRestriction));
         callbackHandler.setConditions(conditions);
         
+        Response response = createResponse(subjectConfirmationData, callbackHandler);
+        
+        // Validate the Response
+        SAMLSSOResponseValidator validator = new SAMLSSOResponseValidator();
+        validator.setIssuerIDP("http://cxf.apache.org/issuer");
+        validator.setAssertionConsumerURL("http://recipient.apache.org");
+        validator.setClientAddress("http://apache.org");
+        validator.setRequestId("12345");
+        validator.setSpIdentifier("http://service.apache.org");
+        
+        try {
+            validator.validateSamlResponse(response, false);
+            fail("Expected failure on bad response");
+        } catch (WSSecurityException ex) {
+            // expected
+        }
+    }
+    
+    private Response createResponse(
+        SubjectConfirmationDataBean subjectConfirmationData
+    ) throws Exception {
+        return createResponse(subjectConfirmationData, null, null);
+    }
+    
+    private Response createResponse(
+        SubjectConfirmationDataBean subjectConfirmationData,
+        List<AudienceRestrictionBean> audienceRestrictions,
+        String authnClassRef
+    ) throws Exception {
+        DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
+        docBuilderFactory.setNamespaceAware(true);
+        DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
+        Document doc = docBuilder.newDocument();
+        
+        Status status = 
+            SAML2PResponseComponentBuilder.createStatus(
+                SAMLProtocolResponseValidator.SAML2_STATUSCODE_SUCCESS, null
+            );
+        Response response = 
+            SAML2PResponseComponentBuilder.createSAMLResponse(
+                "http://cxf.apache.org/saml", "http://cxf.apache.org/issuer", status
+            );
+        
+        // Create an AuthenticationAssertion
+        SAML2CallbackHandler callbackHandler = new SAML2CallbackHandler();
+        callbackHandler.setStatement(SAML2CallbackHandler.Statement.AUTHN);
+        callbackHandler.setIssuer("http://cxf.apache.org/issuer");
+        callbackHandler.setConfirmationMethod(SAML2Constants.CONF_BEARER);
+        
+        callbackHandler.setSubjectConfirmationData(subjectConfirmationData);
+        
+        ConditionsBean conditions = new ConditionsBean();
+        conditions.setNotBefore(new DateTime());
+        conditions.setNotAfter(new DateTime().plusMinutes(5));
+        
+        if (audienceRestrictions == null) {
+            AudienceRestrictionBean audienceRestriction = new AudienceRestrictionBean();
+            audienceRestriction.setAudienceURIs(Collections.singletonList("http://service.apache.org"));
+            conditions.setAudienceRestrictions(Collections.singletonList(audienceRestriction));
+        } else {
+            conditions.setAudienceRestrictions(audienceRestrictions);
+        }
+        callbackHandler.setConditions(conditions);
+        
         SAMLCallback samlCallback = new SAMLCallback();
         SAMLUtil.doSAMLCallback(callbackHandler, samlCallback);
         SamlAssertionWrapper assertion = new SamlAssertionWrapper(samlCallback);
         
         response.getAssertions().add(assertion.getSaml2());
         
+        if (authnClassRef != null) {
+            AuthnStatement authnStatement = 
+                response.getAssertions().get(0).getAuthnStatements().get(0);
+            authnStatement.getAuthnContext().setAuthnContextClassRef(
+                SAML2PResponseComponentBuilder.createAuthnContextClassRef(authnClassRef));
+        }
+        
         Element policyElement = OpenSAMLUtil.toDom(response, doc);
         doc.appendChild(policyElement);
         assertNotNull(policyElement);
         
         return (Response)OpenSAMLUtil.fromDom(policyElement);
     }
+ 
+    private Response createResponse(
+        SubjectConfirmationDataBean subjectConfirmationData,
+        SAML2CallbackHandler callbackHandler
+    ) throws Exception {
+        DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
+        docBuilderFactory.setNamespaceAware(true);
+        DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
+        Document doc = docBuilder.newDocument();
+
+        Status status = 
+            SAML2PResponseComponentBuilder.createStatus(
+                SAMLProtocolResponseValidator.SAML2_STATUSCODE_SUCCESS, null
+            );
+        Response response = 
+            SAML2PResponseComponentBuilder.createSAMLResponse(
+                "http://cxf.apache.org/saml", "http://cxf.apache.org/issuer", status
+            );
+
+        // Create an AuthenticationAssertion
+        SAMLCallback samlCallback = new SAMLCallback();
+        SAMLUtil.doSAMLCallback(callbackHandler, samlCallback);
+        SamlAssertionWrapper assertion = new SamlAssertionWrapper(samlCallback);
+
+        response.getAssertions().add(assertion.getSaml2());
+
+        Element policyElement = OpenSAMLUtil.toDom(response, doc);
+        doc.appendChild(policyElement);
+        assertNotNull(policyElement);
+
+        return (Response)OpenSAMLUtil.fromDom(policyElement);
+    }
     
+    /**
+     * Sign a SAML Response
+     * @throws Exception 
+     */
+    private void signResponse(
+        Response response,
+        String issuerKeyName,
+        String issuerKeyPassword,
+        Crypto issuerCrypto,
+        boolean useKeyInfo
+    ) throws Exception {
+        //
+        // Create the signature
+        //
+        Signature signature = OpenSAMLUtil.buildSignature();
+        signature.setCanonicalizationAlgorithm(SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS);
+        
+        // prepare to sign the SAML token
+        CryptoType cryptoType = new CryptoType(CryptoType.TYPE.ALIAS);
+        cryptoType.setAlias(issuerKeyName);
+        X509Certificate[] issuerCerts = issuerCrypto.getX509Certificates(cryptoType);
+        if (issuerCerts == null) {
+            throw new Exception(
+                    "No issuer certs were found to sign the SAML Assertion using issuer name:
"
+                            + issuerKeyName);
+        }
+
+        String sigAlgo = SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA1;
+        String pubKeyAlgo = issuerCerts[0].getPublicKey().getAlgorithm();
+
+        if (pubKeyAlgo.equalsIgnoreCase("DSA")) {
+            sigAlgo = SignatureConstants.ALGO_ID_SIGNATURE_DSA;
+        }
+        
+        PrivateKey privateKey = issuerCrypto.getPrivateKey(issuerKeyName, issuerKeyPassword);
+
+        signature.setSignatureAlgorithm(sigAlgo);
+
+        BasicX509Credential signingCredential = new BasicX509Credential();
+        signingCredential.setEntityCertificate(issuerCerts[0]);
+        signingCredential.setPrivateKey(privateKey);
+
+        signature.setSigningCredential(signingCredential);
+
+        if (useKeyInfo) {
+            X509KeyInfoGeneratorFactory kiFactory = new X509KeyInfoGeneratorFactory();
+            kiFactory.setEmitEntityCertificate(true);
+            
+            try {
+                KeyInfo keyInfo = kiFactory.newInstance().generate(signingCredential);
+                signature.setKeyInfo(keyInfo);
+            } catch (org.opensaml.xml.security.SecurityException ex) {
+                throw new Exception(
+                        "Error generating KeyInfo from signing credential", ex);
+            }
+        }
+
+        // add the signature to the assertion
+        SignableSAMLObject signableObject = (SignableSAMLObject) response;
+        signableObject.setSignature(signature);
+        signableObject.releaseDOM();
+        signableObject.releaseChildrenDOM(true);
+    }
 }


Mime
View raw message