[java-idp-plugin-duo] 03/04: JDUO-8 Registering the Duo MVC Controller
Phil Smart
philip.smart at jisc.ac.uk
Fri Jul 31 16:36:52 UTC 2020
This is an automated email from the git hooks/post-receive script.
philsmart pushed a commit to branch master
in repository java-idp-plugin-duo.
View the commit online:
http://git.shibboleth.net/view/?p=java-idp-plugin-duo.git;a=commit;h=873f2b00ae5f1a4f3a28aeb17f83387ae2cdc5fe
commit 873f2b00ae5f1a4f3a28aeb17f83387ae2cdc5fe
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Fri Jul 31 17:10:39 2020 +0100
JDUO-8 Registering the Duo MVC Controller
- Internal changes to add the Duo client to the Duo context. This way
The only thing the controller needs access to is the context. Hence,
those beans can be removed from the postconfig.xml file and added
to the webflow beans file.
https://issues.shibboleth.net/jira/browse/JDUO-8
---
.../duo/AbstractDuoClientAuthenticationAction.java | 182 ---------------------
.../duo/context/DuoOIDCAuthenticationContext.java | 26 ++-
.../AbstractDuoClientAuthenticationActionTest.java | 164 -------------------
idp-duo-impl/pom.xml | 7 +
.../duo/impl/DefaultDuoOIDCClientRegistry.java | 4 +-
.../authn/duo/impl/DuoOIDCAuthnController.java | 31 +---
.../authn/duo/impl/ExchangeCodeForDuoToken.java | 15 +-
.../authn/duo/impl/HealthCheckDuoOIDCAuthAPI.java | 10 +-
.../duo/impl/PopulateDuoAuthenticationContext.java | 41 ++++-
.../META-INF/net.shibboleth.idp/postconfig.xml | 150 +----------------
.../flows/authn/DuoOIDC/duo-oidc-authn-beans.xml | 42 +++--
.../impl/AbstractAuthnXmlFlowExecutionTests.java | 11 +-
.../plugin/authn/duo/impl/DuoAuthnFlowTest.java | 48 +++++-
.../authn/duo/impl/DuoOIDCAuthnControllerTest.java | 28 ++--
.../duo/impl/ExchangeCodeForDuoTokenTest.java | 12 +-
.../duo/impl/HealthCheckDuoOIDCAuthAPITest.java | 18 +-
.../impl/PopulateDuoAuthenticationContextTest.java | 41 ++++-
.../authn/duo-oidc-authn-config-custom-factory.xml | 18 ++
.../authn/duo/sdk/impl/DuoSDKClientFactory.java | 1 -
19 files changed, 259 insertions(+), 590 deletions(-)
diff --git a/idp-duo-api/src/main/java/net/shbboleth/idp/plugin/authn/duo/AbstractDuoClientAuthenticationAction.java b/idp-duo-api/src/main/java/net/shbboleth/idp/plugin/authn/duo/AbstractDuoClientAuthenticationAction.java
deleted file mode 100644
index a4812c1..0000000
--- a/idp-duo-api/src/main/java/net/shbboleth/idp/plugin/authn/duo/AbstractDuoClientAuthenticationAction.java
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements. See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID licenses this file to You under the Apache
- * License, Version 2.0 (the "License"); you may not use this file except in
- * compliance with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package net.shbboleth.idp.plugin.authn.duo;
-
-import java.util.function.Function;
-
-import javax.annotation.Nonnull;
-
-import org.opensaml.messaging.context.navigate.ChildContextLookup;
-import org.opensaml.profile.action.ActionSupport;
-import org.opensaml.profile.context.ProfileRequestContext;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import net.shbboleth.idp.plugin.authn.duo.context.DuoOIDCAuthenticationContext;
-import net.shibboleth.idp.authn.AuthnEventIds;
-import net.shibboleth.idp.authn.context.AuthenticationContext;
-import net.shibboleth.idp.authn.duo.DuoIntegration;
-import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
-import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
-import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
-import net.shibboleth.utilities.java.support.component.ComponentSupport;
-import net.shibboleth.utilities.java.support.logic.Constraint;
-
-/**
- * <p>A base class for Duo 2FA authentication related actions.</p>
- *
- * <p>In addition to the work performed by {@link AbstractDuoAuthenticationAction}, this action also looks up
- * and makes available the {@link DuoOIDCClient} created or registered against the in-flight {@link DuoIntegration}
- * attached to the {@link DuoOIDCAuthenticationContext}.</p>
- *
- * <p>Duo 2FA authentication action implementations should override
- * {@link #doExecute(ProfileRequestContext, AuthenticationContext, DuoOIDCClient, DuoOIDCAuthenticationContext)}
- * method.</p>
- *
- * @event {@link AuthnEventIds#INVALID_AUTHN_CTX}
- */
-public abstract class AbstractDuoClientAuthenticationAction extends AbstractDuoAuthenticationAction {
-
- /** Class logger. */
- @Nonnull @NotEmpty private final Logger log = LoggerFactory.getLogger(AbstractDuoClientAuthenticationAction.class);
-
- /** Lookup strategy to locate the Duo authentication context. */
- @Nonnull private Function<ProfileRequestContext,DuoOIDCAuthenticationContext> duoContextLookupStrategy;
-
- /** The registry for locating the DuoClient for the established integration.*/
- @NonnullAfterInit private DuoOIDCClientRegistry clientRegistry;
-
- /** The Duo OIDC client .*/
- @Nonnull private DuoOIDCClient duoClient;
-
- /** Constructor.*/
- public AbstractDuoClientAuthenticationAction() {
- duoContextLookupStrategy = new ChildContextLookup<>(DuoOIDCAuthenticationContext.class).
- compose(new ChildContextLookup<>(AuthenticationContext.class));
- }
-
- /**
- * Set the Duo client registry.
- *
- * @param duoRegistry the registry
- */
- public void setClientRegistry(@Nonnull final DuoOIDCClientRegistry duoRegistry) {
- clientRegistry = Constraint.isNotNull(duoRegistry,"DuoClient registry can not be null");
- }
-
-
- /**
- * Set Duo authentication context lookup strategy to use.
- *
- * @param strategy lookup strategy
- */
- public void setDuoContextLookupStrategy(
- @Nonnull final Function<ProfileRequestContext,DuoOIDCAuthenticationContext> strategy) {
- ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
-
- duoContextLookupStrategy = Constraint.isNotNull(strategy, "DuoContextLookuplookup strategy cannot be null");
- }
-
-
- /** {@inheritDoc} */
- @Override protected void doInitialize() throws ComponentInitializationException {
- super.doInitialize();
-
- if (clientRegistry == null) {
- throw new ComponentInitializationException("Duo Client Registry cannot be null");
- }
- }
-
- /** {@inheritDoc} */
- @Override
- protected final boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext,
- @Nonnull final AuthenticationContext authenticationContext,
- @Nonnull final DuoOIDCAuthenticationContext duoContext) {
-
- if (!super.doPreExecute(profileRequestContext, authenticationContext, duoContext)) {
- return false;
- }
-
- final DuoOIDCIntegration duoIntegration = duoContext.getIntegration();
- if (duoIntegration == null) {
- log.warn("{} No DuoIntegration attached to Duo authentication context", getLogPrefix());
- ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.INVALID_AUTHN_CTX);
- return false;
- }
-
- //Configure the Duo client for the established integration
- try {
- duoClient = clientRegistry.getIntegrationClientOrCreate(duoIntegration);
- } catch (final DuoRegistryException e) {
- log.warn("{} No DuoClient established (located or created) for "
- + "this integration", getLogPrefix(),e);
- ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.INVALID_AUTHN_CTX);
- return false;
- }
-
-
- return doPreExecute(profileRequestContext, authenticationContext, duoClient, duoContext);
- }
-
- /**
- * Delegates to {@link #doExecute(ProfileRequestContext, AuthenticationContext, DuoOIDCClient,
- * DuoOIDCAuthenticationContext)} to perform the actual authentication. Implementations can not
- * override this method.
- *
- * @param profileRequestContext the current IdP profile request context
- * @param authenticationContext the current authentication context
- */
- @Override
- protected final void doExecute(@Nonnull final ProfileRequestContext profileRequestContext,
- @Nonnull final AuthenticationContext authenticationContext,
- @Nonnull final DuoOIDCAuthenticationContext duoContext) {
- doExecute(profileRequestContext,authenticationContext,duoClient,duoContext);
- }
-
- /**
- * Performs this authentication action's pre-execute step. Default implementation just returns true.
- *
- * @param profileRequestContext the current IdP profile request context
- * @param authenticationContext the current authentication context
- * @param client the Duo OIDC client appropriate for the located Duo integration.
- * @param context the Duo authentication context
- *
- * @return true iff execution should continue
- */
- protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext,
- @Nonnull final AuthenticationContext authenticationContext, @Nonnull final DuoOIDCClient client,
- @Nonnull final DuoOIDCAuthenticationContext context) {
- return true;
- }
-
- /**
- * Performs this Duo authentication action using the supplied Duo client. Implementations
- * should override this method.
- *
- * @param profileRequestContext the current IdP profile request context
- * @param authenticationContext the current authentication context
- * @param client the Duo OIDC client appropriate for the located Duo integration.
- * @param context the Duo authentication context
- */
- protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext,
- @Nonnull final AuthenticationContext authenticationContext, @Nonnull final DuoOIDCClient client,
- @Nonnull final DuoOIDCAuthenticationContext context) {
-
- }
-
-}
diff --git a/idp-duo-api/src/main/java/net/shbboleth/idp/plugin/authn/duo/context/DuoOIDCAuthenticationContext.java b/idp-duo-api/src/main/java/net/shbboleth/idp/plugin/authn/duo/context/DuoOIDCAuthenticationContext.java
index f396dd9..4ad329f 100644
--- a/idp-duo-api/src/main/java/net/shbboleth/idp/plugin/authn/duo/context/DuoOIDCAuthenticationContext.java
+++ b/idp-duo-api/src/main/java/net/shbboleth/idp/plugin/authn/duo/context/DuoOIDCAuthenticationContext.java
@@ -22,9 +22,9 @@ import javax.annotation.Nullable;
import org.opensaml.messaging.context.BaseContext;
+import net.shbboleth.idp.plugin.authn.duo.DuoOIDCClient;
import net.shbboleth.idp.plugin.authn.duo.DuoOIDCIntegration;
import net.shbboleth.idp.plugin.authn.duo.model.DuoAuthToken;
-import net.shibboleth.idp.authn.context.AuthenticationContext;
/**
* <p>Context that carries the Duo integration and Duo authentication result token.</p>
@@ -51,6 +51,9 @@ public final class DuoOIDCAuthenticationContext extends BaseContext {
/** The token received from Duo as a result of 2FA.*/
@Nullable private DuoAuthToken authToken;
+
+ /** The Duo OIDC client .*/
+ @Nullable private DuoOIDCClient client;
/** Constructor. */
public DuoOIDCAuthenticationContext() {
@@ -78,6 +81,27 @@ public final class DuoOIDCAuthenticationContext extends BaseContext {
return this;
}
+ /**
+ * Get the client used to communicate with the Duo OIDC API.
+ *
+ * @return the duo client.
+ */
+ @Nullable public DuoOIDCClient getClient() {
+ return client;
+ }
+
+ /**
+ * Set the client used to communicate with the Duo OIDC API.
+ *
+ * @param duoClient the duo client.
+ *
+ * @return this context.
+ */
+ @Nonnull public DuoOIDCAuthenticationContext setClient(@Nullable final DuoOIDCClient duoClient) {
+ client = duoClient;
+ return this;
+ }
+
/**
* Set the Duo authentication token.
*
diff --git a/idp-duo-api/src/test/java/net/shbboleth/idp/plugin/authn/duo/AbstractDuoClientAuthenticationActionTest.java b/idp-duo-api/src/test/java/net/shbboleth/idp/plugin/authn/duo/AbstractDuoClientAuthenticationActionTest.java
deleted file mode 100644
index 9ca773b..0000000
--- a/idp-duo-api/src/test/java/net/shbboleth/idp/plugin/authn/duo/AbstractDuoClientAuthenticationActionTest.java
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements. See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID licenses this file to You under the Apache
- * License, Version 2.0 (the "License"); you may not use this file except in
- * compliance with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package net.shbboleth.idp.plugin.authn.duo;
-
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertFalse;
-import static org.testng.Assert.assertThrows;
-import static org.testng.Assert.assertTrue;
-
-import javax.annotation.Nonnull;
-
-import org.mockito.Mockito;
-import org.opensaml.profile.context.EventContext;
-import org.opensaml.profile.context.ProfileRequestContext;
-import org.springframework.webflow.execution.RequestContext;
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Test;
-
-import net.shbboleth.idp.plugin.authn.duo.context.DuoOIDCAuthenticationContext;
-import net.shibboleth.idp.authn.AuthnEventIds;
-import net.shibboleth.idp.authn.context.AuthenticationContext;
-import net.shibboleth.idp.profile.RequestContextBuilder;
-import net.shibboleth.idp.profile.context.navigate.WebflowRequestContextProfileRequestContextLookup;
-import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
-import net.shibboleth.utilities.java.support.logic.ConstraintViolationException;
-
-/**
- * Tests for the {@link AbstractDuoClientAuthenticationAction}.
- */
-public class AbstractDuoClientAuthenticationActionTest {
-
- /** Mock concrete implementation of an {@link AbstractDuoClientAuthenticationAction}.*/
- private MockAbstractDuoAuthenticationAction action;
-
- /** The request context.*/
- private RequestContext src;
-
- /** The profile request context.*/
- private ProfileRequestContext prc;
-
- /** The authentication context.*/
- private AuthenticationContext ac;
-
- /** The duo oidc authentication context.*/
- private DuoOIDCAuthenticationContext dc;
-
- /**
- * <p>Setup the relevant contexts per method execution.</p>
- *
- * @throws ComponentInitializationException on error.
- */
- @BeforeMethod public void setup() throws ComponentInitializationException {
- action = new MockAbstractDuoAuthenticationAction();
- final DuoOIDCClientRegistry registry = Mockito.mock(DuoOIDCClientRegistry.class);
- action.setClientRegistry(registry);
-
- src = new RequestContextBuilder().buildRequestContext();
- prc = new WebflowRequestContextProfileRequestContextLookup().apply(src);
- ac = new AuthenticationContext();
-
- prc.addSubcontext(ac);
-
- }
-
- /** Add the Duo authentication context to the authentication context.*/
- private void addDuoContext() {
- dc = new DuoOIDCAuthenticationContext();
- ac.addSubcontext(dc);
- }
-
- /** Add duo integration to the duo context.*/
- private void addDuoIntegration() {
- final DefaultDuoOIDCIntegration integ = new DefaultDuoOIDCIntegration();
- integ.setAPIHost("host.com");
- integ.setClientId("DIU6GEFWG5LIUBVV2M3P");
- integ.setRedirectURI("http://localhost/");
- integ.setSecretKey("rFvDfPul27v3Wew2zb6xRPzAJewJ34MP2w8UitPh");
- dc.setIntegration(integ);
- }
-
- /**
- * Check the {@link EventContext} in the {@link ProfileRequestContext} is a {@link String}
- * which represents the eventId argument.
- *
- * @param eventId the eventId to check.
- */
- private void assertEventId(@Nonnull final String eventId) {
- final Object event = prc.getSubcontext(EventContext.class, true).getEvent();
- assertTrue(event instanceof String);
- assertEquals(eventId, (String)event);
- }
-
- /** Test setting a null client registry.*/
- @Test
- public void testSetNullClientRegistry() {
- assertThrows(ConstraintViolationException.class,() -> action.setClientRegistry(null));
- }
-
- /** Test setting a null duo context.*/
- @Test
- public void publictestSetNullDuoContextLookupStrategy() {
- assertThrows(ConstraintViolationException.class,() ->
- action.setDuoContextLookupStrategy(null));
- }
-
- /** Test doPreExecute returns true, no failures.*/
- @Test
- public void testDoPreExecuteSuccess() throws ComponentInitializationException {
- addDuoContext();
- addDuoIntegration();
- action.setDuoContextLookupStrategy(prc -> dc);
- action.initialize();
- assertTrue(action.doPreExecute(prc, ac));
-
- }
-
- /**
- * Test pre-execute without a duo context.
- *
- * @throws ComponentInitializationException on error.
- */
- @Test
- public void testDoPreExecuteWithNoDuoContext() throws ComponentInitializationException {
- action.setDuoContextLookupStrategy(prc -> dc);
- action.initialize();
- assertFalse(action.doPreExecute(prc, ac));
- assertEventId(AuthnEventIds.INVALID_AUTHN_CTX);
- }
-
- /**
- * Test pre-execute without a duo integration.
- *
- * @throws ComponentInitializationException
- */
- @Test
- public void testDoPreExecuteWithNoDuoIntegration() throws ComponentInitializationException {
- addDuoContext();
- action.setDuoContextLookupStrategy(prc -> dc);
- action.initialize();
- assertFalse(action.doPreExecute(prc, ac));
- assertEventId(AuthnEventIds.INVALID_AUTHN_CTX);
- }
-
- /** Mock concrete implementation of the AbstractDuoAuthenticationAction.*/
- private static class MockAbstractDuoAuthenticationAction extends AbstractDuoClientAuthenticationAction{
-
- }
-
-}
-
diff --git a/idp-duo-impl/pom.xml b/idp-duo-impl/pom.xml
index 000bc29..78764c7 100644
--- a/idp-duo-impl/pom.xml
+++ b/idp-duo-impl/pom.xml
@@ -148,6 +148,13 @@
<version>4.12</version>
<scope>test</scope>
</dependency>
+ <!-- Duo client implementation is a runtime dependency that can be swapped out.
+ Add as a test dependencies for SWF testing -->
+ <dependency>
+ <groupId>net.shibboleth.idp.plugin</groupId>
+ <artifactId>idp-plugin-duo-native-client-impl</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
diff --git a/idp-duo-impl/src/main/java/net/shibboleth/idp/plugin/authn/duo/impl/DefaultDuoOIDCClientRegistry.java b/idp-duo-impl/src/main/java/net/shibboleth/idp/plugin/authn/duo/impl/DefaultDuoOIDCClientRegistry.java
index 7451764..4eeda7f 100644
--- a/idp-duo-impl/src/main/java/net/shibboleth/idp/plugin/authn/duo/impl/DefaultDuoOIDCClientRegistry.java
+++ b/idp-duo-impl/src/main/java/net/shibboleth/idp/plugin/authn/duo/impl/DefaultDuoOIDCClientRegistry.java
@@ -96,9 +96,7 @@ public class DefaultDuoOIDCClientRegistry extends AbstractIdentifiableInitializa
try {
//this is an atomic call, avoiding the need to synchronise here e.g. two clients should never
//be created for the same integration.
- final DuoOIDCClient client = clientRegistry.computeIfAbsent(integration,clientRegistryMappingFunction);
- log.debug("Retrieved existing Duo client for integration '{}'",integration);
- return client;
+ return clientRegistry.computeIfAbsent(integration,clientRegistryMappingFunction);
} catch (final DuoClientInitializationException e) {
throw new DuoRegistryException("DuoClient could not be found or created in the registry",e);
}
diff --git a/idp-duo-impl/src/main/java/net/shibboleth/idp/plugin/authn/duo/impl/DuoOIDCAuthnController.java b/idp-duo-impl/src/main/java/net/shibboleth/idp/plugin/authn/duo/impl/DuoOIDCAuthnController.java
index 2ee017c..c73276e 100644
--- a/idp-duo-impl/src/main/java/net/shibboleth/idp/plugin/authn/duo/impl/DuoOIDCAuthnController.java
+++ b/idp-duo-impl/src/main/java/net/shibboleth/idp/plugin/authn/duo/impl/DuoOIDCAuthnController.java
@@ -72,16 +72,12 @@ public class DuoOIDCAuthnController extends AbstractInitializableComponent{
@Nonnull @NotEmpty public static final String KEY_SESSION_ATTRIBUTE = "key";
/** Class logger. */
- @Nonnull private final Logger log = LoggerFactory.getLogger(DuoOIDCAuthnController.class);
-
+ @Nonnull private final Logger log = LoggerFactory.getLogger(DuoOIDCAuthnController.class);
/** Lookup strategy to locate the Duo authentication context. */
@Nonnull private Function<ProfileRequestContext,DuoOIDCAuthenticationContext> duoContextLookupStrategy;
- /** The registry for locating the DuoClient for the established integration.*/
- @NonnullAfterInit private DuoOIDCClientRegistry clientRegistry;
-
/** Constructor. */
public DuoOIDCAuthnController() {
// PRC -> AC -> DuoAuthenticationContext
@@ -99,24 +95,6 @@ public class DuoOIDCAuthnController extends AbstractInitializableComponent{
duoContextLookupStrategy = Constraint.isNotNull(strategy, "DuoContextLookuplookup strategy cannot be null");
}
- /**
- * Set the Duo client registry.
- *
- * @param duoRegistry the registry
- */
- public void setClientRegistry(@Nonnull final DuoOIDCClientRegistry duoRegistry) {
- clientRegistry = Constraint.isNotNull(duoRegistry,"DuoCient registry can not be null");
- }
-
-
- /** {@inheritDoc} */
- @Override protected void doInitialize() throws ComponentInitializationException {
- super.doInitialize();
-
- if (clientRegistry == null) {
- throw new ComponentInitializationException("Duo Client Registry cannot be null");
- }
- }
@GetMapping("/authorize")
public void authorizationRequest(@Nonnull final HttpServletRequest httpRequest,
@@ -149,7 +127,10 @@ public class DuoOIDCAuthnController extends AbstractInitializableComponent{
log.trace("Starting Duo 2FA for integration client '{}' and user '{}'",integration.getClientId(),
duoContext.getUsername());
try {
- final DuoOIDCClient client = clientRegistry.getIntegrationClientOrCreate(integration);
+ final DuoOIDCClient client = duoContext.getClient();
+ if (client == null) {
+ throw new DuoClientException("Duo client is null, has the context been created correctly?");
+ }
//generate state, stash in the context for checking on return.
//TODO: could use a type of replay cache and storage service?
final String state = DuoSupport.generateState(32);
@@ -157,7 +138,7 @@ public class DuoOIDCAuthnController extends AbstractInitializableComponent{
final String authURL = client.createAuthUrl(duoContext.getUsername(), state);
httpResponse.sendRedirect(authURL);
- } catch (final DuoRegistryException | DuoClientException e) {
+ } catch (final DuoClientException e) {
httpRequest.setAttribute(ExternalAuthentication.AUTHENTICATION_EXCEPTION_KEY, e);
ExternalAuthentication.finishExternalAuthentication(key, httpRequest, httpResponse);
}
diff --git a/idp-duo-impl/src/main/java/net/shibboleth/idp/plugin/authn/duo/impl/ExchangeCodeForDuoToken.java b/idp-duo-impl/src/main/java/net/shibboleth/idp/plugin/authn/duo/impl/ExchangeCodeForDuoToken.java
index 3bd0f78..5834efc 100644
--- a/idp-duo-impl/src/main/java/net/shibboleth/idp/plugin/authn/duo/impl/ExchangeCodeForDuoToken.java
+++ b/idp-duo-impl/src/main/java/net/shibboleth/idp/plugin/authn/duo/impl/ExchangeCodeForDuoToken.java
@@ -24,7 +24,7 @@ import org.opensaml.profile.context.ProfileRequestContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import net.shbboleth.idp.plugin.authn.duo.AbstractDuoClientAuthenticationAction;
+import net.shbboleth.idp.plugin.authn.duo.AbstractDuoAuthenticationAction;
import net.shbboleth.idp.plugin.authn.duo.DuoClientException;
import net.shbboleth.idp.plugin.authn.duo.DuoOIDCClient;
import net.shbboleth.idp.plugin.authn.duo.context.DuoOIDCAuthenticationContext;
@@ -43,7 +43,7 @@ import net.shibboleth.idp.authn.context.AuthenticationContext;
* AuthenticationContext.getSubcontect(DuoOIDCAuthenticationContext.class,false)!=null</pre>
* @post Add the Duo authentication token to the context.
*/
-public class ExchangeCodeForDuoToken extends AbstractDuoClientAuthenticationAction{
+public class ExchangeCodeForDuoToken extends AbstractDuoAuthenticationAction{
/** Class logger.*/
@Nonnull private final Logger log = LoggerFactory.getLogger(ExchangeCodeForDuoToken.class);
@@ -51,8 +51,15 @@ public class ExchangeCodeForDuoToken extends AbstractDuoClientAuthenticationActi
/** {@inheritDoc} */
@Override protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext,
- @Nonnull final AuthenticationContext authenticationContext, @Nonnull final DuoOIDCClient client,
- @Nonnull final DuoOIDCAuthenticationContext duoContext) {
+ @Nonnull final AuthenticationContext authenticationContext,
+ @Nonnull final DuoOIDCAuthenticationContext duoContext) {
+
+ final DuoOIDCClient client = duoContext.getClient();
+ if (client == null) {
+ log.error("{} Duo client is null, has the context been created correctly?",getLogPrefix());
+ ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.AUTHN_EXCEPTION);
+ return;
+ }
final String code = duoContext.getAuthorizationCode();
diff --git a/idp-duo-impl/src/main/java/net/shibboleth/idp/plugin/authn/duo/impl/HealthCheckDuoOIDCAuthAPI.java b/idp-duo-impl/src/main/java/net/shibboleth/idp/plugin/authn/duo/impl/HealthCheckDuoOIDCAuthAPI.java
index 450b449..6a2164c 100644
--- a/idp-duo-impl/src/main/java/net/shibboleth/idp/plugin/authn/duo/impl/HealthCheckDuoOIDCAuthAPI.java
+++ b/idp-duo-impl/src/main/java/net/shibboleth/idp/plugin/authn/duo/impl/HealthCheckDuoOIDCAuthAPI.java
@@ -25,7 +25,7 @@ import org.opensaml.profile.context.ProfileRequestContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import net.shbboleth.idp.plugin.authn.duo.AbstractDuoClientAuthenticationAction;
+import net.shbboleth.idp.plugin.authn.duo.AbstractDuoAuthenticationAction;
import net.shbboleth.idp.plugin.authn.duo.DuoClientException;
import net.shbboleth.idp.plugin.authn.duo.DuoOIDCAuthAPI;
import net.shbboleth.idp.plugin.authn.duo.DuoOIDCClient;
@@ -49,7 +49,7 @@ import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
* AuthenticationContext.getSubcontext(DuoOIDCAuthenticationContext.class, false) != null
* </pre>
*/
-public class HealthCheckDuoOIDCAuthAPI extends AbstractDuoClientAuthenticationAction{
+public class HealthCheckDuoOIDCAuthAPI extends AbstractDuoAuthenticationAction{
/** Event signalling that the Duo 2FA endpoints are not available. */
@Nonnull @NotEmpty public static final String DUO_UNAVAILABLE = "DuoUnavailable";
@@ -59,10 +59,14 @@ public class HealthCheckDuoOIDCAuthAPI extends AbstractDuoClientAuthenticationAc
/** {@inheritDoc} */
@Override protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext,
- @Nonnull final AuthenticationContext authenticationContext, @Nonnull final DuoOIDCClient client,
+ @Nonnull final AuthenticationContext authenticationContext,
@Nonnull final DuoOIDCAuthenticationContext duoContext) {
try {
+ final DuoOIDCClient client = duoContext.getClient();
+ if (client == null) {
+ throw new DuoClientException("Duo client is null, has the context been created correctly?");
+ }
//Native duo client will throw an exception if anything other than OK is returned.
final DuoHealthCheck healthCheckResponse = client.healthCheck();
log.trace("Duo health check response '{}'",healthCheckResponse);
diff --git a/idp-duo-impl/src/main/java/net/shibboleth/idp/plugin/authn/duo/impl/PopulateDuoAuthenticationContext.java b/idp-duo-impl/src/main/java/net/shibboleth/idp/plugin/authn/duo/impl/PopulateDuoAuthenticationContext.java
index c027e1b..12ab0a1 100644
--- a/idp-duo-impl/src/main/java/net/shibboleth/idp/plugin/authn/duo/impl/PopulateDuoAuthenticationContext.java
+++ b/idp-duo-impl/src/main/java/net/shibboleth/idp/plugin/authn/duo/impl/PopulateDuoAuthenticationContext.java
@@ -27,20 +27,26 @@ import org.opensaml.profile.action.EventIds;
import org.opensaml.profile.context.ProfileRequestContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+
+import net.shbboleth.idp.plugin.authn.duo.DuoOIDCClient;
+import net.shbboleth.idp.plugin.authn.duo.DuoOIDCClientRegistry;
import net.shbboleth.idp.plugin.authn.duo.DuoOIDCIntegration;
+import net.shbboleth.idp.plugin.authn.duo.DuoRegistryException;
import net.shbboleth.idp.plugin.authn.duo.context.DuoOIDCAuthenticationContext;
import net.shibboleth.idp.authn.AbstractAuthenticationAction;
import net.shibboleth.idp.authn.AuthnEventIds;
import net.shibboleth.idp.authn.context.AuthenticationContext;
import net.shibboleth.idp.authn.duo.DuoIntegration;
import net.shibboleth.idp.session.context.navigate.CanonicalUsernameLookupStrategy;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
import net.shibboleth.utilities.java.support.component.ComponentSupport;
import net.shibboleth.utilities.java.support.logic.Constraint;
import net.shibboleth.utilities.java.support.logic.FunctionSupport;
/**
* An action to create (or lookup) and populate the {@link DuoOIDCAuthenticationContext}
- * with the username and chosen {@link DuoIntegration} appropriate for this request.
+ * with the username, chosen {@link DuoIntegration}, and {@link DuoOIDCClient} appropriate for this request.
*
* @event {@link org.opensaml.profile.action.EventIds#PROCEED_EVENT_ID}
* @event {@link org.opensaml.profile.action.EventIds#INVALID_PROFILE_CTX}
@@ -60,6 +66,9 @@ public class PopulateDuoAuthenticationContext extends AbstractAuthenticationActi
/** Lookup strategy for Duo integration. */
@Nonnull private Function<ProfileRequestContext, DuoOIDCIntegration> duoIntegrationLookupStrategy;
+
+ /** The registry for locating the DuoClient for the established integration.*/
+ @NonnullAfterInit private DuoOIDCClientRegistry clientRegistry;
/** Constructor.*/
public PopulateDuoAuthenticationContext() {
@@ -71,6 +80,15 @@ public class PopulateDuoAuthenticationContext extends AbstractAuthenticationActi
usernameLookupStrategy = new CanonicalUsernameLookupStrategy();
duoIntegrationLookupStrategy = FunctionSupport.constant(null);
}
+
+ /**
+ * Set the Duo client registry.
+ *
+ * @param duoRegistry the registry
+ */
+ public void setClientRegistry(@Nonnull final DuoOIDCClientRegistry duoRegistry) {
+ clientRegistry = Constraint.isNotNull(duoRegistry,"DuoClient registry can not be null");
+ }
/**
* Set the lookup strategy to use for the username to match against Duo identity.
@@ -107,11 +125,21 @@ public class PopulateDuoAuthenticationContext extends AbstractAuthenticationActi
duoIntegrationLookupStrategy = Constraint.isNotNull(strategy, "DuoIntegration lookup strategy cannot be null");
}
+
+ /** {@inheritDoc} */
+ @Override protected void doInitialize() throws ComponentInitializationException {
+ super.doInitialize();
+
+ if (clientRegistry == null) {
+ throw new ComponentInitializationException("Duo Client Registry cannot be null");
+ }
+ }
/** {@inheritDoc} */
@Override protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext,
@Nonnull final AuthenticationContext authenticationContext) {
+
final DuoOIDCAuthenticationContext context = duoAuthContextCreationStrategy.apply(profileRequestContext);
if (context == null) {
log.error("{} Error creating DuoAuthenticationContext", getLogPrefix());
@@ -135,6 +163,17 @@ public class PopulateDuoAuthenticationContext extends AbstractAuthenticationActi
}
context.setUsername(username);
+ //Configure the Duo client for the established integration
+ try {
+ DuoOIDCClient client = clientRegistry.getIntegrationClientOrCreate(duoIntegration);
+ context.setClient(client);
+ } catch (final DuoRegistryException e) {
+ log.warn("{} No DuoClient established (located or created) for "
+ + "this integration", getLogPrefix(),e);
+ ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.INVALID_AUTHN_CTX);
+ return;
+ }
+
log.debug("Created Duo authentication context for '{}'",username);
}
diff --git a/idp-duo-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml b/idp-duo-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
index 9803918..36dd998 100644
--- a/idp-duo-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
+++ b/idp-duo-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
@@ -13,154 +13,8 @@
<!-- Controller implementation -->
<bean id="shibboleth.DuoOIDCAuthnController"
- class="net.shibboleth.idp.plugin.authn.duo.impl.DuoOIDCAuthnController"
- p:clientRegistry-ref="shibboleth.authn.duo.OIDC.clientRegistry" />
-
-
- <!-- Duo Client factory and bean registry -->
- <bean id="shibboleth.authn.duo.OIDC.clientRegistry" scope="singleton"
- class="net.shibboleth.idp.plugin.authn.duo.impl.DefaultDuoOIDCClientRegistry"
- p:clientFactory="#{getObject('shibboleth.authn.duo.OIDC.clientFactory') ?: getObject('shibboleth.authn.duo.OIDC.internal.clientFactory')}" />
-
- <!-- Need the client factory from a different context e.g. webflow context which is a child context
- so added here for now.-->
- <bean id="shibboleth.authn.duo.OIDC.internal.clientFactory"
- class="net.shibboleth.idp.plugin.authn.duo.sdk.impl.DuoSDKClientFactory" scope="singleton">
- <!-- certficate pins -->
- <property name="caCerts">
- <list>
- <value>sha256/I/Lt/z7ekCWanjD0Cvj5EqXls2lOaThEA0H2Bg4BT/o=</value>
- <value>sha256/r/mIkG3eEpVdm+u/ko/cwxzOMo1bk4TyHIlByibiA5E=</value>
- <value>sha256/WoiWRyIOVNa9ihaBciRSC7XHjliYS9VwUGOIud4PB18=</value>
- <value>sha256/dykHF2FLJfEpZOvbOLX4PKrcD2w2sHd/iA/G3uHTOcw=</value>
- <value>sha256/JZaQTcTWma4gws703OR/KFk313RkrDcHRvUt6na6DCg=</value>
- <value>sha256/++MBgDH5WGvL9Bcn5Be30cRcL0f5O+NyoXuWtQdX1aI=</value>
- <value>sha256/f0KW/FtqTjs108NpYj42SrGvOB2PpxIVM8nWxjPqJGE=</value>
- <value>sha256/NqvDJlas/GRcYbcWE8S/IceH9cq77kg0jVhZeAPXq8k=</value>
- <value>sha256/9+ze1cZgR9KO1kZrVDxA4HQ6voHRCSVNz4RdTCx4U8U=</value>
- <value>sha256/j9ESw8g3DxR9XM06fYZeuN1UB4O6xp/GAIjjdD/zM3g=</value>
- </list>
- </property>
- </bean>
-
- <util:list id="shibboleth.AvailableAuthenticationFlows">
-
- <bean id="authn/IPAddress" parent="shibboleth.AuthenticationFlow" p:passiveAuthenticationSupported="true"
- p:lifetime="PT60S" p:inactivityTimeout="PT60S">
- <property name="supportedPrincipals">
- <list>
- <bean parent="shibboleth.SAML2AuthnContextClassRef"
- c:classRef="urn:oasis:names:tc:SAML:2.0:ac:classes:InternetProtocol" />
- </list>
- </property>
- </bean>
-
- <bean id="authn/SPNEGO" parent="shibboleth.AuthenticationFlow" p:nonBrowserSupported="false">
- <property name="supportedPrincipals">
- <list>
- <bean parent="shibboleth.SAML2AuthnContextClassRef"
- c:classRef="urn:oasis:names:tc:SAML:2.0:ac:classes:Kerberos" />
- <bean parent="shibboleth.SAML1AuthenticationMethod" c:method="urn:ietf:rfc:1510" />
- </list>
- </property>
- </bean>
-
- <bean id="authn/Function" parent="shibboleth.AuthenticationFlow" />
-
- <bean id="authn/X509" parent="shibboleth.AuthenticationFlow" p:nonBrowserSupported="false">
- <property name="supportedPrincipals">
- <list>
- <bean parent="shibboleth.SAML2AuthnContextClassRef"
- c:classRef="urn:oasis:names:tc:SAML:2.0:ac:classes:X509" />
- <bean parent="shibboleth.SAML2AuthnContextClassRef"
- c:classRef="urn:oasis:names:tc:SAML:2.0:ac:classes:TLSClient" />
- <bean parent="shibboleth.SAML1AuthenticationMethod" c:method="urn:ietf:rfc:2246" />
- </list>
- </property>
- </bean>
-
- <bean id="authn/X509Internal" parent="shibboleth.AuthenticationFlow">
- <property name="supportedPrincipals">
- <list>
- <bean parent="shibboleth.SAML2AuthnContextClassRef"
- c:classRef="urn:oasis:names:tc:SAML:2.0:ac:classes:X509" />
- <bean parent="shibboleth.SAML2AuthnContextClassRef"
- c:classRef="urn:oasis:names:tc:SAML:2.0:ac:classes:TLSClient" />
- <bean parent="shibboleth.SAML1AuthenticationMethod" c:method="urn:ietf:rfc:2246" />
- </list>
- </property>
- </bean>
-
- <bean id="authn/Password" parent="shibboleth.AuthenticationFlow" p:passiveAuthenticationSupported="true"
- p:forcedAuthenticationSupported="true" />
-
- <bean id="authn/Duo" parent="shibboleth.AuthenticationFlow" p:forcedAuthenticationSupported="true"
- p:nonBrowserSupported="false"> <!-- put this back, false is original -->
- <!-- The list below should be changed to reflect whatever locally- or community-defined values are appropriate
- to represent MFA. It is strongly advised that the value not be specific to Duo or any particular technology. -->
- <property name="supportedPrincipals">
- <list>
- <bean parent="shibboleth.SAML2AuthnContextClassRef"
- c:classRef="http://example.org/ac/classes/mfa" />
- <bean parent="shibboleth.SAML1AuthenticationMethod"
- c:method="http://example.org/ac/classes/mfa" />
- </list>
- </property>
- </bean>
-
- <!-- new for Duo OIDC -->
- <bean id="authn/DuoOIDC" parent="shibboleth.AuthenticationFlow" p:forcedAuthenticationSupported="true"
- p:passiveAuthenticationSupported="false" p:nonBrowserSupported="false"> <!-- no nonbrowser, passive? -->
- <!-- The list below should be changed to reflect whatever locally- or community-defined values are appropriate
- to represent MFA. It is strongly advised that the value not be specific to Duo or any particular technology. -->
- <property name="supportedPrincipals">
- <list>
- <bean parent="shibboleth.SAML2AuthnContextClassRef"
- c:classRef="http://example.org/ac/classes/mfa" />
- <bean parent="shibboleth.SAML1AuthenticationMethod"
- c:method="http://example.org/ac/classes/mfa" />
- </list>
- </property>
- </bean>
-
- <bean id="authn/MFA" parent="shibboleth.AuthenticationFlow" p:passiveAuthenticationSupported="true"
- p:forcedAuthenticationSupported="true">
- <!-- The list below almost certainly requires changes, and should generally be the union of any of the separate
- factors you combine in your particular MFA flow rules. The example corresponds to the example in mfa-authn-config.xml that
- combines IPAddress with Password. -->
- <property name="supportedPrincipals">
- <list>
- <bean parent="shibboleth.SAML2AuthnContextClassRef"
- c:classRef="urn:oasis:names:tc:SAML:2.0:ac:classes:InternetProtocol" />
- <bean parent="shibboleth.SAML2AuthnContextClassRef"
- c:classRef="urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport" />
- <bean parent="shibboleth.SAML2AuthnContextClassRef"
- c:classRef="urn:oasis:names:tc:SAML:2.0:ac:classes:Password" />
- <bean parent="shibboleth.SAML1AuthenticationMethod"
- c:method="urn:oasis:names:tc:SAML:1.0:am:password" />
- </list>
- </property>
- </bean>
-
- <!-- This is a flow for proxied SAML authentication to another IdP. -->
-
- <bean id="authn/SAML" parent="shibboleth.AuthenticationFlow" p:nonBrowserSupported="false"
- p:passiveAuthenticationSupported="true" p:forcedAuthenticationSupported="true" p:proxyScopingEnforced="true"
- p:discoveryRequired="true" />
-
- <!-- These flows are often, though not exclusively, used to proxy authentication, so may need the proxyScopingEnforced
- flag enabled by hand to honor RP/local proxy count limits, and may optionally trigger discovery via the discoveryRequired
- flag. -->
-
- <bean id="authn/External" parent="shibboleth.AuthenticationFlow" p:nonBrowserSupported="false" />
-
- <bean id="authn/RemoteUser" parent="shibboleth.AuthenticationFlow" p:nonBrowserSupported="false" />
-
- <bean id="authn/RemoteUserInternal" parent="shibboleth.AuthenticationFlow" />
-
- </util:list>
-
-
+ class="net.shibboleth.idp.plugin.authn.duo.impl.DuoOIDCAuthnController" />
+
</beans>
\ No newline at end of file
diff --git a/idp-duo-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/DuoOIDC/duo-oidc-authn-beans.xml b/idp-duo-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/DuoOIDC/duo-oidc-authn-beans.xml
index d87d6a0..bce3e66 100644
--- a/idp-duo-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/DuoOIDC/duo-oidc-authn-beans.xml
+++ b/idp-duo-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/DuoOIDC/duo-oidc-authn-beans.xml
@@ -38,21 +38,48 @@
<!-- FIXME: For current SWF flow tests the client registry and factory beans from postconfig.xml
need to be copied into here, will fix. -->
+
+ <!-- Duo Client factory and bean registry -->
+ <bean id="shibboleth.authn.duo.OIDC.clientRegistry" scope="singleton"
+ class="net.shibboleth.idp.plugin.authn.duo.impl.DefaultDuoOIDCClientRegistry"
+ p:clientFactory="#{getObject('shibboleth.authn.duo.OIDC.clientFactory') ?: getObject('shibboleth.authn.duo.OIDC.internal.clientFactory')}" />
+
+
+ <!-- FIXME: aim here is to allow this to be switched out, but as it stands you always need this dep as it is
+ explicit here! -->
+ <bean id="shibboleth.authn.duo.OIDC.internal.clientFactory"
+ class="net.shibboleth.idp.plugin.authn.duo.sdk.impl.DuoSDKClientFactory" scope="singleton">
+ <!-- certficate pins. probably need managing external to the flow/plugin or overwritable -->
+ <property name="caCerts">
+ <list>
+ <value>sha256/I/Lt/z7ekCWanjD0Cvj5EqXls2lOaThEA0H2Bg4BT/o=</value>
+ <value>sha256/r/mIkG3eEpVdm+u/ko/cwxzOMo1bk4TyHIlByibiA5E=</value>
+ <value>sha256/WoiWRyIOVNa9ihaBciRSC7XHjliYS9VwUGOIud4PB18=</value>
+ <value>sha256/dykHF2FLJfEpZOvbOLX4PKrcD2w2sHd/iA/G3uHTOcw=</value>
+ <value>sha256/JZaQTcTWma4gws703OR/KFk313RkrDcHRvUt6na6DCg=</value>
+ <value>sha256/++MBgDH5WGvL9Bcn5Be30cRcL0f5O+NyoXuWtQdX1aI=</value>
+ <value>sha256/f0KW/FtqTjs108NpYj42SrGvOB2PpxIVM8nWxjPqJGE=</value>
+ <value>sha256/NqvDJlas/GRcYbcWE8S/IceH9cq77kg0jVhZeAPXq8k=</value>
+ <value>sha256/9+ze1cZgR9KO1kZrVDxA4HQ6voHRCSVNz4RdTCx4U8U=</value>
+ <value>sha256/j9ESw8g3DxR9XM06fYZeuN1UB4O6xp/GAIjjdD/zM3g=</value>
+ </list>
+ </property>
+ </bean>
<!-- Can override one or more of the beans above. -->
- <import resource="%{idp.home}/conf/authn/duo-authn-config.xml" />
+ <import resource="${idp.duo.oidc.config.user:%{idp.home}/conf/authn/duo-authn-config.xml}" />
<!-- Prototype per conversation beans -->
<bean id="PopulateDuoAuthenticationContext" scope="prototype"
class="net.shibboleth.idp.plugin.authn.duo.impl.PopulateDuoAuthenticationContext"
p:duoIntegrationLookupStrategy-ref="shibboleth.authn.duo.OIDC.DuoIntegrationStrategy"
- p:usernameLookupStrategy-ref="shibboleth.authn.duo.OIDC.UsernameLookupStrategy" />
+ p:usernameLookupStrategy-ref="shibboleth.authn.duo.OIDC.UsernameLookupStrategy"
+ p:clientRegistry-ref="shibboleth.authn.duo.OIDC.clientRegistry" />
<bean id="HealthCheckDuoOIDCAuthAPI" scope="prototype"
- class="net.shibboleth.idp.plugin.authn.duo.impl.HealthCheckDuoOIDCAuthAPI"
- parent="shibboleth.authn.duo.OIDC.DuoClientActions" />
+ class="net.shibboleth.idp.plugin.authn.duo.impl.HealthCheckDuoOIDCAuthAPI"/>
<bean id="ValidateDuoResponseState" scope="prototype"
class="net.shibboleth.idp.plugin.authn.duo.impl.ValidateDuoResponseState" />
@@ -70,8 +97,7 @@
class="net.shibboleth.idp.plugin.authn.duo.impl.ValidateDuoTokenIssuedAt" />
<bean id="ExchangeCodeForDuoToken" scope="prototype"
- class="net.shibboleth.idp.plugin.authn.duo.impl.ExchangeCodeForDuoToken"
- parent="shibboleth.authn.duo.OIDC.DuoClientActions" />
+ class="net.shibboleth.idp.plugin.authn.duo.impl.ExchangeCodeForDuoToken"/>
<bean id="ValidateDuoTokenAuthenticationResult" scope="prototype"
class="net.shibboleth.idp.plugin.authn.duo.impl.ValidateDuoTokenAuthenticationResult"
@@ -90,9 +116,5 @@
</entry>
</util:map>
- <!-- parent to aid in the configuration of AbstractDuoClientAuthenticationAction beans -->
- <bean id="shibboleth.authn.duo.OIDC.DuoClientActions" abstract="true"
- p:clientRegistry-ref="shibboleth.authn.duo.OIDC.clientRegistry" />
-
</beans>
\ No newline at end of file
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 2d21cd5..4c93c3c 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
@@ -23,6 +23,7 @@ import java.util.List;
import java.util.Map;
import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
import org.mockito.Mockito;
import org.opensaml.core.config.ConfigurationService;
@@ -112,7 +113,7 @@ public abstract class AbstractAuthnXmlFlowExecutionTests extends AbstractXmlFlow
@Nonnull private Map<String,String> mockProperties;
/** The Duo client factory to use.*/
- @Nonnull private DuoOIDCClientFactory clientFactory;
+ @Nullable private DuoOIDCClientFactory clientFactory;
/** Constructor. */
@@ -263,10 +264,10 @@ public abstract class AbstractAuthnXmlFlowExecutionTests extends AbstractXmlFlow
genericBeanDefinition(net.shibboleth.utilities.java.support.logic.FunctionSupport.class)
.setFactoryMethod("constant").setAbstract(true).getBeanDefinition());
- //register the client registry and factory
- final DefaultDuoOIDCClientRegistry registry = new DefaultDuoOIDCClientRegistry();
- builderContext.registerBean("shibboleth.authn.duo.OIDC.clientRegistry", registry);
- registry.setClientFactory(clientFactory);
+ if (clientFactory != null) {
+ //register the client factory
+ builderContext.registerBean("shibboleth.authn.duo.OIDC.clientFactory", clientFactory);
+ }
}
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 d1bfc05..d405f62 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
@@ -35,11 +35,13 @@ import org.springframework.webflow.engine.impl.FlowExecutionImpl;
import org.springframework.webflow.execution.FlowExecution;
import net.shbboleth.idp.plugin.authn.duo.DefaultDuoOIDCIntegration;
+import net.shbboleth.idp.plugin.authn.duo.DuoClientException;
import net.shbboleth.idp.plugin.authn.duo.context.DuoOIDCAuthenticationContext;
import net.shibboleth.idp.authn.context.AuthenticationContext;
import net.shibboleth.idp.authn.context.SubjectCanonicalizationContext;
import net.shibboleth.idp.plugin.authn.mock.MockDuoOIDCClientFactory_FAIL_Client;
import net.shibboleth.idp.plugin.authn.mock.MockDuoOIDCClientFactory_OK_Client;
+import net.shibboleth.idp.plugin.authn.mock.MockDuoOIDCClient_OK;
import net.shibboleth.idp.plugin.authn.mock.MockFlowBuilder;
import net.shibboleth.idp.profile.context.RelyingPartyContext;
import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
@@ -87,7 +89,7 @@ public class DuoAuthnFlowTest extends AbstractAuthnXmlFlowExecutionTests {
*/
@Nonnull @NonnullElements @Unmodifiable private final Map<String,String> flowResources =
Map.of(
- "classpath:/net/shibboleth/idp/authn/flows/authn-abstract-flow.xml","authn.abstract",
+ "classpath:/net/shibboleth/idp/flows/authn/authn-abstract-flow.xml","authn.abstract",
"classpath:/conf/authn/authn-events-flow.xml","authn.events");
@@ -237,16 +239,51 @@ public class DuoAuthnFlowTest extends AbstractAuthnXmlFlowExecutionTests {
}
+ /** Test the Duo flow up to the external authorization request using a user space (in XML file)
+ * factory. Will fail if the default DuoSDKFactory is used - invalid_client.*/
+ @Test
+ public void testDuoAuthnFlowToAuthorizationRequestWithUserSpaceFactory() {
+
+ setFlowPath(FLOW);
+ setFlowModelResources(flowResources);
+ setSubflows(subflows);
+ //sepcifically DO NOT specify the factory here. It comes from the duo-oidc-authn-config-custom-factory.xml
+ //setClientFactory(new MockDuoOIDCClientFactory_OK_Client());
+
+ final Map<String,String> mockProperties = Map.of(
+ "idp.duo.oidc.redirectUri","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.config.user","classpath:/conf/authn/duo-oidc-authn-config-custom-factory.xml");
+
+ setMockProperties(mockProperties);
+
+ final LocalAttributeMap<Object> inputMap = new LocalAttributeMap<Object>();
+ inputMap.put("calledAsSubflow", true);
+
+ final FlowExecution flowExecution = getFlowExecutionFactory().createFlowExecution(getFlowDefinition());
+ flowExecution.getConversationScope().put("opensamlProfileRequestContext", buildProfileRequestContext(false));
+ updateFlowExecution(flowExecution);
+ flowExecution.start(inputMap, externalContext);
+ assertFlowExecutionActive();
+ assertCurrentStateEquals("Duo2FAAuthorizationRequest");
+
+ }
+
+
- /** Test the Duo flow from the external authorization request to the end of the flow.*/
+ /** Test the Duo flow from the external authorization request to the end of the flow.
+ * @throws DuoClientException if the client can not be created.*/
@Test
- public void testDuoAuthnFlowFromAuthorizationCallback() {
+ public void testDuoAuthnFlowFromAuthorizationCallback() throws DuoClientException {
setFlowPath(FLOW);
setFlowModelResources(flowResources);
setSubflows(subflows);
setClientFactory(new MockDuoOIDCClientFactory_OK_Client());
+
final Map<String,String> mockProperties = Map.of(
"idp.duo.oidc.config.user","classpath:/conf/authn/duo-oidc-authn-config.xml");
@@ -262,6 +299,7 @@ public class DuoAuthnFlowTest extends AbstractAuthnXmlFlowExecutionTests {
duoContext.setRequestState(state);
duoContext.setResponseState(state);
duoContext.setUsername("jdoe");
+
final DefaultDuoOIDCIntegration integ = new DefaultDuoOIDCIntegration();
integ.setClientId("DIU6GEFWG5LIUBVV2M3P");
integ.setAPIHost("api-c9f24c5a.duosecurity.com");
@@ -269,6 +307,10 @@ public class DuoAuthnFlowTest extends AbstractAuthnXmlFlowExecutionTests {
integ.setRedirectURI("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);
flowExecution.getConversationScope().put("opensamlProfileRequestContext",prc);
updateFlowExecution(flowExecution);
diff --git a/idp-duo-impl/src/test/java/net/shibboleth/idp/plugin/authn/duo/impl/DuoOIDCAuthnControllerTest.java b/idp-duo-impl/src/test/java/net/shibboleth/idp/plugin/authn/duo/impl/DuoOIDCAuthnControllerTest.java
index cc6069a..2c29e06 100644
--- a/idp-duo-impl/src/test/java/net/shibboleth/idp/plugin/authn/duo/impl/DuoOIDCAuthnControllerTest.java
+++ b/idp-duo-impl/src/test/java/net/shibboleth/idp/plugin/authn/duo/impl/DuoOIDCAuthnControllerTest.java
@@ -54,6 +54,7 @@ import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import net.shbboleth.idp.plugin.authn.duo.DefaultDuoOIDCIntegration;
+import net.shbboleth.idp.plugin.authn.duo.DuoClientException;
import net.shbboleth.idp.plugin.authn.duo.context.DuoOIDCAuthenticationContext;
import net.shibboleth.idp.authn.AuthenticationFlowDescriptor;
import net.shibboleth.idp.authn.ExternalAuthentication;
@@ -63,6 +64,7 @@ import net.shibboleth.idp.authn.context.ExternalAuthenticationContext;
import net.shibboleth.idp.authn.context.SubjectCanonicalizationContext;
import net.shibboleth.idp.authn.impl.ExternalAuthenticationImpl;
import net.shibboleth.idp.plugin.authn.mock.MockDuoOIDCClientFactory_OK_Client;
+import net.shibboleth.idp.plugin.authn.mock.MockDuoOIDCClient_OK;
import net.shibboleth.idp.plugin.authn.util.mock.IdPPropertyConfigurer;
import net.shibboleth.idp.session.IdPSession;
import net.shibboleth.idp.session.context.SessionContext;
@@ -75,6 +77,9 @@ import net.shibboleth.idp.ui.context.RelyingPartyUIContext;
@WebAppConfiguration
@TestPropertySource(properties = {"idp.authn.duo.OIDC.externalAuthnPath=/Authn/Duo/2FA",})
public class DuoOIDCAuthnControllerTest extends AbstractTestNGSpringContextTests{
+
+ /** Duo host the integration uses, and redirects should goto.*/
+ @Nonnull private final String API_HOST = "duo.host.com";
/** Class logger. */
@Nonnull private final Logger log = LoggerFactory.getLogger(DuoOIDCAuthnControllerTest.class);
@@ -103,13 +108,6 @@ public class DuoOIDCAuthnControllerTest extends AbstractTestNGSpringContextTests
final DuoOIDCAuthnController controller = webApplicationContext.getBean(DuoOIDCAuthnController.class);
assertNotNull(controller);
- // add a Duo client registry
- final DefaultDuoOIDCClientRegistry registry = new DefaultDuoOIDCClientRegistry();
- registry.setClientFactory(new MockDuoOIDCClientFactory_OK_Client());
- registry.setId("TestDefaultClientRegistry");
- registry.initialize();
- controller.setClientRegistry(registry);
-
session = new MockHttpSession();
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
@@ -125,8 +123,10 @@ public class DuoOIDCAuthnControllerTest extends AbstractTestNGSpringContextTests
@Test
public void testSuccessfulAuthorizeRequest() throws Exception {
- mockMvc.perform(get("/Authn/Duo/2FA/authorize").session(session).param("conversation", "e1s1")).andDo(print())
- .andExpect(status().is3xxRedirection());
+ MvcResult result = mockMvc.perform(get("/Authn/Duo/2FA/authorize").session(session).param("conversation", "e1s1")).andDo(print())
+ .andExpect(status().is3xxRedirection()).andReturn();
+ assertNotNull(result.getResponse().getHeader("Location"));
+ assertTrue(result.getResponse().getHeader("Location").contains(API_HOST));
//assert the key is in the session
assertNotNull(session.getAttribute(DuoOIDCAuthnController.KEY_SESSION_ATTRIBUTE));
@@ -227,8 +227,10 @@ public class DuoOIDCAuthnControllerTest extends AbstractTestNGSpringContextTests
/**
* Export the FlowExecutor to the servlet context with the correct set of configured contexts. Mimicking the
* IdP's configuration of the {@link ServletContextAttributeExporter}.
+ *
+ * @throws DuoClientException on error creating the duoclient.
*/
- private void exportServletContextAttributes() {
+ private void exportServletContextAttributes() throws DuoClientException {
final FlowExecutorImpl mockFlowExecutor = Mockito.mock(FlowExecutorImpl.class);
final FlowExecutionRepository mockFlowExecutionRepo = Mockito.mock(FlowExecutionRepository.class);
@@ -252,8 +254,9 @@ public class DuoOIDCAuthnControllerTest extends AbstractTestNGSpringContextTests
* {@link AuthenticationContext} and {@link ExternalAuthenticationContext}.
*
* @return a profile request context.
+ * @throws DuoClientException on error creating the duo client
*/
- @Nonnull private ProfileRequestContext buildProfileRequestContext() {
+ @Nonnull private ProfileRequestContext buildProfileRequestContext() throws DuoClientException {
final ProfileRequestContext prc = new ProfileRequestContext();
final AuthenticationContext ac = new AuthenticationContext();
@@ -264,12 +267,13 @@ public class DuoOIDCAuthnControllerTest extends AbstractTestNGSpringContextTests
ec.setFlowExecutionUrl("http://localhost/idp/profile/SSO&_eventId_proceed=1");
final DefaultDuoOIDCIntegration integ = new DefaultDuoOIDCIntegration();
- integ.setAPIHost("host.com");
+ integ.setAPIHost(API_HOST);
integ.setClientId("DIU6GEFWG5LIUBVV2M3P");
integ.setRedirectURI("http://localhost/");
integ.setSecretKey("rFvDfPul27v3Wew2zb6xRPzAJewJ34MP2w8UitPh");
dc.setUsername("jdoe");
dc.setIntegration(integ);
+ dc.setClient(new MockDuoOIDCClient_OK(integ));
final AuthenticationFlowDescriptor afd = new AuthenticationFlowDescriptor();
afd.setId("authn/DuoOIDC");
diff --git a/idp-duo-impl/src/test/java/net/shibboleth/idp/plugin/authn/duo/impl/ExchangeCodeForDuoTokenTest.java b/idp-duo-impl/src/test/java/net/shibboleth/idp/plugin/authn/duo/impl/ExchangeCodeForDuoTokenTest.java
index 160f2c1..ffded9f 100644
--- a/idp-duo-impl/src/test/java/net/shibboleth/idp/plugin/authn/duo/impl/ExchangeCodeForDuoTokenTest.java
+++ b/idp-duo-impl/src/test/java/net/shibboleth/idp/plugin/authn/duo/impl/ExchangeCodeForDuoTokenTest.java
@@ -53,11 +53,7 @@ public class ExchangeCodeForDuoTokenTest extends AbstractDuoActionTest {
addDuoIntegrationToContext();
//add an auth code
dc.setAuthorizationCode("testcode");
- final DuoOIDCClientRegistry mockClientRegistry = Mockito.mock(DuoOIDCClientRegistry.class);
-
- Mockito.when(mockClientRegistry.getIntegrationClientOrCreate(dc.getIntegration()))
- .thenReturn(new MockDuoOIDCClient_OK(dc.getIntegration()));
- action.setClientRegistry(mockClientRegistry);
+ dc.setClient(new MockDuoOIDCClient_OK(dc.getIntegration()));
action.initialize();
final Event event = action.execute(src);
// success here is a null event
@@ -73,11 +69,7 @@ public class ExchangeCodeForDuoTokenTest extends AbstractDuoActionTest {
addDuoIntegrationToContext();
// blank the auth code.
dc.setAuthorizationCode(null);
- final DuoOIDCClientRegistry mockClientRegistry = Mockito.mock(DuoOIDCClientRegistry.class);
-
- Mockito.when(mockClientRegistry.getIntegrationClientOrCreate(dc.getIntegration()))
- .thenReturn(new MockDuoOIDCClient_OK(dc.getIntegration()));
- action.setClientRegistry(mockClientRegistry);
+ dc.setClient(new MockDuoOIDCClient_OK(dc.getIntegration()));
action.initialize();
final Event event = action.execute(src);
diff --git a/idp-duo-impl/src/test/java/net/shibboleth/idp/plugin/authn/duo/impl/HealthCheckDuoOIDCAuthAPITest.java b/idp-duo-impl/src/test/java/net/shibboleth/idp/plugin/authn/duo/impl/HealthCheckDuoOIDCAuthAPITest.java
index 533b4cf..2d8c513 100644
--- a/idp-duo-impl/src/test/java/net/shibboleth/idp/plugin/authn/duo/impl/HealthCheckDuoOIDCAuthAPITest.java
+++ b/idp-duo-impl/src/test/java/net/shibboleth/idp/plugin/authn/duo/impl/HealthCheckDuoOIDCAuthAPITest.java
@@ -67,12 +67,8 @@ class HealthCheckDuoOIDCAuthAPITest extends AbstractDuoActionTest {
void testDoExecuteHealthy() throws DuoRegistryException, ComponentInitializationException, DuoClientException {
addDuoContext();
addDuoIntegrationToContext();
+ dc.setClient(new MockDuoOIDCClient_OK(dc.getIntegration()));
- final DuoOIDCClientRegistry mockClientRegistry = Mockito.mock(DuoOIDCClientRegistry.class);
-
- Mockito.when(mockClientRegistry.getIntegrationClientOrCreate(dc.getIntegration()))
- .thenReturn(new MockDuoOIDCClient_OK(dc.getIntegration()));
- healthCheckAction.setClientRegistry(mockClientRegistry);
healthCheckAction.initialize();
final Event event = healthCheckAction.execute(src);
// success here is a null event
@@ -85,12 +81,8 @@ class HealthCheckDuoOIDCAuthAPITest extends AbstractDuoActionTest {
void testDoExecuteUnHealthy() throws DuoRegistryException, ComponentInitializationException, DuoClientException {
addDuoContext();
addDuoIntegrationToContext();
+ dc.setClient(new MockDuoOIDCClient_FAIL(dc.getIntegration()));
- final DuoOIDCClientRegistry mockClientRegistry = Mockito.mock(DuoOIDCClientRegistry.class);
-
- Mockito.when(mockClientRegistry.getIntegrationClientOrCreate(dc.getIntegration()))
- .thenReturn(new MockDuoOIDCClient_FAIL(dc.getIntegration()));
- healthCheckAction.setClientRegistry(mockClientRegistry);
healthCheckAction.initialize();
final Event event = healthCheckAction.execute(src);
// success here is a null event
@@ -103,12 +95,8 @@ class HealthCheckDuoOIDCAuthAPITest extends AbstractDuoActionTest {
void testDoExecuteUnknown() throws DuoRegistryException, ComponentInitializationException, DuoClientException {
addDuoContext();
addDuoIntegrationToContext();
+ dc.setClient(new MockDuoOIDCClient_UNKNOWN(dc.getIntegration()));
- final DuoOIDCClientRegistry mockClientRegistry = Mockito.mock(DuoOIDCClientRegistry.class);
-
- Mockito.when(mockClientRegistry.getIntegrationClientOrCreate(dc.getIntegration()))
- .thenReturn(new MockDuoOIDCClient_UNKNOWN(dc.getIntegration()));
- healthCheckAction.setClientRegistry(mockClientRegistry);
healthCheckAction.initialize();
final Event event = healthCheckAction.execute(src);
// success here is a null event
diff --git a/idp-duo-impl/src/test/java/net/shibboleth/idp/plugin/authn/duo/impl/PopulateDuoAuthenticationContextTest.java b/idp-duo-impl/src/test/java/net/shibboleth/idp/plugin/authn/duo/impl/PopulateDuoAuthenticationContextTest.java
index 0a84012..5435352 100644
--- a/idp-duo-impl/src/test/java/net/shibboleth/idp/plugin/authn/duo/impl/PopulateDuoAuthenticationContextTest.java
+++ b/idp-duo-impl/src/test/java/net/shibboleth/idp/plugin/authn/duo/impl/PopulateDuoAuthenticationContextTest.java
@@ -19,12 +19,16 @@ package net.shibboleth.idp.plugin.authn.duo.impl;
import static org.testng.Assert.assertNull;
+import org.mockito.Mockito;
import org.opensaml.profile.action.EventIds;
import org.springframework.webflow.execution.Event;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import net.shbboleth.idp.plugin.authn.duo.DefaultDuoOIDCIntegration;
+import net.shbboleth.idp.plugin.authn.duo.DuoClientException;
+import net.shbboleth.idp.plugin.authn.duo.DuoOIDCClientRegistry;
+import net.shbboleth.idp.plugin.authn.duo.DuoRegistryException;
import net.shibboleth.idp.authn.AuthnEventIds;
import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
@@ -51,14 +55,21 @@ public class PopulateDuoAuthenticationContextTest extends AbstractDuoActionTest{
* Test successful execution.
*
* @throws ComponentInitializationException on error.
+ * @throws DuoClientException on error.
+ * @throws DuoRegistryException one error.
*/
@Test
- public void testExecuteSuccess() throws ComponentInitializationException {
+ public void testExecuteSuccess() throws ComponentInitializationException, DuoRegistryException, DuoClientException {
final DefaultDuoOIDCIntegration integ = createDummyDuoIntegration();
//set the duo integration strategy to lookup this
action.setDuoIntegrationLookupStrategy(prc -> integ);
//lookup a username
action.setUsernameLookupStrategy(prc -> "jdoe");
+
+ final DuoOIDCClientRegistry mockClientRegistry = Mockito.mock(DuoOIDCClientRegistry.class);
+
+ action.setClientRegistry(mockClientRegistry);
+
action.initialize();
final Event event = action.execute(src);
//success here is a null event
@@ -69,9 +80,15 @@ public class PopulateDuoAuthenticationContextTest extends AbstractDuoActionTest{
* Test unsuccessful execution if there is no duo integration specified.
*
* @throws ComponentInitializationException on error.
+ * @throws DuoClientException on error.
+ * @throws DuoRegistryException on error.
*/
@Test
- public void testExecuteNoDuoIntegration() throws ComponentInitializationException {
+ public void testExecuteNoDuoIntegration() throws ComponentInitializationException, DuoRegistryException, DuoClientException {
+
+ final DuoOIDCClientRegistry mockClientRegistry = Mockito.mock(DuoOIDCClientRegistry.class);
+
+ action.setClientRegistry(mockClientRegistry);
action.initialize();
final Event event = action.execute(src);
@@ -79,18 +96,36 @@ public class PopulateDuoAuthenticationContextTest extends AbstractDuoActionTest{
assertEventId(event, EventIds.INVALID_PROFILE_CTX);
}
+ /**
+ * Test unsuccessful execution if there is no duo client registry specified.
+ *
+ * @throws ComponentInitializationException on error.
+ * @throws DuoClientException on error.
+ * @throws DuoRegistryException on error.
+ */
+ @Test(expectedExceptions = ComponentInitializationException.class)
+ public void testExecuteNoDuoClientRegistry() throws ComponentInitializationException, DuoRegistryException, DuoClientException {
+
+ action.initialize();
+ }
+
/**
* Test unsuccessful execution if there is a null username.
*
* @throws ComponentInitializationException on error.
+ * @throws DuoClientException on error.
+ * @throws DuoRegistryException on error.
*/
@Test
- public void testExecuteNullUsername() throws ComponentInitializationException {
+ public void testExecuteNullUsername() throws ComponentInitializationException, DuoRegistryException, DuoClientException {
final DefaultDuoOIDCIntegration integ = createDummyDuoIntegration();
//set the duo integration strategy to lookup this
action.setDuoIntegrationLookupStrategy(prc -> integ);
//lookup a username
action.setUsernameLookupStrategy(prc -> null);
+ final DuoOIDCClientRegistry mockClientRegistry = Mockito.mock(DuoOIDCClientRegistry.class);
+
+ action.setClientRegistry(mockClientRegistry);
action.initialize();
final Event event = action.execute(src);
//success here is a null event
diff --git a/idp-duo-impl/src/test/resources/conf/authn/duo-oidc-authn-config-custom-factory.xml b/idp-duo-impl/src/test/resources/conf/authn/duo-oidc-authn-config-custom-factory.xml
new file mode 100644
index 0000000..5a7a661
--- /dev/null
+++ b/idp-duo-impl/src/test/resources/conf/authn/duo-oidc-authn-config-custom-factory.xml
@@ -0,0 +1,18 @@
+<?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">
+
+ <!-- Add a custom Duo client factory oin the same way a user would-->
+
+ <bean id="shibboleth.authn.duo.OIDC.clientFactory"
+ class="net.shibboleth.idp.plugin.authn.mock.MockDuoOIDCClientFactory_OK_Client" scope="singleton">
+ </bean>
+
+
+</beans>
diff --git a/idp-duo-native-client-impl/src/main/java/net/shibboleth/idp/plugin/authn/duo/sdk/impl/DuoSDKClientFactory.java b/idp-duo-native-client-impl/src/main/java/net/shibboleth/idp/plugin/authn/duo/sdk/impl/DuoSDKClientFactory.java
index 7df20b9..6c73fb9 100644
--- a/idp-duo-native-client-impl/src/main/java/net/shibboleth/idp/plugin/authn/duo/sdk/impl/DuoSDKClientFactory.java
+++ b/idp-duo-native-client-impl/src/main/java/net/shibboleth/idp/plugin/authn/duo/sdk/impl/DuoSDKClientFactory.java
@@ -9,7 +9,6 @@ import net.shbboleth.idp.plugin.authn.duo.DuoClientException;
import net.shbboleth.idp.plugin.authn.duo.DuoOIDCClient;
import net.shbboleth.idp.plugin.authn.duo.DuoOIDCClientFactory;
import net.shbboleth.idp.plugin.authn.duo.DuoOIDCIntegration;
-import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
/**
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list