[java-identity-provider] branch master updated: IDP-1239 - Non-browser support for Duo authentication
Scott Cantor
cantor.2 at osu.edu
Mon Aug 13 22:10:54 EDT 2018
This is an automated email from the git hooks/post-receive script.
scantor pushed a commit to branch master
in repository java-identity-provider.
View the commit online:
http://git.shibboleth.net/view/?p=java-identity-provider.git;a=commit;h=4845f544cc5e9a19ad7d71a207711c775035841e
The following commit(s) were added to refs/heads/master by this push:
new 4845f54 IDP-1239 - Non-browser support for Duo authentication
4845f54 is described below
commit 4845f544cc5e9a19ad7d71a207711c775035841e
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Mon Aug 13 22:10:47 2018 -0400
IDP-1239 - Non-browser support for Duo authentication
https://issues.shibboleth.net/jira/browse/IDP-1239
Add pushinfo support.
---
.../net/shibboleth/idp/authn/duo/DuoAuthAPI.java | 5 ++-
.../duo/context/DuoAuthenticationContext.java | 22 +++++++++++
idp-authn-impl/pom.xml | 5 +++
.../idp/authn/duo/impl/DuoAuthAuthenticator.java | 20 +++++++++-
.../impl/ExtractDuoAuthenticationFromHeaders.java | 43 +++++++++++++++++++++-
.../system/flows/authn/duo-authn-beans.xml | 3 +-
6 files changed, 93 insertions(+), 5 deletions(-)
diff --git a/idp-authn-api/src/main/java/net/shibboleth/idp/authn/duo/DuoAuthAPI.java b/idp-authn-api/src/main/java/net/shibboleth/idp/authn/duo/DuoAuthAPI.java
index eb22e80..9eef29c 100644
--- a/idp-authn-api/src/main/java/net/shibboleth/idp/authn/duo/DuoAuthAPI.java
+++ b/idp-authn-api/src/main/java/net/shibboleth/idp/authn/duo/DuoAuthAPI.java
@@ -42,7 +42,10 @@ public final class DuoAuthAPI {
/** Duo AuthAPI parameter name. */
@Nonnull @NotEmpty public static final String DUO_PASSCODE = "passcode";
-
+
+ /** Duo AuthAPI parameter name. */
+ @Nonnull @NotEmpty public static final String DUO_PUSHINFO = "pushinfo";
+
/** Duo AuthAPI factor "auto" value. */
@Nonnull @NotEmpty public static final String DUO_FACTOR_AUTO = "auto";
diff --git a/idp-authn-api/src/main/java/net/shibboleth/idp/authn/duo/context/DuoAuthenticationContext.java b/idp-authn-api/src/main/java/net/shibboleth/idp/authn/duo/context/DuoAuthenticationContext.java
index 696a82b..02a0a37 100644
--- a/idp-authn-api/src/main/java/net/shibboleth/idp/authn/duo/context/DuoAuthenticationContext.java
+++ b/idp-authn-api/src/main/java/net/shibboleth/idp/authn/duo/context/DuoAuthenticationContext.java
@@ -17,12 +17,17 @@
package net.shibboleth.idp.authn.duo.context;
+import java.util.HashMap;
+import java.util.Map;
+
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.opensaml.messaging.context.BaseContext;
import net.shibboleth.idp.authn.context.AuthenticationContext;
+import net.shibboleth.utilities.java.support.annotation.constraint.Live;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
/**
* Context that carries Duo factor and device or passcode to be used in validation.
@@ -49,7 +54,15 @@ public class DuoAuthenticationContext extends BaseContext {
/** Passcode. */
@Nullable private String duoPasscode;
+
+ /** PushInfo data. */
+ @Nullable private Map<String,String> pushInfo;
+ /** Constructor. */
+ public DuoAuthenticationContext() {
+ pushInfo = new HashMap<>();
+ }
+
/**
* Get the username.
*
@@ -155,4 +168,13 @@ public class DuoAuthenticationContext extends BaseContext {
return this;
}
+ /**
+ * Get the pushinfo.
+ *
+ * @return the pushinfo
+ */
+ @Nonnull @NonnullElements @Live public Map<String,String> getPushInfo() {
+ return pushInfo;
+ }
+
}
\ No newline at end of file
diff --git a/idp-authn-impl/pom.xml b/idp-authn-impl/pom.xml
index c684074..01ed3f9 100644
--- a/idp-authn-impl/pom.xml
+++ b/idp-authn-impl/pom.xml
@@ -43,6 +43,11 @@
<artifactId>idp-profile-api</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>idp-ui</artifactId>
+ <version>${project.version}</version>
+ </dependency>
<dependency>
<groupId>${opensaml.groupId}</groupId>
diff --git a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/DuoAuthAuthenticator.java b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/DuoAuthAuthenticator.java
index 04523ca..573b8d9 100644
--- a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/DuoAuthAuthenticator.java
+++ b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/DuoAuthAuthenticator.java
@@ -22,6 +22,8 @@ import java.net.URI;
import java.net.URISyntaxException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Map;
import javax.annotation.Nonnull;
@@ -31,24 +33,31 @@ import org.apache.http.client.utils.URIBuilder;
import com.duosecurity.duoweb.DuoWebException;
import com.fasterxml.jackson.core.type.TypeReference;
+import com.google.common.escape.Escaper;
+import com.google.common.net.UrlEscapers;
import net.shibboleth.idp.authn.duo.DuoAuthAPI;
import net.shibboleth.idp.authn.duo.DuoIntegration;
import net.shibboleth.idp.authn.duo.context.DuoAuthenticationContext;
+import net.shibboleth.utilities.java.support.primitive.StringSupport;
/**
* Implementation of the the Duo AuthApi /v2/auth endpoint.
*/
public class DuoAuthAuthenticator extends AbstractDuoAuthenticator {
+
+ /** pushinfo escaper. */
+ @Nonnull private final Escaper paramEscaper;
/** a TypeReference for the repsonse generated by the endpoint. */
@Nonnull private final TypeReference<DuoResponseWrapper<DuoAuthResponse>> wrapperTypeRef;
-
+
/** Constructor. */
public DuoAuthAuthenticator() {
wrapperTypeRef = new TypeReference<DuoResponseWrapper<DuoAuthResponse>>() {};
+ paramEscaper = UrlEscapers.urlFormParameterEscaper();
}
-
+
/**
* Perform an authentication action via the Duo AuthApi /auth endpoint.
*
@@ -80,6 +89,13 @@ public class DuoAuthAuthenticator extends AbstractDuoAuthenticator {
if (duoContext.getPasscode() != null) {
rb.addParameter(DuoAuthAPI.DUO_PASSCODE, duoContext.getPasscode());
}
+ if (!duoContext.getPushInfo().isEmpty()) {
+ final ArrayList<String> pushinfo = new ArrayList<String>(duoContext.getPushInfo().size());
+ for (final Map.Entry<String,String> entry : duoContext.getPushInfo().entrySet()) {
+ pushinfo.add(paramEscaper.escape(entry.getKey()) + "=" + paramEscaper.escape(entry.getValue()));
+ }
+ rb.addParameter(DuoAuthAPI.DUO_PUSHINFO, StringSupport.listToStringValue(pushinfo, "&"));
+ }
DuoSupport.signRequest(rb, duoIntegration);
final HttpUriRequest request = rb.build();
diff --git a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/ExtractDuoAuthenticationFromHeaders.java b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/ExtractDuoAuthenticationFromHeaders.java
index fc9fea3..d475471 100644
--- a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/ExtractDuoAuthenticationFromHeaders.java
+++ b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/ExtractDuoAuthenticationFromHeaders.java
@@ -17,7 +17,10 @@
package net.shibboleth.idp.authn.duo.impl;
+import java.util.Map;
+
import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
import javax.servlet.http.HttpServletRequest;
import org.opensaml.profile.action.ActionSupport;
@@ -25,11 +28,14 @@ import org.opensaml.profile.context.ProfileRequestContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.common.base.Function;
+
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.DuoAuthAPI;
import net.shibboleth.idp.authn.duo.context.DuoAuthenticationContext;
+import net.shibboleth.idp.ui.context.RelyingPartyUIContext;
import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
import net.shibboleth.utilities.java.support.component.ComponentSupport;
import net.shibboleth.utilities.java.support.logic.Constraint;
@@ -72,6 +78,9 @@ public class ExtractDuoAuthenticationFromHeaders<InboundMessageType,OutboundMess
/** Header name for passcode. */
@Nonnull @NotEmpty private String passcodeHeaderName;
+
+ /** Strategy function for populating pushinfo AuthAPI parameter. */
+ @Nullable private Function<ProfileRequestContext,Map<String,String>> pushInfoLookupStrategy;
/** Constructor. */
ExtractDuoAuthenticationFromHeaders() {
@@ -154,9 +163,23 @@ public class ExtractDuoAuthenticationFromHeaders<InboundMessageType,OutboundMess
* @param flag flag to set
*/
public void setAutoAuthenticationSupported(final boolean flag) {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
autoAuthenticationSupported = flag;
}
+ /**
+ * Set lookup strategy for AuthAPI pushinfo parameter.
+ *
+ * @param strategy lookup strategy
+ */
+ public void setPushInfoLookupStrategy(
+ @Nullable final Function<ProfileRequestContext,Map<String,String>> strategy) {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
+ pushInfoLookupStrategy = strategy;
+ }
+
/** {@inheritDoc} */
@Override
protected boolean doPreExecute(
@@ -169,6 +192,7 @@ public class ExtractDuoAuthenticationFromHeaders<InboundMessageType,OutboundMess
return true;
}
+// Checkstyle: CyclomaticComplexity OFF
/** {@inheritDoc} */
@Override protected void doExecute(
@Nonnull final ProfileRequestContext<InboundMessageType,OutboundMessageType> profileRequestContext,
@@ -207,13 +231,30 @@ public class ExtractDuoAuthenticationFromHeaders<InboundMessageType,OutboundMess
duoCtx.setDeviceID(DuoAuthAPI.DUO_DEVICE_AUTO);
}
+ // Populate pushinfo either customized or just with service name.
+ if (pushInfoLookupStrategy != null) {
+ final Map<String,String> pushinfo = pushInfoLookupStrategy.apply(profileRequestContext);
+ if (pushinfo != null) {
+ duoCtx.getPushInfo().putAll(pushinfo);
+ }
+ } else {
+ final RelyingPartyUIContext uiCtx = authenticationContext.getSubcontext(RelyingPartyUIContext.class);
+ if (uiCtx != null) {
+ final String name = uiCtx.getServiceName();
+ if (name != null) {
+ duoCtx.getPushInfo().put("service", uiCtx.getServiceName());
+ }
+ }
+ }
+
authenticationContext.addSubcontext(duoCtx, true);
log.debug("{} Duo AuthAPI parameters extracted from request (Factor: {}, Device: {}, Passcode: {})",
getLogPrefix(), duoCtx.getFactor(), duoCtx.getDeviceID(),
duoCtx.getPasscode() != null ? "set" : "not set");
}
-
+ // Checkstyle: CyclomaticComplexity ON
+
/**
* Extracts the Duo API arguments passed in via the request headers.
*
diff --git a/idp-conf/src/main/resources/system/flows/authn/duo-authn-beans.xml b/idp-conf/src/main/resources/system/flows/authn/duo-authn-beans.xml
index ef64f53..bf65567 100644
--- a/idp-conf/src/main/resources/system/flows/authn/duo-authn-beans.xml
+++ b/idp-conf/src/main/resources/system/flows/authn/duo-authn-beans.xml
@@ -57,7 +57,8 @@
p:clientAdddressTrusted="%{idp.duo.nonbrowser.clientAddressTrusted:true}"
p:factorHeader="%{idp.duo.nonbrowser.header.factor:X-Shibboleth-Duo-Factor}"
p:deviceHeader="%{idp.duo.nonbrowser.header.device:X-Shibboleth-Duo-Device}"
- p:passcodeHeader="%{idp.duo.nonbrowser.header.passcode:X-Shibboleth-Duo-Passcode}" />
+ p:passcodeHeader="%{idp.duo.nonbrowser.header.passcode:X-Shibboleth-Duo-Passcode}"
+ p:pushInfoLookupStrategy="#{getObject('shibboleth.authn.Duo.PushInfoLookupStrategy')}" />
<bean id="DuoPreauthAuthenticator" lazy-init="true"
class="net.shibboleth.idp.authn.duo.impl.DuoPreauthAuthenticator"
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list