You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cxf.apache.org by co...@apache.org on 2018/09/24 21:57:11 UTC

[cxf-fediz] branch master updated: Also test for required claims for SAML SSO

This is an automated email from the ASF dual-hosted git repository.

coheigea pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cxf-fediz.git


The following commit(s) were added to refs/heads/master by this push:
     new d70ec98  Also test for required claims for SAML SSO
d70ec98 is described below

commit d70ec98dfe3c750628dc830b4482f75350a394b8
Author: Colm O hEigeartaigh <co...@apache.org>
AuthorDate: Mon Sep 24 17:56:52 2018 -0400

    Also test for required claims for SAML SSO
---
 .../core/processor/AbstractFedizProcessor.java     |  24 +++-
 .../core/processor/FederationProcessorImpl.java    |  25 +---
 .../fediz/core/processor/SAMLProcessorImpl.java    |   4 +
 .../fediz/core/AbstractSAMLCallbackHandler.java    |  26 +++++
 .../cxf/fediz/core/samlsso/SAMLResponseTest.java   | 128 +++++++++++++++++++--
 .../src/test/resources/fediz_test_config_saml.xml  |  29 +++++
 6 files changed, 199 insertions(+), 37 deletions(-)

diff --git a/plugins/core/src/main/java/org/apache/cxf/fediz/core/processor/AbstractFedizProcessor.java b/plugins/core/src/main/java/org/apache/cxf/fediz/core/processor/AbstractFedizProcessor.java
index f6b3c9e..71a04d3 100644
--- a/plugins/core/src/main/java/org/apache/cxf/fediz/core/processor/AbstractFedizProcessor.java
+++ b/plugins/core/src/main/java/org/apache/cxf/fediz/core/processor/AbstractFedizProcessor.java
@@ -123,7 +123,7 @@ public abstract class AbstractFedizProcessor implements FedizProcessor {
         for (Claim c : claims) {
             if (roleURI.equals(c.getClaimType())) {
                 Object oValue = c.getValue();
-                if ((oValue instanceof String) && !"".equals((String) oValue)) {
+                if ((oValue instanceof String) && !"".equals(oValue)) {
                     roles = Collections.singletonList((String) oValue);
                 } else if ((oValue instanceof List<?>) && !((List<?>) oValue).isEmpty()) {
                     @SuppressWarnings("unchecked")
@@ -158,4 +158,26 @@ public abstract class AbstractFedizProcessor implements FedizProcessor {
         return reply;
     }
 
+    protected void testForMandatoryClaims(String roleURI,
+                                        List<org.apache.cxf.fediz.core.config.Claim> requestedClaims,
+                                        List<org.apache.cxf.fediz.core.Claim> receivedClaims
+    ) throws ProcessingException {
+        if (requestedClaims != null) {
+            for (org.apache.cxf.fediz.core.config.Claim requestedClaim : requestedClaims) {
+                if (!requestedClaim.isOptional()) {
+                    boolean found = false;
+                    for (org.apache.cxf.fediz.core.Claim receivedClaim : receivedClaims) {
+                        if (requestedClaim.getType().equals(receivedClaim.getClaimType().toString())) {
+                            found = true;
+                            break;
+                        }
+                    }
+                    if (!found) {
+                        LOG.warn("Mandatory claim {} not found in token", requestedClaim.getType());
+                        throw new ProcessingException("Mandatory claim not found in token", TYPE.INVALID_REQUEST);
+                    }
+                }
+            }
+        }
+    }
 }
diff --git a/plugins/core/src/main/java/org/apache/cxf/fediz/core/processor/FederationProcessorImpl.java b/plugins/core/src/main/java/org/apache/cxf/fediz/core/processor/FederationProcessorImpl.java
index 2d1c2bc..f62ca7e 100644
--- a/plugins/core/src/main/java/org/apache/cxf/fediz/core/processor/FederationProcessorImpl.java
+++ b/plugins/core/src/main/java/org/apache/cxf/fediz/core/processor/FederationProcessorImpl.java
@@ -221,7 +221,7 @@ public class FederationProcessorImpl extends AbstractFedizProcessor {
         List<Claim> claims = validatorResponse.getClaims();
         
         testForMandatoryClaims(config.getProtocol().getRoleURI(),
-                ((FederationProtocol)config.getProtocol()).getClaimTypesRequested(),
+                config.getProtocol().getClaimTypesRequested(),
                 claims);
 
         List<ClaimsProcessor> processors = config.getClaimsProcessor();
@@ -734,29 +734,6 @@ public class FederationProcessorImpl extends AbstractFedizProcessor {
         return wReq;
     }
 
-    private void testForMandatoryClaims(String roleURI,
-                                        List<org.apache.cxf.fediz.core.config.Claim> requestedClaims,
-                                        List<org.apache.cxf.fediz.core.Claim> receivedClaims
-    ) throws ProcessingException {
-        if (requestedClaims != null) {
-            for (org.apache.cxf.fediz.core.config.Claim requestedClaim : requestedClaims) {
-                if (!requestedClaim.isOptional()) {
-                    boolean found = false;
-                    for (org.apache.cxf.fediz.core.Claim receivedClaim : receivedClaims) {
-                        if (requestedClaim.getType().equals(receivedClaim.getClaimType().toString())) {
-                            found = true;
-                            break;
-                        }
-                    }
-                    if (!found) {
-                        LOG.warn("Mandatory claim {} not found in token", requestedClaim.getType());
-                        throw new ProcessingException("Mandatory claim not found in token", TYPE.INVALID_REQUEST);
-                    }
-                }
-            }
-        }
-    }
-
     private static class DecryptionCallbackHandler implements CallbackHandler {
 
         private final String password;
diff --git a/plugins/core/src/main/java/org/apache/cxf/fediz/core/processor/SAMLProcessorImpl.java b/plugins/core/src/main/java/org/apache/cxf/fediz/core/processor/SAMLProcessorImpl.java
index b44a961..9bc26f0 100644
--- a/plugins/core/src/main/java/org/apache/cxf/fediz/core/processor/SAMLProcessorImpl.java
+++ b/plugins/core/src/main/java/org/apache/cxf/fediz/core/processor/SAMLProcessorImpl.java
@@ -224,6 +224,10 @@ public class SAMLProcessorImpl extends AbstractFedizProcessor {
 
         List<Claim> claims = validatorResponse.getClaims();
 
+        testForMandatoryClaims(config.getProtocol().getRoleURI(),
+                               config.getProtocol().getClaimTypesRequested(),
+                               claims);
+
         if (config.getClaimsProcessor() != null) {
             List<ClaimsProcessor> processors = config.getClaimsProcessor();
             if (processors != null) {
diff --git a/plugins/core/src/test/java/org/apache/cxf/fediz/core/AbstractSAMLCallbackHandler.java b/plugins/core/src/test/java/org/apache/cxf/fediz/core/AbstractSAMLCallbackHandler.java
index fe90905..ef750a8 100644
--- a/plugins/core/src/test/java/org/apache/cxf/fediz/core/AbstractSAMLCallbackHandler.java
+++ b/plugins/core/src/test/java/org/apache/cxf/fediz/core/AbstractSAMLCallbackHandler.java
@@ -93,6 +93,7 @@ public abstract class AbstractSAMLCallbackHandler implements CallbackHandler {
     protected String customClaimName = CLAIM_TYPE_LANGUAGE.toString();
     protected String attributeNameFormat = "urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified";
     protected boolean useNameFormatAsNamespace;
+    private boolean addGivenName;
 
     public void setSubjectConfirmationData(SubjectConfirmationDataBean subjectConfirmationData) {
         this.subjectConfirmationData = subjectConfirmationData;
@@ -363,6 +364,23 @@ public abstract class AbstractSAMLCallbackHandler implements CallbackHandler {
             }
             attributeList.add(attributeBean2);
 
+            if (addGivenName) {
+                AttributeBean attributeBean3 = new AttributeBean();
+                if (subjectBean != null) {
+                    //SAML 1.1
+                    attributeBean3.setSimpleName(getNameOfClaimType(ClaimTypes.FIRSTNAME.toString()));
+                    //QualifiedName maps to AttributeNamespace in SAML1ComponentBuilder.createSamlv1Attribute()
+                    attributeBean3.setQualifiedName(getNamespaceOfClaimType((ClaimTypes.FIRSTNAME.toString())));
+
+                } else {
+                    //SAML 2.0
+                    attributeBean3.setQualifiedName((ClaimTypes.FIRSTNAME.toString()));
+                    attributeBean3.setNameFormat(this.getAttributeNameFormat());
+                }
+                attributeBean3.addAttributeValue("alice");
+                attributeList.add(attributeBean3);
+            }
+
             attrStateBean.setSamlAttributes(attributeList);
             callback.setAttributeStatementData(Collections.singletonList(attrStateBean));
 
@@ -434,4 +452,12 @@ public abstract class AbstractSAMLCallbackHandler implements CallbackHandler {
     public void setAlsoAddAuthnStatement(boolean alsoAddAuthnStatement) {
         this.alsoAddAuthnStatement = alsoAddAuthnStatement;
     }
+
+    public boolean isAddGivenName() {
+        return addGivenName;
+    }
+
+    public void setAddGivenName(boolean addGivenName) {
+        this.addGivenName = addGivenName;
+    }
 }
diff --git a/plugins/core/src/test/java/org/apache/cxf/fediz/core/samlsso/SAMLResponseTest.java b/plugins/core/src/test/java/org/apache/cxf/fediz/core/samlsso/SAMLResponseTest.java
index 5334faa..3817169 100644
--- a/plugins/core/src/test/java/org/apache/cxf/fediz/core/samlsso/SAMLResponseTest.java
+++ b/plugins/core/src/test/java/org/apache/cxf/fediz/core/samlsso/SAMLResponseTest.java
@@ -22,6 +22,7 @@ package org.apache.cxf.fediz.core.samlsso;
 import java.io.File;
 import java.io.IOException;
 import java.math.BigInteger;
+import java.net.URI;
 import java.net.URL;
 import java.net.URLEncoder;
 import java.nio.charset.StandardCharsets;
@@ -48,7 +49,6 @@ import org.apache.cxf.fediz.core.AbstractSAMLCallbackHandler;
 import org.apache.cxf.fediz.core.AbstractSAMLCallbackHandler.MultiValue;
 import org.apache.cxf.fediz.core.Claim;
 import org.apache.cxf.fediz.core.ClaimTypes;
-import org.apache.cxf.fediz.core.FedizConstants;
 import org.apache.cxf.fediz.core.KeystoreCallbackHandler;
 import org.apache.cxf.fediz.core.RequestState;
 import org.apache.cxf.fediz.core.SAML1CallbackHandler;
@@ -200,7 +200,8 @@ public class SAMLResponseTest {
         Assert.assertEquals("Two roles must be found", 2, wfRes.getRoles()
                             .size());
         Assert.assertEquals("Audience wrong", TEST_REQUEST_URL, wfRes.getAudience());
-        assertClaims(wfRes.getClaims(), FedizConstants.DEFAULT_ROLE_URI.toString());
+        assertClaims(wfRes.getClaims(), ClaimTypes.COUNTRY);
+        assertClaims(wfRes.getClaims(), AbstractSAMLCallbackHandler.CLAIM_TYPE_LANGUAGE);
     }
 
     /**
@@ -443,7 +444,8 @@ public class SAMLResponseTest {
         Assert.assertEquals("Issuer wrong", TEST_IDP_ISSUER, wfRes.getIssuer());
         Assert.assertEquals("Two roles must be found", 2, wfRes.getRoles().size());
         Assert.assertEquals("Audience wrong", TEST_REQUEST_URL, wfRes.getAudience());
-        assertClaims(wfRes.getClaims(), callbackHandler.getRoleAttributeName());
+        assertClaims(wfRes.getClaims(), ClaimTypes.COUNTRY);
+        assertClaims(wfRes.getClaims(), AbstractSAMLCallbackHandler.CLAIM_TYPE_LANGUAGE);
     }
 
     /**
@@ -552,7 +554,8 @@ public class SAMLResponseTest {
         Assert.assertEquals("Issuer wrong", TEST_IDP_ISSUER, wfRes.getIssuer());
         Assert.assertEquals("Two roles must be found", 2, wfRes.getRoles().size());
         Assert.assertEquals("Audience wrong", TEST_REQUEST_URL, wfRes.getAudience());
-        assertClaims(wfRes.getClaims(), callbackHandler.getRoleAttributeName());
+        assertClaims(wfRes.getClaims(), ClaimTypes.COUNTRY);
+        assertClaims(wfRes.getClaims(), AbstractSAMLCallbackHandler.CLAIM_TYPE_LANGUAGE);
     }
 
     /**
@@ -605,10 +608,10 @@ public class SAMLResponseTest {
         Assert.assertEquals("Principal name wrong", TEST_USER,
                             wfRes.getUsername());
         Assert.assertEquals("Issuer wrong", TEST_IDP_ISSUER, wfRes.getIssuer());
-        System.out.println("ROLE: " + wfRes.getRoles().get(0));
         Assert.assertEquals("Two roles must be found", 2, wfRes.getRoles().size());
         Assert.assertEquals("Audience wrong", TEST_REQUEST_URL, wfRes.getAudience());
-        assertClaims(wfRes.getClaims(), callbackHandler.getRoleAttributeName());
+        assertClaims(wfRes.getClaims(), ClaimTypes.COUNTRY);
+        assertClaims(wfRes.getClaims(), AbstractSAMLCallbackHandler.CLAIM_TYPE_LANGUAGE);
     }
 
     /**
@@ -1400,6 +1403,106 @@ public class SAMLResponseTest {
             // expected
         }
     }
+    
+    @org.junit.Test
+    public void validateSAMLResponseWithRequiredClaims() throws Exception {
+        // Mock up a Request
+        FedizContext config = getFederationConfigurator().getFedizContext("REQUIRED_CLAIMS");
+
+        String requestId = URLEncoder.encode(UUID.randomUUID().toString(), "UTF-8");
+
+        String relayState = URLEncoder.encode(UUID.randomUUID().toString(), "UTF-8");
+        RequestState requestState = new RequestState(TEST_REQUEST_URL,
+                                                     TEST_IDP_ISSUER,
+                                                     requestId,
+                                                     TEST_REQUEST_URL,
+                                                     (String)config.getProtocol().getIssuer(),
+                                                     null,
+                                                     relayState,
+                                                     System.currentTimeMillis());
+
+        // Create SAML Response
+        SAML2CallbackHandler callbackHandler = new SAML2CallbackHandler();
+        callbackHandler.setAlsoAddAuthnStatement(true);
+        callbackHandler.setStatement(SAML2CallbackHandler.Statement.ATTR);
+        callbackHandler.setConfirmationMethod(SAML2Constants.CONF_BEARER);
+        callbackHandler.setIssuer(TEST_IDP_ISSUER);
+        callbackHandler.setSubjectName(TEST_USER);
+        callbackHandler.setAddGivenName(true);
+
+        String responseStr = createSamlResponseStr(callbackHandler, requestId);
+
+        HttpServletRequest req = EasyMock.createMock(HttpServletRequest.class);
+        EasyMock.expect(req.getRequestURL()).andReturn(new StringBuffer(TEST_REQUEST_URL));
+        EasyMock.expect(req.getRemoteAddr()).andReturn(TEST_CLIENT_ADDRESS);
+        EasyMock.replay(req);
+
+        FedizRequest wfReq = new FedizRequest();
+        wfReq.setResponseToken(responseStr);
+        wfReq.setState(relayState);
+        wfReq.setRequest(req);
+        wfReq.setRequestState(requestState);
+
+        FedizProcessor wfProc = new SAMLProcessorImpl();
+        FedizResponse wfRes = wfProc.processRequest(wfReq, config);
+
+        Assert.assertEquals("Principal name wrong", TEST_USER,
+                            wfRes.getUsername());
+        Assert.assertEquals("Issuer wrong", TEST_IDP_ISSUER, wfRes.getIssuer());
+        Assert.assertEquals("Two roles must be found", 2, wfRes.getRoles()
+                            .size());
+        Assert.assertEquals("Audience wrong", TEST_REQUEST_URL, wfRes.getAudience());
+        assertClaims(wfRes.getClaims(), ClaimTypes.COUNTRY);
+        assertClaims(wfRes.getClaims(), AbstractSAMLCallbackHandler.CLAIM_TYPE_LANGUAGE);
+        assertClaims(wfRes.getClaims(), ClaimTypes.FIRSTNAME);
+    }
+    
+    @org.junit.Test
+    public void validateSAMLResponseWithoutRequiredClaims() throws Exception {
+        // Mock up a Request
+        FedizContext config = getFederationConfigurator().getFedizContext("REQUIRED_CLAIMS");
+
+        String requestId = URLEncoder.encode(UUID.randomUUID().toString(), "UTF-8");
+
+        String relayState = URLEncoder.encode(UUID.randomUUID().toString(), "UTF-8");
+        RequestState requestState = new RequestState(TEST_REQUEST_URL,
+                                                     TEST_IDP_ISSUER,
+                                                     requestId,
+                                                     TEST_REQUEST_URL,
+                                                     (String)config.getProtocol().getIssuer(),
+                                                     null,
+                                                     relayState,
+                                                     System.currentTimeMillis());
+
+        // Create SAML Response
+        SAML2CallbackHandler callbackHandler = new SAML2CallbackHandler();
+        callbackHandler.setAlsoAddAuthnStatement(true);
+        callbackHandler.setStatement(SAML2CallbackHandler.Statement.ATTR);
+        callbackHandler.setConfirmationMethod(SAML2Constants.CONF_BEARER);
+        callbackHandler.setIssuer(TEST_IDP_ISSUER);
+        callbackHandler.setSubjectName(TEST_USER);
+
+        String responseStr = createSamlResponseStr(callbackHandler, requestId);
+
+        HttpServletRequest req = EasyMock.createMock(HttpServletRequest.class);
+        EasyMock.expect(req.getRequestURL()).andReturn(new StringBuffer(TEST_REQUEST_URL));
+        EasyMock.expect(req.getRemoteAddr()).andReturn(TEST_CLIENT_ADDRESS);
+        EasyMock.replay(req);
+
+        FedizRequest wfReq = new FedizRequest();
+        wfReq.setResponseToken(responseStr);
+        wfReq.setState(relayState);
+        wfReq.setRequest(req);
+        wfReq.setRequestState(requestState);
+
+        FedizProcessor wfProc = new SAMLProcessorImpl();
+        try {
+            wfProc.processRequest(wfReq, config);
+            fail("Failure expected on a required claim that isn't present");
+        } catch (ProcessingException ex) {
+            // expected
+        }
+    }
 
     private String createSamlResponseStr(String requestId) throws Exception {
         // Create SAML Assertion
@@ -1550,14 +1653,15 @@ public class SAMLResponseTest {
         signableObject.releaseChildrenDOM(true);
     }
 
-    private void assertClaims(List<Claim> claims, String roleClaimType) {
+    private void assertClaims(List<Claim> claims, URI claimType) {
+        boolean found = false;
         for (Claim c : claims) {
-            Assert.assertTrue("Invalid ClaimType URI: " + c.getClaimType(),
-                              c.getClaimType().toString().equals(roleClaimType)
-                              || c.getClaimType().equals(ClaimTypes.COUNTRY)
-                              || c.getClaimType().equals(AbstractSAMLCallbackHandler.CLAIM_TYPE_LANGUAGE)
-                              );
+            if (c.getClaimType().equals(claimType)) {
+                found = true;
+                break;
+            }
         }
+        Assert.assertTrue(found);
     }
 
     private String encodeResponse(Element response) throws IOException {
diff --git a/plugins/core/src/test/resources/fediz_test_config_saml.xml b/plugins/core/src/test/resources/fediz_test_config_saml.xml
index 16eaa80..5d6ca03 100644
--- a/plugins/core/src/test/resources/fediz_test_config_saml.xml
+++ b/plugins/core/src/test/resources/fediz_test_config_saml.xml
@@ -274,4 +274,33 @@
         <logoutRedirectTo>/redir.html</logoutRedirectTo>
 	</contextConfig>
 	
+	<contextConfig name="REQUIRED_CLAIMS">
+		<audienceUris>
+			<audienceItem>http://host_one:port/url</audienceItem>
+		</audienceUris>
+		<certificateStores>
+			<trustManager>
+				<keyStore file="ststrust.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="samlProtocolType" version="1.2">
+			<issuer>http://url_to_the_issuer</issuer>
+			<roleDelimiter>;</roleDelimiter>
+			<roleURI>http://schemas.xmlsoap.org/ws/2005/05/identity/claims/role</roleURI>
+			<claimTypesRequested>
+				<claimType type="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname" optional="false" />
+			</claimTypesRequested>
+		</protocol>
+		
+		<logoutURL>secure/logout</logoutURL>
+        <logoutRedirectTo>/redir.html</logoutRedirectTo>
+	</contextConfig>
+	
 </FedizConfig>