[java-idp-plugin-duo] branch main updated: JDUO-47 - No Duo Context in shibboleth.authn.DuoOIDC.ContextToPrincipalMappingStrategy?

Phil Smart philip.smart at jisc.ac.uk
Thu Jun 24 11:21:49 UTC 2021


This is an automated email from the git hooks/post-receive script.

philsmart pushed a commit to branch main
in repository java-idp-plugin-duo.

View the commit online:
http://git.shibboleth.net/view/?p=java-idp-plugin-duo.git;a=commit;h=a7947a11ec9411bb26a087b8c94800d4326d2a36

The following commit(s) were added to refs/heads/main by this push:
       new  a7947a1   JDUO-47 - No Duo Context in shibboleth.authn.DuoOIDC.ContextToPrincipalMappingStrategy?
a7947a1 is described below

commit a7947a11ec9411bb26a087b8c94800d4326d2a36
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Thu Jun 24 12:21:44 2021 +0100

    JDUO-47 - No Duo Context in
    shibboleth.authn.DuoOIDC.ContextToPrincipalMappingStrategy?
    
    Added test to determine if ACR was added from the example mapping
    strategy.
    
    https://issues.shibboleth.net/jira/browse/JDUO-47
---
 .../impl/AbstractAuthnXmlFlowExecutionTests.java   | 44 +++++++++--
 .../plugin/authn/duo/impl/DuoAuthnFlowTest.java    | 91 ++++++++++++++++++++--
 .../duo-oidc-authn-config-principal-mapping.xml    | 51 ++++++++++++
 3 files changed, 172 insertions(+), 14 deletions(-)

diff --git a/idp-duo-impl/src/test/java/net/shibboleth/idp/plugin/authn/duo/impl/AbstractAuthnXmlFlowExecutionTests.java b/idp-duo-impl/src/test/java/net/shibboleth/idp/plugin/authn/duo/impl/AbstractAuthnXmlFlowExecutionTests.java
index 9cde3b4..d05ff30 100644
--- a/idp-duo-impl/src/test/java/net/shibboleth/idp/plugin/authn/duo/impl/AbstractAuthnXmlFlowExecutionTests.java
+++ b/idp-duo-impl/src/test/java/net/shibboleth/idp/plugin/authn/duo/impl/AbstractAuthnXmlFlowExecutionTests.java
@@ -140,6 +140,12 @@ public abstract class AbstractAuthnXmlFlowExecutionTests extends CustomAbstractX
     
     /** The Duo client factory to use.*/
     @Nullable private DuoOIDCClientFactory clientFactory;  
+    
+    /** 
+     * If true the default DuoContext removal cleanup hook is removed, so the
+     * context can be inspected by the test conditions.
+     */
+    private boolean removeDefaultContextCleanupHook;
 
  
     /** Constructor. */
@@ -147,6 +153,7 @@ public abstract class AbstractAuthnXmlFlowExecutionTests extends CustomAbstractX
         flowModelResources = Collections.emptyMap();
         subflows = Collections.emptyList();    
         mockProperties = Collections.emptyMap();
+        removeDefaultContextCleanupHook = false;
     }
     
     /**
@@ -225,6 +232,7 @@ public abstract class AbstractAuthnXmlFlowExecutionTests extends CustomAbstractX
         externalContext = new MockExternalContext();
         externalContext.setNativeRequest(mockRequest);
         externalContext.setNativeResponse(mockResponse);
+        removeDefaultContextCleanupHook = false;
     }
     
 
@@ -313,14 +321,16 @@ public abstract class AbstractAuthnXmlFlowExecutionTests extends CustomAbstractX
             builderContext.registerBean("shibboleth.authn.DuoOIDC.test.clientFactory", clientFactory);
         }
         
-        //create a no-op cleanup hook, so the DuoOIDCAuthenticationContext is not removed for testing
-        builderContext.registerBean("shibboleth.authn.DuoOIDC.CleanUpHook", new Consumer<ProfileRequestContext>() {
-
-            @Override
-            public void accept(final ProfileRequestContext t) {
-               return;
-                
-            }});
+        if (isRemoveDefaultContextCleanupHook()) {
+            //create a no-op cleanup hook, so the DuoOIDCAuthenticationContext is not removed for testing
+            builderContext.registerBean("shibboleth.authn.DuoOIDC.CleanUpHook", new Consumer<ProfileRequestContext>() {
+    
+                @Override
+                public void accept(final ProfileRequestContext t) {
+                   return;
+                    
+                }});
+        }
                        
     }
     
@@ -418,6 +428,24 @@ public abstract class AbstractAuthnXmlFlowExecutionTests extends CustomAbstractX
         prc.setBrowserProfile(true);
         return prc;
     }
+
+    /**
+     * Is the default duo context cleanup hook to be removed?
+     * 
+     * @return Returns the removeDefaultContextCleanupHook.
+     */
+    protected boolean isRemoveDefaultContextCleanupHook() {
+        return removeDefaultContextCleanupHook;
+    }
+
+    /**
+     * Set the remove default context cleanup hook.
+     * 
+     * @param removeDefaultContextCleanupHook The removeDefaultContextCleanupHook to set.
+     */
+    protected void setRemoveDefaultContextCleanupHook(final boolean removeDefaultContextCleanupHook) {
+        this.removeDefaultContextCleanupHook = removeDefaultContextCleanupHook;
+    }
   
 
 }
diff --git a/idp-duo-impl/src/test/java/net/shibboleth/idp/plugin/authn/duo/impl/DuoAuthnFlowTest.java b/idp-duo-impl/src/test/java/net/shibboleth/idp/plugin/authn/duo/impl/DuoAuthnFlowTest.java
index 994ae22..a1230e6 100644
--- a/idp-duo-impl/src/test/java/net/shibboleth/idp/plugin/authn/duo/impl/DuoAuthnFlowTest.java
+++ b/idp-duo-impl/src/test/java/net/shibboleth/idp/plugin/authn/duo/impl/DuoAuthnFlowTest.java
@@ -21,6 +21,7 @@ package net.shibboleth.idp.plugin.authn.duo.impl;
 import java.security.Principal;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 import javax.annotation.Nonnull;
 
@@ -115,7 +116,7 @@ public class DuoAuthnFlowTest extends AbstractAuthnXmlFlowExecutionTests {
                 "idp.duo.oidc.apiHost","api-c9f24c5a.duosecurity.com",
                 "idp.duo.oidc.clientId","DIU6GEFWG5LIUBVV2M3P",
                 "idp.duo.oidc.secretKey","rFvDfPul27v3Wew2zb6xRPzAJewJ34MP2w8UitPh",
-                "idp.duo.oidc.user.config.home","/conf/authn/duo-oidc-authn-config.xml",
+                "idp.duo.oidc.user.config","duo-oidc-authn-config.xml",
                 "idp.duo.oidc.clientFactoryBean","shibboleth.authn.DuoOIDC.test.clientFactory");
         
         setMockProperties(mockProperties);
@@ -147,7 +148,7 @@ public class DuoAuthnFlowTest extends AbstractAuthnXmlFlowExecutionTests {
                 "idp.duo.oidc.apiHost","api-c9f24c5a.duosecurity.com",
                 "idp.duo.oidc.clientId","DIU6GEFWG5LIUBVV2M3P",
                 "idp.duo.oidc.secretKey","rFvDfPul27v3Wew2zb6xRPzAJewJ34MP2w8UitPh",
-                "idp.duo.oidc.user.config.home","/conf/authn/duo-oidc-authn-config.xml",
+                "idp.duo.oidc.user.config","duo-oidc-authn-config.xml",
                 "idp.duo.oidc.clientFactoryBean","shibboleth.authn.DuoOIDC.test.clientFactory");
         
         setMockProperties(mockProperties);
@@ -283,6 +284,82 @@ public class DuoAuthnFlowTest extends AbstractAuthnXmlFlowExecutionTests {
 
     }
     
+    /** 
+     * Test for https://issues.shibboleth.net/jira/browse/JDUO-47. A mapping strategy should
+     * correctly add the ACR to the set of principals.
+     * @throws DuoClientException 
+     */
+    @Test
+    public void testContextToPrincipalMappingStrategy() throws DuoClientException {
+        setFlowPath(FLOW);
+        setFlowModelResources(flowResources);
+        setSubflows(subflows);
+        setClientFactory(new MockDuoOIDCClientFactory_OK_Client());
+        
+        final Map<String,String> mockProperties = Map.of(
+                "idp.duo.oidc.redirectURL","http://localhost/authorization-callback",
+                "idp.duo.oidc.apiHost","api-c9f24c5a.duosecurity.com",
+                "idp.duo.oidc.clientId","DIU6GEFWG5LIUBVV2M3P",
+                "idp.duo.oidc.secretKey","rFvDfPul27v3Wew2zb6xRPzAJewJ34MP2w8UitPh",
+                "idp.duo.oidc.clientFactoryBean","shibboleth.authn.DuoOIDC.test.clientFactory",
+                "idp.duo.oidc.user.config","duo-oidc-authn-config-principal-mapping.xml");
+        
+        setMockProperties(mockProperties);
+        
+        
+        
+        final LocalAttributeMap<Object> inputMap = new LocalAttributeMap<Object>();
+        inputMap.put("calledAsSubflow", true);
+        
+        final FlowExecutionImpl flowExecution = (FlowExecutionImpl)getFlowExecutionFactory()
+                .createFlowExecution(getFlowDefinition());
+        final ProfileRequestContext prc =  buildProfileRequestContext(false,false);
+        //add a DuoContext
+        final DuoOIDCAuthenticationContext duoContext = new DuoOIDCAuthenticationContext();
+        final String nonce = DuoSupport.generateNonce(32);
+        duoContext.setAuthorizationCode("adummycode");
+        duoContext.setRequestState(nonce);
+        duoContext.setResponseState(nonce);
+        duoContext.setUsername("jdoe");
+        
+        final DefaultDuoOIDCIntegration integ = new DefaultDuoOIDCIntegration();
+        integ.setClientId("DIU6GEFWG5LIUBVV2M3P");
+        integ.setAPIHost("api-c9f24c5a.duosecurity.com");
+        integ.setSecretKey("rFvDfPul27v3Wew2zb6xRPzAJewJ34MP2w8UitPh");
+        integ.setAuthorizeEndpoint("/authorize");
+        integ.setHealthCheckEndpoint("/health");
+        integ.setTokenEndpoint("/token");
+        integ.setRegisteredRedirectURI("http://localhost/authorization-callback");
+        
+        duoContext.setIntegration(integ);
+        
+      //add the mock client as was not added by the populate stage
+        duoContext.setClient(new MockDuoOIDCClient_OK(duoContext.getIntegration()));
+        
+        prc.getSubcontext(AuthenticationContext.class).addSubcontext(duoContext);
+        prc.getSubcontext(AuthenticationContext.class)
+                    .addSubcontext(new ExternalAuthenticationContext(new ExternalAuthenticationImpl(false)));
+        flowExecution.getConversationScope().put("opensamlProfileRequestContext",prc);
+        updateFlowExecution(flowExecution);
+        
+        //set start view and ending event to transition on.
+        externalContext.setEventId("proceed");
+        setCurrentState("Duo2FAAuthorizationRequest");       
+        resumeFlow(externalContext);
+        
+        //assert success conditions
+        assertFlowExecutionEnded();
+        assertNotNull(prc.getSubcontext(AuthenticationContext.class));
+        assertNotNull(prc.getSubcontext(SubjectCanonicalizationContext.class));
+        assertEquals(prc.getSubcontext(SubjectCanonicalizationContext.class).getPrincipalName(),"jdoe");
+        
+        //now test the ACR was added
+        final Set<AuthnContextClassRefPrincipal> principals = prc.getSubcontext(SubjectCanonicalizationContext.class)
+                .getSubject().getPrincipals(AuthnContextClassRefPrincipal.class);
+        assertEquals(1,principals.size());
+        assertEquals("http://example.org/ac/classes/mfa/strong",principals.iterator().next().getName());
+    }
+    
       
    
     /** Test the Duo flow from the external authorization request to the end of the flow.
@@ -290,6 +367,7 @@ public class DuoAuthnFlowTest extends AbstractAuthnXmlFlowExecutionTests {
     @Test 
     public void testDuoAuthnFlowFromAuthorizationCallback() throws DuoClientException {
         
+        setRemoveDefaultContextCleanupHook(true);
         setFlowPath(FLOW);
         setFlowModelResources(flowResources);
         setSubflows(subflows);
@@ -297,7 +375,7 @@ public class DuoAuthnFlowTest extends AbstractAuthnXmlFlowExecutionTests {
         
         
         final Map<String,String> mockProperties = Map.of(
-                "idp.duo.oidc.user.config.home","/conf/authn/duo-oidc-authn-config.xml",
+                "idp.duo.oidc.user.config","duo-oidc-authn-config.xml",
                 "idp.duo.oidc.clientFactoryBean","shibboleth.authn.DuoOIDC.test.clientFactory",
                 //these are not used for the test, but required to prevent init exceptions
                 "idp.duo.oidc.redirectURL","http://localhost/authorization-callback",
@@ -365,7 +443,8 @@ public class DuoAuthnFlowTest extends AbstractAuthnXmlFlowExecutionTests {
     //TODO: finish this, AFD is not checked in the dummy flow, and we are not checking auth result principals.
     @Test 
     public void testDuoAuthnFlowFromAuthorizationCallbackWithRPC() throws DuoClientException {
-        
+
+        setRemoveDefaultContextCleanupHook(true);
         setFlowPath(FLOW);
         setFlowModelResources(flowResources);
         setSubflows(subflows);
@@ -374,7 +453,7 @@ public class DuoAuthnFlowTest extends AbstractAuthnXmlFlowExecutionTests {
         
         final Map<String,String> mockProperties = Map.of(
                 "idp.duo.oidc.clientFactoryBean","shibboleth.authn.DuoOIDC.test.clientFactory",
-                "idp.duo.oidc.user.config.home","/conf/authn/duo-oidc-authn-config.xml",
+                "idp.duo.oidc.user.config","duo-oidc-authn-config.xml",
                 //these are not used for the test, but required to prevent init exceptions
                 "idp.duo.oidc.redirectURL","http://localhost/authorization-callback",
                 "idp.duo.oidc.apiHost","api-c9f24c5a.duosecurity.com",
@@ -464,7 +543,7 @@ public class DuoAuthnFlowTest extends AbstractAuthnXmlFlowExecutionTests {
         
         final Map<String,String> mockProperties = Map.of(
                 "idp.duo.oidc.clientFactoryBean","shibboleth.authn.DuoOIDC.test.clientFactory",
-                "idp.duo.oidc.user.config.home","/conf/authn/duo-oidc-authn-config.xml",
+                "idp.duo.oidc.user.config","duo-oidc-authn-config.xml",
                 //these are not used for the test, but required to prevent init exceptions
                 "idp.duo.oidc.redirectURL","http://localhost/authorization-callback",
                 "idp.duo.oidc.apiHost","api-c9f24c5a.duosecurity.com",
diff --git a/idp-duo-impl/src/test/resources/conf/authn/duo-oidc-authn-config-principal-mapping.xml b/idp-duo-impl/src/test/resources/conf/authn/duo-oidc-authn-config-principal-mapping.xml
new file mode 100644
index 0000000..e904ae2
--- /dev/null
+++ b/idp-duo-impl/src/test/resources/conf/authn/duo-oidc-authn-config-principal-mapping.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context"
+    xmlns:util="http://www.springframework.org/schema/util" xmlns:p="http://www.springframework.org/schema/p" xmlns:c="http://www.springframework.org/schema/c"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
+                           http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
+
+    default-init-method="initialize" default-destroy-method="destroy">
+
+    <!-- need this bean to define the scripted function -->
+     <bean id="shibboleth.ContextFunctions.Scripted"
+        class="net.shibboleth.idp.profile.context.navigate.ScriptedContextLookupFunction" abstract="true" />
+
+    <bean id="shibboleth.authn.DuoOIDC.ContextToPrincipalMappingStrategy" parent="shibboleth.ContextFunctions.Scripted"
+            factory-method="inlineScript"
+            c:outputType="java.util.Collection"
+            p:hideExceptions="false">
+            <constructor-arg>
+            <value>
+                <![CDATA[
+                //setup logger and types
+                logger = Java.type("org.slf4j.LoggerFactory").getLogger("net.shibboleth.idp.plugin");
+                var ArrayList = Java.type("java.util.ArrayList");
+                var ACR = Java.type("net.shibboleth.idp.saml.authn.principal.AuthnContextClassRefPrincipal");
+                
+                //create new output collection
+                var principals = new ArrayList(1);
+                
+                var ac = input.getSubcontext("net.shibboleth.idp.authn.context.AuthenticationContext");
+                
+                logger.debug("ac context: " + ac);
+                
+                if (ac != null){
+                var dc = ac.getSubcontext("net.shibboleth.idp.plugin.authn.duo.context.DuoOIDCAuthenticationContext");
+                
+                logger.debug("Collected Duo subcontext: " + dc);
+                if (dc != null){
+                if (dc.getAuthToken() != null && "duo_push".equals(dc.getAuthToken().getJWTClaimsSet().getJSONObjectClaim("auth_context").get("factor")))
+                
+                { principals.add(new ACR("http://example.org/ac/classes/mfa/strong")); }
+                
+                }
+                }
+                //return obj is the value of the last expression.
+                principals;
+                ]]>
+                </value>
+            </constructor-arg>
+      </bean>
+</beans>

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the commits mailing list