[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