[java-identity-provider] branch main updated: IDP-2068 - Remove legacy Duo flow
Scott Cantor
cantor.2 at osu.edu
Tue Jul 18 16:34:16 UTC 2023
This is an automated email from the git hooks/post-receive script.
scantor pushed a commit to branch main
in repository java-identity-provider.
View the commit online:
http://git.shibboleth.net/view/?p=java-identity-provider.git;a=commit;h=653f1212184bc67042cb0285e0f3da981fbaf44a
The following commit(s) were added to refs/heads/main by this push:
new 653f12121 IDP-2068 - Remove legacy Duo flow
653f12121 is described below
commit 653f1212184bc67042cb0285e0f3da981fbaf44a
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Tue Jul 18 12:33:50 2023 -0400
IDP-2068 - Remove legacy Duo flow
https://shibboleth.atlassian.net/browse/IDP-2068
Complete removal of legacy code and dependencies.
---
.../idp/authn/duo/BasicDuoIntegration.java | 165 ----------
.../net/shibboleth/idp/authn/duo/DuoAuthAPI.java | 104 ------
.../shibboleth/idp/authn/duo/DuoIntegration.java | 61 ----
.../net/shibboleth/idp/authn/duo/DuoPrincipal.java | 86 -----
.../duo/context/DuoAuthenticationContext.java | 179 ----------
.../idp/authn/duo/context/package-info.java | 24 --
.../net/shibboleth/idp/authn/duo/package-info.java | 24 --
idp-authn-impl/pom.xml | 5 -
.../authn/duo/impl/AbstractDuoAuthenticator.java | 161 ---------
.../idp/authn/duo/impl/DuoAuthAPIResponse.java | 62 ----
.../idp/authn/duo/impl/DuoAuthAuthenticator.java | 112 -------
.../idp/authn/duo/impl/DuoAuthResponse.java | 59 ----
.../shibboleth/idp/authn/duo/impl/DuoDevice.java | 107 ------
.../idp/authn/duo/impl/DuoFailureResponse.java | 79 -----
.../authn/duo/impl/DuoPreauthAuthenticator.java | 85 -----
.../idp/authn/duo/impl/DuoPreauthResponse.java | 61 ----
.../idp/authn/duo/impl/DuoResponseWrapper.java | 62 ----
.../shibboleth/idp/authn/duo/impl/DuoSupport.java | 218 ------------
.../impl/ExtractDuoAuthenticationFromHeaders.java | 263 ---------------
.../idp/authn/duo/impl/ValidateDuoAuthAPI.java | 366 ---------------------
.../idp/authn/duo/impl/ValidateDuoWebResponse.java | 255 --------------
.../idp/authn/duo/impl/package-info.java | 24 --
.../idp/authn/duo/impl/DuoSupportTest.java | 33 --
.../ExtractDuoAuthenticationFromHeadersTest.java | 166 ----------
.../net/shibboleth/idp/conf/authn-system.xml | 9 -
25 files changed, 2770 deletions(-)
diff --git a/idp-authn-api/src/main/java/net/shibboleth/idp/authn/duo/BasicDuoIntegration.java b/idp-authn-api/src/main/java/net/shibboleth/idp/authn/duo/BasicDuoIntegration.java
deleted file mode 100644
index 5d2fdc1f3..000000000
--- a/idp-authn-api/src/main/java/net/shibboleth/idp/authn/duo/BasicDuoIntegration.java
+++ /dev/null
@@ -1,165 +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.shibboleth.idp.authn.duo;
-
-import java.security.Principal;
-import java.util.Collection;
-import java.util.Set;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-import javax.security.auth.Subject;
-
-import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
-import net.shibboleth.shared.annotation.constraint.NotEmpty;
-import net.shibboleth.shared.annotation.constraint.NotLive;
-import net.shibboleth.shared.annotation.constraint.Unmodifiable;
-import net.shibboleth.shared.component.AbstractInitializableComponent;
-import net.shibboleth.shared.component.ComponentInitializationException;
-import net.shibboleth.shared.logic.Constraint;
-import net.shibboleth.shared.primitive.StringSupport;
-
-/**
- * Wrapper for use of Duo.
- *
- * @since 3.3.0
- */
-public class BasicDuoIntegration extends AbstractInitializableComponent implements DuoIntegration {
-
- /** API host. */
- @NonnullAfterInit @NotEmpty private String apiHost;
-
- /** Application key. */
- @Nullable @NotEmpty private String applicationKey;
-
- /** Integration key. */
- @NonnullAfterInit @NotEmpty private String integrationKey;
-
- /** Secret key. */
- @NonnullAfterInit @NotEmpty private String secretKey;
-
- /** Container for supported principals. */
- @Nonnull private final Subject supportedPrincipals;
-
- /** Constructor. */
- public BasicDuoIntegration() {
- supportedPrincipals = new Subject();
- }
-
- /** {@inheritDoc} */
- @Nonnull @NotEmpty public String getAPIHost() {
- checkComponentActive();
- assert apiHost != null;
- return apiHost;
- }
-
- /**
- * Set the API host to use.
- *
- * @param host API host
- */
- public void setAPIHost(@Nonnull @NotEmpty final String host) {
- checkSetterPreconditions();
- apiHost = Constraint.isNotNull(StringSupport.trimOrNull(host), "API host cannot be null or empty");
- }
-
- /** {@inheritDoc} */
- @Nullable @NotEmpty public String getApplicationKey() {
- return applicationKey;
- }
-
- /**
- * Set the application key to use.
- *
- * @param key application key
- */
- public void setApplicationKey(@Nullable @NotEmpty final String key) {
- checkSetterPreconditions();
- applicationKey = StringSupport.trimOrNull(key);
- }
-
- /** {@inheritDoc} */
- @Nonnull @NotEmpty public String getIntegrationKey() {
- checkComponentActive();
- assert integrationKey != null;
- return integrationKey;
- }
-
- /**
- * Set the integration key to use.
- *
- * @param key integration key
- */
- public void setIntegrationKey(@Nonnull @NotEmpty final String key) {
- checkSetterPreconditions();
- integrationKey = Constraint.isNotNull(StringSupport.trimOrNull(key), "Integration key cannot be null or empty");
- }
-
- /** {@inheritDoc} */
- @Nonnull @NotEmpty public String getSecretKey() {
- checkComponentActive();
- assert secretKey != null;
- return secretKey;
- }
-
- /**
- * Set the secret key to use.
- *
- * @param key secret key
- */
- public void setSecretKey(@Nonnull @NotEmpty final String key) {
- checkSetterPreconditions();
- secretKey = Constraint.isNotNull(StringSupport.trimOrNull(key), "Secret key cannot be null or empty");
- }
-
- /** {@inheritDoc} */
- @Nonnull @Unmodifiable @NotLive public <T extends Principal> Set<T> getSupportedPrincipals(
- @Nonnull final Class<T> c) {
- final Set<T> result = supportedPrincipals.getPrincipals(c);
- assert result != null;
- return result;
- }
-
- /**
- * Set supported non-user-specific principals that the action will include in the subjects
- * it generates, in place of any default principals from the flow.
- *
- * <p>Setting to a null or empty collection will maintain the default behavior of relying on the flow.</p>
- *
- * @param <T> a type of principal to add, if not generic
- * @param principals supported principals to include
- */
- public <T extends Principal> void setSupportedPrincipals(@Nullable final Collection<T> principals) {
- checkSetterPreconditions();
-
- supportedPrincipals.getPrincipals().clear();
-
- if (principals != null && !principals.isEmpty()) {
- supportedPrincipals.getPrincipals().addAll(Set.copyOf(principals));
- }
- }
-
- /** {@inheritDoc} */
- @Override
- protected void doInitialize() throws ComponentInitializationException {
- if (apiHost == null || integrationKey == null || secretKey == null) {
- throw new ComponentInitializationException("API host and integration keys must be set");
- }
- }
-
-}
\ No newline at end of file
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
deleted file mode 100644
index 07055ff60..000000000
--- a/idp-authn-api/src/main/java/net/shibboleth/idp/authn/duo/DuoAuthAPI.java
+++ /dev/null
@@ -1,104 +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.shibboleth.idp.authn.duo;
-
-import javax.annotation.Nonnull;
-
-import net.shibboleth.shared.annotation.constraint.NotEmpty;
-
-/**
- * Constants defined in the Duo AuthAPI.
- *
- * @since 3.4.0
- */
-public final class DuoAuthAPI {
-
- /** Duo AuthAPI parameter name. */
- @Nonnull @NotEmpty public static final String DUO_USERNAME = "username";
-
- /** Duo AuthAPI parameter name. */
- @Nonnull @NotEmpty public static final String DUO_IPADDR = "ipaddr";
-
- /** Duo AuthAPI parameter name. */
- @Nonnull @NotEmpty public static final String DUO_FACTOR = "factor";
-
- /** Duo AuthAPI parameter name. */
- @Nonnull @NotEmpty public static final String DUO_DEVICE = "device";
-
- /** 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";
-
- /** Duo AuthAPI factor "push" value. */
- @Nonnull @NotEmpty public static final String DUO_FACTOR_PUSH = "push";
-
- /** Duo AuthAPI factor "passcode" value. */
- @Nonnull @NotEmpty public static final String DUO_FACTOR_PASSCODE = "passcode";
-
- /** Duo AuthAPI factor "sms" value. */
- @Nonnull @NotEmpty public static final String DUO_FACTOR_SMS = "sms";
-
- /** Duo AuthAPI factor "enum" value. */
- @Nonnull @NotEmpty public static final String DUO_FACTOR_PHONE = "phone";
-
- /** Duo AuthAPI device "auto" value. */
- @Nonnull @NotEmpty public static final String DUO_DEVICE_AUTO = "auto";
-
- /** Duo AuthAPI preauth "allow" result value. */
- @Nonnull @NotEmpty public static final String DUO_PREAUTH_RESULT_ALLOW = "allow";
-
- /** Duo AuthAPI preauth "auth" result value. */
- @Nonnull @NotEmpty public static final String DUO_PREAUTH_RESULT_AUTH = "auth";
-
- /** Duo AuthAPI preauth "deny" result value. */
- @Nonnull @NotEmpty public static final String DUO_PREAUTH_RESULT_DENY = "deny";
-
- /** Duo AuthAPI preauth "enroll" result value. */
- @Nonnull @NotEmpty public static final String DUO_PREAUTH_RESULT_ENROLL = "enroll";
-
- /** Duo AuthAPI auth "allow" result value. */
- @Nonnull @NotEmpty public static final String DUO_AUTH_RESULT_ALLOW = "allow";
-
- /** Duo AuthAPI auth "deny" result value. */
- @Nonnull @NotEmpty public static final String DUO_AUTH_RESULT_DENY = "deny";
-
- /** Duo AuthAPI auth "bypass" result value. */
- @Nonnull @NotEmpty public static final String DUO_AUTH_STATUS_BYPASS = "bypass";
-
- /** Duo AuthAPI auth "locked_out" result value. */
- @Nonnull @NotEmpty public static final String DUO_AUTH_STATUS_LOCKED = "locked_out";
-
- /** Duo flow default header name for factor. */
- @Nonnull @NotEmpty public static final String DUO_FACTOR_HEADER_NAME = "X-Shibboleth-Duo-Factor";
-
- /** Duo flow default header name for device ID. */
- @Nonnull @NotEmpty public static final String DUO_DEVICE_HEADER_NAME = "X-Shibboleth-Duo-Device";
-
- /** Duo flow default header name for passcode. */
- @Nonnull @NotEmpty public static final String DUO_PASSCODE_HEADER_NAME = "X-Shibboleth-Duo-Passcode";
-
- /** Constructor. */
- private DuoAuthAPI() {
- }
-
-}
\ No newline at end of file
diff --git a/idp-authn-api/src/main/java/net/shibboleth/idp/authn/duo/DuoIntegration.java b/idp-authn-api/src/main/java/net/shibboleth/idp/authn/duo/DuoIntegration.java
deleted file mode 100644
index fd05dca07..000000000
--- a/idp-authn-api/src/main/java/net/shibboleth/idp/authn/duo/DuoIntegration.java
+++ /dev/null
@@ -1,61 +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.shibboleth.idp.authn.duo;
-
-import net.shibboleth.idp.authn.principal.PrincipalSupportingComponent;
-import net.shibboleth.shared.annotation.constraint.NotEmpty;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-/**
- * Interface to a particular Duo integration point.
- *
- * @since 3.3.0
- */
-public interface DuoIntegration extends PrincipalSupportingComponent {
-
- /**
- * Get the name of the API host to contact.
- *
- * @return name of API host
- */
- @Nonnull @NotEmpty String getAPIHost();
-
- /**
- * Get the application key.
- *
- * @return the application key
- */
- @Nullable @NotEmpty String getApplicationKey();
-
- /**
- * Get the integration key.
- *
- * @return the integration key
- */
- @Nonnull @NotEmpty String getIntegrationKey();
-
- /**
- * Get the secret key.
- *
- * @return the secret key
- */
- @Nonnull @NotEmpty String getSecretKey();
-
-}
\ No newline at end of file
diff --git a/idp-authn-api/src/main/java/net/shibboleth/idp/authn/duo/DuoPrincipal.java b/idp-authn-api/src/main/java/net/shibboleth/idp/authn/duo/DuoPrincipal.java
deleted file mode 100644
index a4ff6a0c8..000000000
--- a/idp-authn-api/src/main/java/net/shibboleth/idp/authn/duo/DuoPrincipal.java
+++ /dev/null
@@ -1,86 +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.shibboleth.idp.authn.duo;
-
-import javax.annotation.Nonnull;
-
-import net.shibboleth.idp.authn.principal.CloneablePrincipal;
-import net.shibboleth.shared.annotation.ParameterName;
-import net.shibboleth.shared.annotation.constraint.NotEmpty;
-import net.shibboleth.shared.logic.Constraint;
-import net.shibboleth.shared.primitive.StringSupport;
-
-import com.google.common.base.MoreObjects;
-
-/** Principal based on a Duo authentication. */
-public class DuoPrincipal implements CloneablePrincipal {
-
- /** The username. */
- @Nonnull @NotEmpty private String username;
-
- /**
- * Constructor.
- *
- * @param name the username
- */
- public DuoPrincipal(@Nonnull @NotEmpty @ParameterName(name="name") final String name) {
- username = Constraint.isNotNull(StringSupport.trimOrNull(name), "Username cannot be null or empty");
- }
-
- /** {@inheritDoc} */
- @Nonnull @NotEmpty public String getName() {
- return username;
- }
-
- /** {@inheritDoc} */
- @Override
- public int hashCode() {
- return username.hashCode();
- }
-
- /** {@inheritDoc} */
- @Override
- public boolean equals(final Object other) {
- if (other == null) {
- return false;
- }
-
- if (this == other) {
- return true;
- }
-
- if (other instanceof DuoPrincipal) {
- return username.equals(((DuoPrincipal) other).getName());
- }
-
- return false;
- }
-
- /** {@inheritDoc} */
- @Override
- public String toString() {
- return MoreObjects.toStringHelper(this).add("username", username).toString();
- }
-
- /** {@inheritDoc} */
- @Nonnull public DuoPrincipal clone() throws CloneNotSupportedException {
- final DuoPrincipal copy = (DuoPrincipal) super.clone();
- copy.username = username;
- return copy;
- }
-}
\ No newline at end of file
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
deleted file mode 100644
index a43d2cb16..000000000
--- a/idp-authn-api/src/main/java/net/shibboleth/idp/authn/duo/context/DuoAuthenticationContext.java
+++ /dev/null
@@ -1,179 +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.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.shared.annotation.constraint.Live;
-
-/**
- * Context that carries Duo factor and device or passcode to be used in validation.
- *
- * <p>This is used for AuthAPI-based use of Duo rather than the usual delegation
- * of the process to their Web SDK.</p>
- *
- * @parent {@link AuthenticationContext}
- * @added After extracting the Duo factor and device or passcode during authentication
- */
-public final class DuoAuthenticationContext extends BaseContext {
-
- /** Username. */
- @Nullable private String username;
-
- /** Client address. */
- @Nullable private String clientAddress;
-
- /** Factor. */
- @Nullable private String duoFactor;
-
- /** Device ID. */
- @Nullable private String duoDevice;
-
- /** Passcode. */
- @Nullable private String duoPasscode;
-
- /** PushInfo data. */
- @Nonnull private Map<String,String> pushInfo;
-
- /** Constructor. */
- public DuoAuthenticationContext() {
- pushInfo = new HashMap<>();
- }
-
- /**
- * Get the username.
- *
- * @return username
- */
- @Nullable public String getUsername() {
- return username;
- }
-
- /**
- * Set the username.
- *
- * @param name username
- *
- * @return this context
- */
- @Nonnull public DuoAuthenticationContext setUsername(@Nullable final String name) {
- username = name;
- return this;
- }
-
- /**
- * Get the client address.
- *
- * @return address
- */
- @Nullable public String getClientAddress() {
- return clientAddress;
- }
-
- /**
- * Set the client address.
- *
- * @param address client address
- *
- * @return this context
- */
- @Nonnull public DuoAuthenticationContext setClientAddress(@Nullable final String address) {
- clientAddress = address;
- return this;
- }
-
- /**
- * Get the device ID.
- *
- * @return the Duo device identifier
- */
- @Nullable public String getDeviceID() {
- return duoDevice;
- }
-
- /**
- * Set the device ID.
- *
- * @param deviceId the Duo device identifier
- *
- * @return this context
- */
- @Nonnull public DuoAuthenticationContext setDeviceID(@Nullable final String deviceId) {
- duoDevice = deviceId;
- return this;
- }
-
- /**
- * Get the factor to use.
- *
- * @return the factor to use
- */
- @Nullable public String getFactor() {
- return duoFactor;
- }
-
- /**
- * Set the factor to use.
- *
- * @param factor the Duo factor
- *
- * @return this context
- */
- @Nonnull public DuoAuthenticationContext setFactor(@Nullable final String factor) {
- duoFactor = factor;
- return this;
- }
-
- /**
- * Get the passcode.
- *
- * @return the passcode
- */
- @Nullable public String getPasscode() {
- return duoPasscode;
- }
-
- /**
- * Set the passcode.
- *
- * @param passcode the passcode
- *
- * @return this context
- */
- @Nonnull public DuoAuthenticationContext setPasscode(@Nullable final String passcode) {
- duoPasscode = passcode;
- return this;
- }
-
- /**
- * Get the pushinfo.
- *
- * @return the pushinfo
- */
- @Nonnull @Live public Map<String,String> getPushInfo() {
- return pushInfo;
- }
-
-}
\ No newline at end of file
diff --git a/idp-authn-api/src/main/java/net/shibboleth/idp/authn/duo/context/package-info.java b/idp-authn-api/src/main/java/net/shibboleth/idp/authn/duo/context/package-info.java
deleted file mode 100644
index 7c10173ca..000000000
--- a/idp-authn-api/src/main/java/net/shibboleth/idp/authn/duo/context/package-info.java
+++ /dev/null
@@ -1,24 +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.
- */
-
-/**
- * Context classes related to Duo authentication.
- */
- at NonnullElements
-package net.shibboleth.idp.authn.duo.context;
-
-import net.shibboleth.shared.annotation.constraint.NonnullElements;
diff --git a/idp-authn-api/src/main/java/net/shibboleth/idp/authn/duo/package-info.java b/idp-authn-api/src/main/java/net/shibboleth/idp/authn/duo/package-info.java
deleted file mode 100644
index 1e08c7224..000000000
--- a/idp-authn-api/src/main/java/net/shibboleth/idp/authn/duo/package-info.java
+++ /dev/null
@@ -1,24 +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.
- */
-
-/**
- * Public APIs related to Duo authentication.
- */
- at NonnullElements
-package net.shibboleth.idp.authn.duo;
-
-import net.shibboleth.shared.annotation.constraint.NonnullElements;
diff --git a/idp-authn-impl/pom.xml b/idp-authn-impl/pom.xml
index 4f4a9220b..a451db4ee 100644
--- a/idp-authn-impl/pom.xml
+++ b/idp-authn-impl/pom.xml
@@ -185,11 +185,6 @@
<artifactId>ldaptive</artifactId>
</dependency>
- <dependency>
- <groupId>com.duosecurity</groupId>
- <artifactId>DuoWeb</artifactId>
- </dependency>
-
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
diff --git a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/AbstractDuoAuthenticator.java b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/AbstractDuoAuthenticator.java
deleted file mode 100644
index b64af49f8..000000000
--- a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/AbstractDuoAuthenticator.java
+++ /dev/null
@@ -1,161 +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.shibboleth.idp.authn.duo.impl;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-import javax.annotation.concurrent.ThreadSafe;
-
-import org.apache.hc.client5.http.classic.HttpClient;
-import org.apache.hc.client5.http.protocol.HttpClientContext;
-import org.apache.hc.core5.http.ClassicHttpRequest;
-import org.apache.hc.core5.http.ClassicHttpResponse;
-import org.apache.hc.core5.http.HttpStatus;
-import org.opensaml.security.httpclient.HttpClientSecurityParameters;
-import org.opensaml.security.httpclient.HttpClientSecuritySupport;
-
-import com.duosecurity.duoweb.DuoWebException;
-import com.fasterxml.jackson.core.type.TypeReference;
-import com.fasterxml.jackson.databind.ObjectMapper;
-
-import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
-import net.shibboleth.shared.component.AbstractInitializableComponent;
-import net.shibboleth.shared.component.ComponentInitializationException;
-import net.shibboleth.shared.logic.Constraint;
-
-/**
- * A base class for authentication actions which call a Duo AuthAPI endpont.
- */
- at ThreadSafe
-public abstract class AbstractDuoAuthenticator extends AbstractInitializableComponent {
-
- /** HttpClient for contacting Duo. */
- @NonnullAfterInit private HttpClient httpClient;
-
- /** HTTP client security parameters. */
- @Nullable private HttpClientSecurityParameters httpClientSecurityParameters;
-
- /** JSON object mapper. */
- @NonnullAfterInit private ObjectMapper objectMapper;
-
- /**
- * Set the {@link HttpClient} to use for contacting Duo.
- *
- * @param client HttpClient
- */
- public void setHttpClient(@Nonnull final HttpClient client) {
- checkSetterPreconditions();
- httpClient = Constraint.isNotNull(client, "HTTP client cannot be null");
- }
-
- /**
- * Set the optional client security parameters.
- *
- * @param params the new client security parameters
- */
- public void setHttpClientSecurityParameters(@Nullable final HttpClientSecurityParameters params) {
- checkSetterPreconditions();
- httpClientSecurityParameters = params;
- }
-
- /**
- * Set the JSON {@link ObjectMapper}.
- *
- * @param mapper object mapper
- */
- public void setObjectMapper(@Nonnull final ObjectMapper mapper) {
- checkSetterPreconditions();
- objectMapper = Constraint.isNotNull(mapper, "Object mapper cannot be null");
- }
-
- /** {@inheritDoc} */
- @Override protected void doInitialize() throws ComponentInitializationException {
- super.doInitialize();
-
-
- if (httpClient == null) {
- throw new ComponentInitializationException("HttpClient cannot be null");
- }
-
- if (objectMapper == null) {
- throw new ComponentInitializationException("ObjectMapper cannot be null");
- }
- }
-
- /**
- * Performs a call to the Duo AuthAPI. Upon a successful call, the JSON response is mapped into the appropriate type
- * of {@link DuoResponseWrapper}.
- *
- * @param request the prepared HTTP request
- * @param wrapperTypeRef the type of {@link DuoResponseWrapper} to use
- * @param <T> the DuoResponse type being wrapped
- *
- * @return a {@link DuoResponseWrapper}
- *
- * @throws IOException on an I/O error
- * @throws DuoWebException on a Duo-related error
- */
- protected <T extends DuoResponseWrapper<?>> T doAPIRequest(@Nonnull final ClassicHttpRequest request,
- @Nonnull final TypeReference<T> wrapperTypeRef)
- throws DuoWebException, IOException {
-
- // Make the request.
- final HttpClientContext clientContext = HttpClientContext.create();
- assert clientContext != null;
- HttpClientSecuritySupport.marshalSecurityParameters(clientContext, httpClientSecurityParameters, true);
- HttpClientSecuritySupport.addDefaultTLSTrustEngineCriteria(clientContext, request);
- final ClassicHttpResponse httpResponse = httpClient.executeOpen(null, request, clientContext);
- final String scheme = request.getScheme();
- assert scheme != null;
- HttpClientSecuritySupport.checkTLSCredentialEvaluated(clientContext, scheme);
-
- // Check the HTTP response code.
- final int httpStatusCode = httpResponse.getCode();
- if (httpStatusCode == HttpStatus.SC_BAD_REQUEST) {
- final InputStream httpContent = httpResponse.getEntity().getContent();
- final DuoFailureResponse msg = objectMapper.readValue(httpContent, DuoFailureResponse.class);
- final StringBuilder builder = new StringBuilder();
- builder.append(msg.getMessage() != null ? msg.getMessage() : "no message")
- .append(" (")
- .append(msg.getMessageDetail() != null ? msg.getMessageDetail() : "no detail")
- .append(")");
- throw new DuoWebException(builder.toString());
- }
- if (httpStatusCode != HttpStatus.SC_OK) {
- throw new IOException("Non-ok status code (" + httpStatusCode + ") returned from Duo: "
- + httpResponse.getReasonPhrase());
- } else if (httpResponse.getEntity() == null) {
- throw new IOException("No response body returned from Duo");
- }
-
- // Parse the JSON response.
- final T duoResponse = objectMapper.readValue(httpResponse.getEntity().getContent(), wrapperTypeRef);
-
- if (duoResponse == null) {
- throw new DuoWebException("Unable to parse JSON response");
- } else if (!"OK".equals(duoResponse.getStat())) {
- throw new DuoWebException("Unexpected status value in JSON response: " + duoResponse.getStat());
- }
-
- return duoResponse;
- }
-
-}
diff --git a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/DuoAuthAPIResponse.java b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/DuoAuthAPIResponse.java
deleted file mode 100644
index 123cac4df..000000000
--- a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/DuoAuthAPIResponse.java
+++ /dev/null
@@ -1,62 +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.shibboleth.idp.authn.duo.impl;
-
-import javax.annotation.Nonnull;
-
-import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
-import com.fasterxml.jackson.annotation.JsonProperty;
-
-/**
- * Describes the results of a Duo AuthAPI call, intended for use with a jackson
- * {@link com.fasterxml.jackson.databind.ObjectMapper}.
- *
- * @since 3.4.0
- */
- at JsonIgnoreProperties(ignoreUnknown = true)
-public abstract class DuoAuthAPIResponse {
-
- /** the result. */
- @JsonProperty("result")
- private String result;
-
- /** the status message. */
- @JsonProperty("status_msg")
- private String statusMessage;
-
- /**
- * Get the Duo result string.
- *
- * @return the result string
- */
- @Nonnull public String getResult() {
- assert result != null;
- return result;
- }
-
- /**
- * Get the Duo status message.
- *
- * @return the Duo status message
- */
- @Nonnull public String getStatusMessage() {
- assert statusMessage != null;
- return statusMessage;
- }
-
-}
\ No newline at end of file
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
deleted file mode 100644
index 039492886..000000000
--- a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/DuoAuthAuthenticator.java
+++ /dev/null
@@ -1,112 +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.shibboleth.idp.authn.duo.impl;
-
-import java.io.IOException;
-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;
-
-import org.apache.hc.core5.http.ClassicHttpRequest;
-import org.apache.hc.core5.http.io.support.ClassicRequestBuilder;
-import org.apache.hc.core5.net.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.shared.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. */
- @SuppressWarnings("null")
- public DuoAuthAuthenticator() {
- wrapperTypeRef = new TypeReference<>() {};
- paramEscaper = UrlEscapers.urlFormParameterEscaper();
- }
-
- /**
- * Perform an authentication action via the Duo AuthApi /auth endpoint.
- *
- * @param duoContext Duo authentication context to use
- * @param duoIntegration Duo integration to use
- *
- * @return a {@link DuoAuthResponse}
- *
- * @throws DuoWebException if an error occurs
- */
- public DuoAuthResponse authenticate(@Nonnull final DuoAuthenticationContext duoContext,
- @Nonnull final DuoIntegration duoIntegration) throws DuoWebException {
-
- try {
- // prepare the request
- final URI uri = new URIBuilder().setScheme("https").setHost(duoIntegration.getAPIHost())
- .setPath("/auth/v2/auth").build();
- final ClassicRequestBuilder rb =
- ClassicRequestBuilder.post().setUri(uri).addParameter(DuoAuthAPI.DUO_USERNAME, duoContext.getUsername());
- assert rb != null;
- if (duoContext.getClientAddress() != null) {
- rb.addParameter(DuoAuthAPI.DUO_IPADDR, duoContext.getClientAddress());
- }
- if (duoContext.getFactor() != null) {
- rb.addParameter(DuoAuthAPI.DUO_FACTOR, duoContext.getFactor());
- }
- if (duoContext.getDeviceID() != null) {
- rb.addParameter(DuoAuthAPI.DUO_DEVICE, duoContext.getDeviceID());
- }
- if (duoContext.getPasscode() != null) {
- rb.addParameter(DuoAuthAPI.DUO_PASSCODE, duoContext.getPasscode());
- }
- if (!duoContext.getPushInfo().isEmpty()) {
- final ArrayList<String> pushinfo = new ArrayList<>(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 ClassicHttpRequest request = rb.build();
- assert request != null;
-
- // do it
- return doAPIRequest(request, wrapperTypeRef).getResponse();
- } catch (final IOException | URISyntaxException | InvalidKeyException | NoSuchAlgorithmException ex) {
- throw new DuoWebException("Duo AuthAPI auth request failed: " + ex.getMessage());
- }
- }
-
-}
diff --git a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/DuoAuthResponse.java b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/DuoAuthResponse.java
deleted file mode 100644
index a94062fa2..000000000
--- a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/DuoAuthResponse.java
+++ /dev/null
@@ -1,59 +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.shibboleth.idp.authn.duo.impl;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
-import com.fasterxml.jackson.annotation.JsonProperty;
-
-/**
- * Describes the results of an authentication attempt via the Duo AuthAPI, intended for use with a jackson
- * {@link com.fasterxml.jackson.databind.ObjectMapper}.
- */
- at JsonIgnoreProperties(ignoreUnknown = true)
-public class DuoAuthResponse extends DuoAuthAPIResponse {
-
- /** the status string. */
- @JsonProperty("status") private String status;
-
- /** the trusted device token string. */
- @JsonProperty("trusted_device_token") private String trustedDeviceToken;
-
- /**
- * Get the Duo status string.
- *
- * @return Duo status string
- */
- @Nonnull public String getStatus() {
- assert status != null;
- return status;
- }
-
- /**
- * Get the Duo trusted device token string.
- *
- * @return Duo trusted device token string
- */
- @Nullable public String getTrustedDeviceToken() {
- assert trustedDeviceToken != null;
- return trustedDeviceToken;
- }
-
-}
\ No newline at end of file
diff --git a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/DuoDevice.java b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/DuoDevice.java
deleted file mode 100644
index ca6181a5f..000000000
--- a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/DuoDevice.java
+++ /dev/null
@@ -1,107 +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.shibboleth.idp.authn.duo.impl;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
-import com.fasterxml.jackson.annotation.JsonProperty;
-
-/**
- * Represents a Duo device, intended for use with a jackson
- * {@link com.fasterxml.jackson.databind.ObjectMapper}.
- */
- at JsonIgnoreProperties(ignoreUnknown = true)
-public class DuoDevice {
-
- /** the Duo device identifier. */
- @JsonProperty("device") @Nullable private String device;
-
- /** the Duo device type. */
- @JsonProperty("type") @Nullable private String type;
-
- /** the Duo device number. */
- @JsonProperty("number") @Nullable private String number;
-
- /** the Duo device name. */
- @JsonProperty("name") @Nullable private String name;
-
- /** a {@link List} of Duo device capabilities. */
- @JsonProperty("capabilities") @Nullable private List<String> capabilities;
-
- /** Constructor. */
- public DuoDevice() {
- capabilities = new ArrayList<>();
- }
-
- /**
- * Get the device identifier.
- *
- * @return the device identifier
- */
- @Nonnull public String getDevice() {
- assert device != null;
- return device;
- }
-
- /**
- * Get the device type.
- *
- * @return the device type
- */
- @Nonnull public String getType() {
- assert type != null;
- return type;
- }
-
- /**
- * Get the device number.
- *
- * @return the device number
- */
- @Nonnull public String getNumber() {
- assert number != null;
- return number;
- }
-
- /**
- * Get the device name.
- *
- * @return the device name
- */
- @Nonnull public String getName() {
- assert name != null;
- return name;
- }
-
- /**
- * Get the device capabilities.
- *
- * @return the device capabilities
- */
- @Nonnull public Collection<String> getCapabilities() {
- assert capabilities != null;
- return capabilities;
- }
-
-}
\ No newline at end of file
diff --git a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/DuoFailureResponse.java b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/DuoFailureResponse.java
deleted file mode 100644
index 5dd7d3545..000000000
--- a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/DuoFailureResponse.java
+++ /dev/null
@@ -1,79 +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.shibboleth.idp.authn.duo.impl;
-
-import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
-import com.fasterxml.jackson.annotation.JsonProperty;
-
-import javax.annotation.Nullable;
-
-/**
- * Describes the failure of a Duo AuthAPI call.
- */
- at JsonIgnoreProperties(ignoreUnknown = true)
-public class DuoFailureResponse {
-
- /** the failure status. */
- @JsonProperty("stat") @Nullable private String stat;
-
- /** the failure code. */
- @JsonProperty("code") @Nullable private String code;
-
- /** the failure message. */
- @JsonProperty("message") @Nullable private String message;
-
- /** the failure message detail. */
- @JsonProperty("message_detail") @Nullable private String messageDetail;
-
- /**
- * Get the failure status.
- *
- * @return failure status
- */
- @Nullable public String getStat() {
- return stat;
- }
-
- /**
- * Get the failure code.
- *
- * @return failure code
- */
- @Nullable public String getCode() {
- return code;
- }
-
- /**
- * Get the failure message.
- *
- * @return failure message
- */
- @Nullable public String getMessage() {
- return message;
- }
-
- /**
- * Get the failure message details.
- *
- * @return failure message details
- */
- @Nullable public String getMessageDetail() {
- return messageDetail;
- }
-
-}
\ No newline at end of file
diff --git a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/DuoPreauthAuthenticator.java b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/DuoPreauthAuthenticator.java
deleted file mode 100644
index 20ca5ff3d..000000000
--- a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/DuoPreauthAuthenticator.java
+++ /dev/null
@@ -1,85 +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.shibboleth.idp.authn.duo.impl;
-
-import java.io.IOException;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
-
-import javax.annotation.Nonnull;
-
-import org.apache.hc.core5.http.ClassicHttpRequest;
-import org.apache.hc.core5.http.io.support.ClassicRequestBuilder;
-import org.apache.hc.core5.net.URIBuilder;
-
-import com.duosecurity.duoweb.DuoWebException;
-//import javax.json.JsonObject;
-import com.fasterxml.jackson.core.type.TypeReference;
-
-import net.shibboleth.idp.authn.duo.DuoAuthAPI;
-import net.shibboleth.idp.authn.duo.DuoIntegration;
-import net.shibboleth.idp.authn.duo.context.DuoAuthenticationContext;
-
-/**
- * Implementation of the the Duo AuthAPI /v2/preauth endpoint.
- */
-public class DuoPreauthAuthenticator extends AbstractDuoAuthenticator {
-
- /** TypeReference for the response generated by the endpoint. */
- @Nonnull private final TypeReference<DuoResponseWrapper<DuoPreauthResponse>> wrapperTypeRef;
-
- /** Constructor. */
- public DuoPreauthAuthenticator() {
- wrapperTypeRef = new TypeReference<>() {};
- }
-
- /**
- * Perform an authentication action via the Duo AuthAPI /preauth endpoint.
- *
- * @param duoContext Duo authentication context to use
- * @param duoIntegration Duo integration to use
- *
- * @return a {@link DuoPreauthResponse}
- *
- * @throws DuoWebException if an error occurs
- */
- public DuoPreauthResponse authenticate(@Nonnull final DuoAuthenticationContext duoContext,
- @Nonnull final DuoIntegration duoIntegration) throws DuoWebException {
- try {
- // Prepare the request
- final URI uri = new URIBuilder().setScheme("https").setHost(duoIntegration.getAPIHost())
- .setPath("/auth/v2/preauth").build();
- final ClassicRequestBuilder rb =
- ClassicRequestBuilder.post().setUri(uri).addParameter(DuoAuthAPI.DUO_USERNAME, duoContext.getUsername());
-
- if (duoContext.getClientAddress() != null) {
- rb.addParameter(DuoAuthAPI.DUO_IPADDR, duoContext.getClientAddress());
- }
-
- DuoSupport.signRequest(rb, duoIntegration);
- final ClassicHttpRequest request = rb.build();
-
- return doAPIRequest(request, wrapperTypeRef).getResponse();
- } catch (final IOException | URISyntaxException | InvalidKeyException | NoSuchAlgorithmException ex) {
- throw new DuoWebException("Duo AuthAPI preauth request failed: " + ex.getMessage());
- }
- }
-
-}
\ No newline at end of file
diff --git a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/DuoPreauthResponse.java b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/DuoPreauthResponse.java
deleted file mode 100644
index 53cc918bb..000000000
--- a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/DuoPreauthResponse.java
+++ /dev/null
@@ -1,61 +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.shibboleth.idp.authn.duo.impl;
-
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
-import com.fasterxml.jackson.annotation.JsonProperty;
-
-/**
- * Describes the results of an pre-authentication attempt via the Duo AuthAPI.
- */
- at JsonIgnoreProperties(ignoreUnknown = true)
-public class DuoPreauthResponse extends DuoAuthAPIResponse {
-
- /** the {@link List} of {@link DuoDevice}s registered. */
- @JsonProperty("devices") @Nonnull private List<DuoDevice> devices = new ArrayList<>();
-
- /** the {@link URL} for the self-enrollment portal. */
- @JsonProperty("enroll_portal_url") @Nullable private URL enrollPortalURL;
-
- /**
- * Get the Duo devices.
- *
- * @return Duo devices
- */
- @Nonnull public Collection<DuoDevice> getDevices() {
- return devices;
- }
-
- /**
- * Get the Duo enrollment portal URL.
- *
- * @return Duo enrollment portal URL
- */
- @Nullable public URL getEnrollPortalURL() {
- return enrollPortalURL;
- }
-
-}
\ No newline at end of file
diff --git a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/DuoResponseWrapper.java b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/DuoResponseWrapper.java
deleted file mode 100644
index 4aa0d9422..000000000
--- a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/DuoResponseWrapper.java
+++ /dev/null
@@ -1,62 +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.shibboleth.idp.authn.duo.impl;
-
-import javax.annotation.Nonnull;
-
-import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
-import com.fasterxml.jackson.annotation.JsonProperty;
-
-/**
- * Handle a generic object returned from the response that will come from the Duo
- * AuthAPI.
- *
- * @param <T> the subclass of {@link DuoAuthAPIResponse} being wrapped
- */
- at JsonIgnoreProperties(ignoreUnknown = true)
-public class DuoResponseWrapper<T extends DuoAuthAPIResponse> {
-
- /** the inner response. */
- @JsonProperty("response")
- private T response;
-
- /** the response status. */
- @JsonProperty("stat")
- private String stat;
-
- /**
- * Get the inner response.
- *
- * @return inner response
- */
- @Nonnull public T getResponse() {
- assert response != null;
- return response;
- }
-
- /**
- * Get the response status.
- *
- * @return response status
- */
- @Nonnull public String getStat() {
- assert stat != null;
- return stat;
- }
-
-}
\ No newline at end of file
diff --git a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/DuoSupport.java b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/DuoSupport.java
deleted file mode 100644
index 880768951..000000000
--- a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/DuoSupport.java
+++ /dev/null
@@ -1,218 +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.shibboleth.idp.authn.duo.impl;
-
-import com.duosecurity.duoweb.Base64;
-import com.duosecurity.duoweb.DuoWeb;
-import com.duosecurity.duoweb.DuoWebException;
-import com.duosecurity.duoweb.Util;
-import com.google.common.escape.Escaper;
-import com.google.common.net.UrlEscapers;
-
-import net.shibboleth.idp.authn.duo.DuoIntegration;
-import net.shibboleth.shared.annotation.constraint.NotEmpty;
-import net.shibboleth.shared.primitive.StringSupport;
-
-import java.io.IOException;
-import java.io.UnsupportedEncodingException;
-import java.net.URI;
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
-import java.time.ZonedDateTime;
-import java.time.format.DateTimeFormatter;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-
-import javax.annotation.Nonnull;
-
-import org.apache.hc.core5.http.NameValuePair;
-import org.apache.hc.core5.http.io.support.ClassicRequestBuilder;
-
-/**
- * Helpers for DuoWeb and Duo AuthAPI operations.
- *
- * @since 3.3.0
- */
-public final class DuoSupport {
-
- /** RFC 2822 formatter for date/time. */
- public static final DateTimeFormatter RFC_2822_DATE_FORMAT;
-
- /** Constructor. */
- private DuoSupport() {
- }
-
- /**
- * Created a signed request to Duo for a user.
- *
- * @param duo integration parameters to use
- * @param username user to authenticate
- * @return the signed request string
- *
- * @throws DuoWebException if an error occurs
- */
- @Nonnull @NotEmpty public static String generateSignedRequestToken(@Nonnull final DuoIntegration duo,
- @Nonnull @NotEmpty final String username)
- throws DuoWebException {
- final String signedRequestToken;
-
- if (username == null) {
- signedRequestToken = DuoWeb.ERR_USER;
- } else if (duo.getApplicationKey() == null) {
- signedRequestToken = DuoWeb.ERR_AKEY;
- } else {
- signedRequestToken = DuoWeb.signRequest(duo.getIntegrationKey(), duo.getSecretKey(),
- duo.getApplicationKey(), username);
- }
- if (signedRequestToken.startsWith("ERR|")) {
- throw new DuoWebException(signedRequestToken);
- }
- return signedRequestToken;
- }
-
-// Checkstyle: ThrowsCount OFF
- /**
- * Verify a signed response from Duo and extract the username.
- *
- * @param duo integration parameters to use
- * @param signedResponseToken response to validate
- *
- * @return the username from the response
- * @throws DuoWebException if a Duo failure occurs
- * @throws InvalidKeyException if a key is invalid
- * @throws IOException if an I/O error occurs
- * @throws NoSuchAlgorithmException if the hashing algorithm is unavailable
- */
- @Nonnull @NotEmpty public static String validateSignedResponseToken(@Nonnull final DuoIntegration duo,
- @Nonnull @NotEmpty final String signedResponseToken)
- throws DuoWebException, InvalidKeyException, IOException, NoSuchAlgorithmException {
- try {
- if (duo.getApplicationKey() == null) {
- throw new DuoWebException(DuoWeb.ERR_AKEY);
- }
- final String username = DuoWeb.verifyResponse(duo.getIntegrationKey(), duo.getSecretKey(),
- duo.getApplicationKey(), signedResponseToken);
- assert username != null;
- return username;
- } catch (final ArrayIndexOutOfBoundsException e) {
- // This guard is to prevent an unusual issue being encountered by at least one deployer.
- throw new DuoWebException(e.getMessage());
- }
- }
-// Checkstyle: ThrowsCount ON
-
- /**
- * Sign a Duo AuthAPI request.
- *
- * @param request the request to be signed
- * @param duo integration parameters to use
- *
- * @throws InvalidKeyException bad skey value
- * @throws NoSuchAlgorithmException unknown encryption algorithm
- * @throws UnsupportedEncodingException failure from {@link java.net.URLEncoder}
- *
- * @since 3.4.0
- */
- @NotEmpty public static void signRequest(@Nonnull final ClassicRequestBuilder request,
- @Nonnull final DuoIntegration duo)
- throws InvalidKeyException, NoSuchAlgorithmException, UnsupportedEncodingException {
- final String ikey = duo.getIntegrationKey();
- final String skey = duo.getSecretKey();
- final int sigVersion = 2;
- final String date = RFC_2822_DATE_FORMAT.format(ZonedDateTime.now());
- assert date != null;
- final String canon = canonRequest(request, date, sigVersion);
- final String sig = Util.hmacSign(skey, canon);
-
- final String auth = ikey + ":" + sig;
- final String header = "Basic " + Base64.encodeBytes(auth.getBytes());
- request.addHeader("Authorization", header);
- request.addHeader("Date", date);
- }
-
- /**
- * The signature requires that the request parameters being in a particular order as specified in the API.
- *
- * @param request the request
- * @param date the date
- * @param sigVersion the signature version
- *
- * @return the parameters to be signed in their canonical order
- *
- * @throws UnsupportedEncodingException failure from {@link java.net.URLEncoder}
- */
- private static String canonRequest(@Nonnull final ClassicRequestBuilder request, @Nonnull final String date,
- final int sigVersion) throws UnsupportedEncodingException {
- final URI uri = request.getUri();
- String canon = "";
- if (sigVersion == 2) {
- canon += date + "\n";
- }
- canon += request.getMethod().toUpperCase() + "\n";
- canon += uri.getHost().toLowerCase() + "\n";
- canon += uri.getPath() + "\n";
- final List<NameValuePair> parms = request.getParameters();
- assert parms != null;
- canon += createQueryString(parms);
-
- return canon;
- }
-
- /**
- * Builds a string representation of the query string with the parameter names is alphabetical order. The names and
- * values are URL encoded and then they are concatenated with '&' in between.
- *
- * @param params the name/value pairs to be joined
- *
- * @return the canonical query string
- *
- * @throws UnsupportedEncodingException failure from {@link java.net.URLEncoder}
- */
- private static String createQueryString(@Nonnull final List<NameValuePair> params)
- throws UnsupportedEncodingException {
-
- final ArrayList<String> args = new ArrayList<>();
-
- // sort by name
- Collections.sort(params, new Comparator<NameValuePair>() {
- public int compare(final NameValuePair nvp1, final NameValuePair nvp2) {
- return nvp1.getName().compareTo(nvp2.getName());
- }
- });
-
- // URL encode and join the name/values with '='
- final Escaper escaper = UrlEscapers.urlFormParameterEscaper();
- for (final NameValuePair nvp : params) {
- final String name = escaper.escape(nvp.getName()).replace("+", "%20").replace("*", "%2A")
- .replace("%7E", "~");
- final String value = escaper.escape(nvp.getValue()).replace("+", "%20").replace("*", "%2A")
- .replace("%7E", "~");
- args.add(name + "=" + value);
- }
-
- // Concatenate everything togther with '&'
- return StringSupport.listToStringValue(args, "&");
- }
-
- static {
- RFC_2822_DATE_FORMAT = DateTimeFormatter.ofPattern("EEE', 'dd' 'MMM' 'yyyy' 'HH:mm:ss' 'Z");
- }
-
-}
\ No newline at end of file
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
deleted file mode 100644
index 497beedfd..000000000
--- a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/ExtractDuoAuthenticationFromHeaders.java
+++ /dev/null
@@ -1,263 +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.shibboleth.idp.authn.duo.impl;
-
-import java.util.Map;
-import java.util.function.Function;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import org.opensaml.profile.action.ActionSupport;
-import org.opensaml.profile.context.ProfileRequestContext;
-import org.slf4j.Logger;
-
-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.shared.annotation.constraint.NotEmpty;
-import net.shibboleth.shared.logic.Constraint;
-import net.shibboleth.shared.primitive.LoggerFactory;
-import net.shibboleth.shared.primitive.StringSupport;
-import net.shibboleth.shared.servlet.HttpServletSupport;
-
-import jakarta.servlet.http.HttpServletRequest;
-
-/**
- * An action that extracts the Duo factor and device or passcode from HTTP request headers into a
- * {@link DuoAuthenticationContext}, and attaches it to the {@link AuthenticationContext}.
-
- * @event {@link org.opensaml.profile.action.EventIds#PROCEED_EVENT_ID}
- * @event {@link AuthnEventIds#NO_CREDENTIALS}
- * @pre
- * <pre>
- * ProfileRequestContext.getSubcontext(AuthenticationContext.class) != null
- * </pre>
- *
- * @post If getHttpServletRequest() != null, the content of the headers are checked.
- * The information found will be attached via a {@link DuoAuthenticationContext}.
- */
-public class ExtractDuoAuthenticationFromHeaders extends AbstractAuthenticationAction {
-
- /** Class logger. */
- @Nonnull private final Logger log = LoggerFactory.getLogger(ExtractDuoAuthenticationFromHeaders.class);
-
- /** Whether "auto" should be the default for factor and device. */
- private boolean autoAuthenticationSupported;
-
- /** Whether to trust, and extract, the client address. */
- private boolean clientAddressTrusted;
-
- /** Header name for factor. */
- @Nonnull @NotEmpty private String factorHeaderName;
-
- /** Header name for device. */
- @Nonnull @NotEmpty private String deviceHeaderName;
-
- /** 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() {
- autoAuthenticationSupported = true;
- clientAddressTrusted = true;
-
- factorHeaderName = DuoAuthAPI.DUO_FACTOR_HEADER_NAME;
- deviceHeaderName = DuoAuthAPI.DUO_DEVICE_HEADER_NAME;
- passcodeHeaderName = DuoAuthAPI.DUO_PASSCODE_HEADER_NAME;
- }
-
- /**
- * Set the factor header name.
- *
- * @param headerName the factor header name
- */
- public void setFactorHeader(@Nonnull @NotEmpty final String headerName) {
- checkSetterPreconditions();
- factorHeaderName = Constraint.isNotNull(StringSupport.trimOrNull(headerName),
- "Factor header name cannot be null or empty.");
- }
-
- /**
- * Set the device header name.
- *
- * @param headerName the factor header name
- */
- public void setDeviceHeader(@Nonnull @NotEmpty final String headerName) {
- checkSetterPreconditions();
- deviceHeaderName = Constraint.isNotNull(StringSupport.trimOrNull(headerName),
- "Device header name cannot be null or empty.");
- }
-
- /**
- * Set the passcode header name.
- *
- * @param headerName the factor header name
- */
- public void setPasscodeHeader(@Nonnull @NotEmpty final String headerName) {
- checkSetterPreconditions();
- passcodeHeaderName = Constraint.isNotNull(StringSupport.trimOrNull(headerName),
- "Passcode header name cannot be null or empty.");
- }
-
- /**
- * Get whether the client address should be trusted for use in API calls.
- *
- * @return whether client address should be trusted
- */
- public boolean isClientAddressTrusted() {
- return clientAddressTrusted;
- }
-
- /**
- * Set whether the client address should be trusted for use in API calls.
- *
- * @param flag flag to set
- */
- public void setClientAdddressTrusted(final boolean flag) {
- checkSetterPreconditions();
- clientAddressTrusted = flag;
- }
-
- /**
- * Get whether "auto" is the default setting.
- *
- * @return whether "auto" is the default setting
- */
- public boolean isAutoAuthenticationSupported() {
- return autoAuthenticationSupported;
- }
-
- /**
- * Set whether "auto" is the default setting.
- *
- * @param flag flag to set
- */
- public void setAutoAuthenticationSupported(final boolean flag) {
- checkSetterPreconditions();
- autoAuthenticationSupported = flag;
- }
-
- /**
- * Set lookup strategy for AuthAPI pushinfo parameter.
- *
- * @param strategy lookup strategy
- */
- public void setPushInfoLookupStrategy(
- @Nullable final Function<ProfileRequestContext,Map<String,String>> strategy) {
- checkSetterPreconditions();
- pushInfoLookupStrategy = strategy;
- }
-
-// Checkstyle: CyclomaticComplexity OFF
- /** {@inheritDoc} */
- @Override protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext,
- @Nonnull final AuthenticationContext authenticationContext) {
-
- log.debug("{} Checking for Duo authentication headers", getLogPrefix());
-
- final DuoAuthenticationContext duoCtx = new DuoAuthenticationContext();
-
- extractHeaders(duoCtx);
-
- if (duoCtx.getFactor() == null) {
- if (autoAuthenticationSupported && !profileRequestContext.isBrowserProfile()) {
- log.debug("{} Non-browser request with no Duo factor specified, enabling auto method", getLogPrefix());
- duoCtx.setFactor(DuoAuthAPI.DUO_FACTOR_AUTO);
- } else {
- log.debug("{} No Duo factor specified, auto method will not be attempted", getLogPrefix());
- ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);
- return;
- }
- }
-
- // Check for missing passcode.
- if (DuoAuthAPI.DUO_FACTOR_PASSCODE.equals(duoCtx.getFactor())) {
- if (duoCtx.getPasscode() == null) {
- log.warn("{} Request for passcode-based Duo login with no password supplied", getLogPrefix());
- ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);
- return;
- }
- } else if (autoAuthenticationSupported && duoCtx.getDeviceID() == null) {
- // Set auto device if needed.
- 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.
- *
- * @param context the DuoApiAuthContext to store the parameters in
- */
- protected void extractHeaders(@Nonnull final DuoAuthenticationContext context) {
-
- final HttpServletRequest httpRequest = getHttpServletRequest();
- if (httpRequest == null) {
- return;
- }
-
- if (clientAddressTrusted) {
- context.setClientAddress(HttpServletSupport.getRemoteAddr(httpRequest));
- }
-
- final String factor = httpRequest.getHeader(factorHeaderName);
- if (factor != null && !factor.isEmpty()) {
- context.setFactor(factor);
- }
-
- final String device = httpRequest.getHeader(deviceHeaderName);
- if (device != null && !device.isEmpty()) {
- context.setDeviceID(device);
- }
-
- final String passcode = httpRequest.getHeader(passcodeHeaderName);
- if (passcode != null && !passcode.isEmpty()) {
- context.setPasscode(passcode);
- }
- }
-
-}
diff --git a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/ValidateDuoAuthAPI.java b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/ValidateDuoAuthAPI.java
deleted file mode 100644
index 1722f20e7..000000000
--- a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/ValidateDuoAuthAPI.java
+++ /dev/null
@@ -1,366 +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.shibboleth.idp.authn.duo.impl;
-
-
-import java.security.Principal;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-import java.util.function.Function;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-import javax.security.auth.Subject;
-
-import org.opensaml.profile.action.ActionSupport;
-import org.opensaml.profile.action.EventIds;
-import org.opensaml.profile.context.ProfileRequestContext;
-import org.slf4j.Logger;
-
-import com.duosecurity.duoweb.DuoWebException;
-
-import net.shibboleth.idp.authn.AbstractValidationAction;
-import net.shibboleth.idp.authn.AuthnAuditFields;
-import net.shibboleth.idp.authn.AuthnEventIds;
-import net.shibboleth.idp.authn.context.AuthenticationContext;
-import net.shibboleth.idp.authn.context.SubjectCanonicalizationContext;
-import net.shibboleth.idp.authn.duo.DuoAuthAPI;
-import net.shibboleth.idp.authn.duo.DuoIntegration;
-import net.shibboleth.idp.authn.duo.DuoPrincipal;
-import net.shibboleth.idp.authn.duo.context.DuoAuthenticationContext;
-import net.shibboleth.idp.authn.impl.AbstractAuditingValidationAction;
-import net.shibboleth.idp.profile.IdPAuditFields;
-import net.shibboleth.idp.session.context.navigate.CanonicalUsernameLookupStrategy;
-import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
-import net.shibboleth.shared.annotation.constraint.NonnullBeforeExec;
-import net.shibboleth.shared.annotation.constraint.NotEmpty;
-import net.shibboleth.shared.annotation.constraint.NotLive;
-import net.shibboleth.shared.annotation.constraint.Unmodifiable;
-import net.shibboleth.shared.collection.CollectionSupport;
-import net.shibboleth.shared.component.ComponentInitializationException;
-import net.shibboleth.shared.logic.Constraint;
-import net.shibboleth.shared.logic.FunctionSupport;
-import net.shibboleth.shared.primitive.LoggerFactory;
-
-/**
- * An action that checks for a {@link DuoAuthenticationContext} and directly produces an
- * {@link net.shibboleth.idp.authn.AuthenticationResult} based on that identity by authenticating against the Duo
- * AuthAPI.
- *
- * @event {@link org.opensaml.profile.action.EventIds#PROCEED_EVENT_ID}
- * @event {@link AuthnEventIds#AUTHN_EXCEPTION}
- * @event {@link AuthnEventIds#ACCOUNT_LOCKED}
- * @event {@link AuthnEventIds#ACCOUNT_WARNING}
- * @event {@link AuthnEventIds#ACCOUNT_ERROR}
- * @event {@link AuthnEventIds#NO_CREDENTIALS}
- * @event {@link AuthnEventIds#INVALID_CREDENTIALS}
- * @pre
- *
- * <pre>
- * ProfileRequestContext.getSubcontext(AuthenticationContext.class).getAttemptedFlow() != null
- * </pre>
- *
- * @post If AuthenticationContext.getSubcontext(DuoAuthenticationContext.class) != null, then an
- * {@link net.shibboleth.idp.authn.AuthenticationResult} is saved to the {@link AuthenticationContext} on a
- * successful login. On a failed login, the
- * {@link AbstractValidationAction#handleError(ProfileRequestContext, AuthenticationContext, String, String)}
- * method is called.
- */
-public class ValidateDuoAuthAPI extends AbstractAuditingValidationAction {
-
- /** Default prefix for metrics. */
- @Nonnull @NotEmpty private static final String DEFAULT_METRIC_NAME = "net.shibboleth.idp.authn.duo";
-
- /** Class logger. */
- @Nonnull @NotEmpty private final Logger log = LoggerFactory.getLogger(ValidateDuoAuthAPI.class);
-
- /** Lookp strategy for Duo integration. */
- @Nonnull private Function<ProfileRequestContext,DuoIntegration> duoIntegrationLookupStrategy;
-
- /** Lookup strategy for username to match against Duo identity. */
- @Nonnull private Function<ProfileRequestContext,String> usernameLookupStrategy;
-
- /** Implementation of Duo AuthApi /auth endpoint. */
- @NonnullAfterInit private DuoAuthAuthenticator authAuthenticator;
-
- /** Implementation of Duo AuthApi /preauth enpoint. */
- @NonnullAfterInit private DuoPreauthAuthenticator preauthAuthenticator;
-
- /** DuoApi context for tokens. Non-Null after preExecute **/
- @NonnullBeforeExec @NotEmpty private DuoAuthenticationContext duoContext;
-
- /** Duo integration to use. */
- @NonnullBeforeExec private DuoIntegration duoIntegration;
-
- /** Attempted username. */
- @NonnullBeforeExec @NotEmpty private String username;
-
- /** Constructor. */
- public ValidateDuoAuthAPI() {
- duoIntegrationLookupStrategy = FunctionSupport.constant(null);
- usernameLookupStrategy = new CanonicalUsernameLookupStrategy();
- setMetricName(DEFAULT_METRIC_NAME);
- }
-
- /**
- * Set DuoIntegration lookup strategy to use.
- *
- * @param strategy lookup strategy
- */
- public void setDuoIntegrationLookupStrategy(
- @Nonnull final Function<ProfileRequestContext,DuoIntegration> strategy) {
- checkSetterPreconditions();
- duoIntegrationLookupStrategy = Constraint.isNotNull(strategy, "DuoIntegration lookup strategy cannot be null");
- }
-
- /**
- * Set DuoIntegration details to use directly.
- *
- * @param duo Duo integration details
- */
- public void setDuoIntegration(@Nonnull final DuoIntegration duo) {
- checkSetterPreconditions();
- Constraint.isNotNull(duo, "DuoIntegration cannot be null");
- duoIntegrationLookupStrategy = FunctionSupport.constant(duo);
- }
-
- /**
- * Set the lookup strategy to use for the username to match against Duo identity.
- *
- * @param strategy lookup strategy
- */
- public void setUsernameLookupStrategy(@Nonnull final Function<ProfileRequestContext, String> strategy) {
- checkSetterPreconditions();
- usernameLookupStrategy = Constraint.isNotNull(strategy, "Username lookup strategy cannot be null");
- }
-
- /**
- * Set the {@link DuoAuthAuthenticator}.
- *
- * @param authenticator a Duo AuthAPI /auth endpoint implementation
- */
- public void setAuthAuthenticator(@Nonnull final DuoAuthAuthenticator authenticator) {
- checkSetterPreconditions();
- authAuthenticator = Constraint.isNotNull(authenticator, "DuoAuthAuthenticator cannot be null");
- }
-
- /**
- * Set the {@link DuoPreauthAuthenticator}.
- *
- * @param authenticator a Duo AuthAPI /preauth endpoint implementation
- */
- public void setPreauthAuthenticator(@Nonnull final DuoPreauthAuthenticator authenticator) {
- checkSetterPreconditions();
- preauthAuthenticator = Constraint.isNotNull(authenticator, "DuoPreauthAuthenticator cannot be null");
- }
-
- /** {@inheritDoc} */
- @Override protected void doInitialize() throws ComponentInitializationException {
- super.doInitialize();
-
- if (authAuthenticator == null) {
- throw new ComponentInitializationException("DuoAuthAuthenticator cannot be null");
- }
-
- if (preauthAuthenticator == null) {
- throw new ComponentInitializationException("DuoPreauthAuthenticator cannot be null");
- }
-
- }
-
- /** {@inheritDoc} */
- @Override protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext,
- @Nonnull final AuthenticationContext authenticationContext) {
-
- if (!super.doPreExecute(profileRequestContext, authenticationContext)) {
- return false;
- }
-
- duoIntegration = duoIntegrationLookupStrategy.apply(profileRequestContext);
- if (duoIntegration == null) {
- log.warn("{} No DuoIntegration returned by lookup strategy", getLogPrefix());
- ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
- return false;
- }
-
- username = usernameLookupStrategy.apply(profileRequestContext);
- if (username == null) {
- log.warn("{} No principal name available to cross-check Duo result", getLogPrefix());
- ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);
- return false;
- }
-
- duoContext = authenticationContext.getSubcontext(DuoAuthenticationContext.class);
- if (duoContext == null) {
- log.info("{} No DuoAuthenticationContext available", getLogPrefix());
- handleError(profileRequestContext, authenticationContext, "No DuoAuthenticationContext context available",
- AuthnEventIds.INVALID_AUTHN_CTX);
- recordFailure(profileRequestContext);
- return false;
- } else if (duoContext.getFactor() == null) {
- log.info("{} No factor set in DuoAuthenticationContext", getLogPrefix());
- handleError(profileRequestContext, authenticationContext, "No Duo factor set in DuoAuthenticationContext",
- AuthnEventIds.REQUEST_UNSUPPORTED);
- recordFailure(profileRequestContext);
- return false;
- }
-
- duoContext.setUsername(username);
-
- return true;
- }
-
- /** {@inheritDoc} */
- // CheckStyle: CyclomaticComplexity|MethodLength|ReturnCount OFF
- @Override protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext,
- @Nonnull final AuthenticationContext authenticationContext) {
-
- log.trace("{} Attempting Duo AuthAPI authentication", getLogPrefix());
-
- try {
- // Duo AuthAPI pre-authentication
- final DuoPreauthResponse preAuthResponse = preauthAuthenticator.authenticate(duoContext, duoIntegration);
- if (preAuthResponse == null) {
- log.info("{} No Duo AuthAPI preauthentication response", getLogPrefix());
- throw new DuoWebException("No preauthentication response");
- }
-
- final String preAuthResult = preAuthResponse.getResult();
-
- if (DuoAuthAPI.DUO_PREAUTH_RESULT_ALLOW.equals(preAuthResult)) {
- // User in bypass mode; treat as authenticated.
- log.info("{} Duo pre-authentication (bypass) succeeded for '{}'", getLogPrefix(), username);
- recordSuccess(profileRequestContext);
- buildAuthenticationResult(profileRequestContext, authenticationContext);
- return;
- }
-
- if (!DuoAuthAPI.DUO_PREAUTH_RESULT_AUTH.equals(preAuthResult)) {
- // Either deny or enroll.
- log.info("{} Duo pre-authentication failed for '{}': {}", getLogPrefix(), username,
- preAuthResponse.getStatusMessage());
- handleError(profileRequestContext, authenticationContext,
- String.format("%s:%s:%s", preAuthResult, username, preAuthResponse.getStatusMessage()),
- AuthnEventIds.ACCOUNT_ERROR);
- recordFailure(profileRequestContext);
- return;
- }
-
- // Validate device ID specified against the enrolled set.
- if (duoContext.getDeviceID() != null && !DuoAuthAPI.DUO_DEVICE_AUTO.equals(duoContext.getDeviceID())) {
- boolean found = false;
- for (final DuoDevice device : preAuthResponse.getDevices()) {
- final String deviceId = duoContext.getDeviceID();
- assert deviceId != null;
- if (deviceId.equals(device.getDevice())) {
- found = true;
- break;
- } else if (deviceId.equals(device.getName())) {
- log.debug("{} Remapped device ID based on device name ({}) for '{}'", getLogPrefix(),
- device.getName(), username);
- duoContext.setDeviceID(device.getDevice());
- found = true;
- break;
- }
- }
- if (!found) {
- log.info("{} Duo authentication failed for '{}': non-existent device ID ({})", getLogPrefix(),
- username, duoContext.getDeviceID());
- handleError(profileRequestContext, authenticationContext, AuthnEventIds.INVALID_CREDENTIALS,
- AuthnEventIds.INVALID_CREDENTIALS);
- recordFailure(profileRequestContext);
- return;
- }
- }
-
- // Duo AuthAPI authentication
- final DuoAuthResponse authenticationResponse = authAuthenticator.authenticate(duoContext, duoIntegration);
- if (authenticationResponse == null) {
- log.info("{} No Duo AuthAPI authentication response", getLogPrefix());
- throw new DuoWebException("No authentication response");
- }
-
- final String authResult = authenticationResponse.getResult();
- if (DuoAuthAPI.DUO_AUTH_RESULT_ALLOW.equals(authResult)) {
- log.info("{} Duo authentication succeeded for '{}' (Factor: {}, Device: {})", getLogPrefix(), username,
- duoContext.getFactor(), duoContext.getDeviceID());
- recordSuccess(profileRequestContext);
- buildAuthenticationResult(profileRequestContext, authenticationContext);
- } else if (DuoAuthAPI.DUO_AUTH_RESULT_DENY.equals(authResult)) {
- log.info("{} Duo authentication failed for '{}'", getLogPrefix(), username);
- handleError(profileRequestContext, authenticationContext, authenticationResponse.getStatus(),
- AuthnEventIds.INVALID_CREDENTIALS);
- recordFailure(profileRequestContext);
- } else {
- throw new DuoWebException("Unexpected authentication response");
- }
- } catch (final DuoWebException e) {
- log.error("{} Duo AuthAPI access failed for '{}'", getLogPrefix(), username, e);
- handleError(profileRequestContext, authenticationContext, e, AuthnEventIds.AUTHN_EXCEPTION);
- recordFailure(profileRequestContext);
- }
- }
- // CheckStyle: CyclomaticComplexity|MethodLength|ReturnCount OFF
-
- /** {@inheritDoc} */
- @Override protected @Nonnull Subject populateSubject(@Nonnull final Subject subject) {
- assert isPreExecuteCalled();
- final DuoPrincipal princ = new DuoPrincipal(username);
- subject.getPrincipals().add(princ);
- final Set<Principal> princs = duoIntegration.getSupportedPrincipals(Principal.class);
- subject.getPrincipals().addAll(princs);
- return subject;
- }
-
- /** {@inheritDoc} */
- @Override protected void buildAuthenticationResult(@Nonnull final ProfileRequestContext profileRequestContext,
- @Nonnull final AuthenticationContext authenticationContext) {
- super.buildAuthenticationResult(profileRequestContext, authenticationContext);
-
- // Bypass c14n. We already operate on a canonical name, so just re-confirm it.
- profileRequestContext.ensureSubcontext(SubjectCanonicalizationContext.class).setPrincipalName(username);
- }
-
- /** {@inheritDoc} */
- @Override
- @Nullable @Unmodifiable @NotLive protected Map<String,String> getAuditFields(
- @Nonnull final ProfileRequestContext profileRequestContext) {
- final Map<String,String> fields = new HashMap<>();
-
- if (username != null) {
- fields.put(IdPAuditFields.USERNAME, username);
- }
-
- if (duoIntegration != null) {
- fields.put(AuthnAuditFields.DUO_CLIENT_ID, duoIntegration.getIntegrationKey());
- }
- final DuoAuthenticationContext duoCtx = duoContext;
- if (duoCtx != null) {
- if (duoCtx.getDeviceID() != null) {
- fields.put(AuthnAuditFields.DUO_DEVICE_ID, duoCtx.getDeviceID());
- }
- if (duoCtx.getFactor() != null) {
- fields.put(AuthnAuditFields.DUO_FACTOR, duoCtx.getFactor());
- }
- }
-
- return CollectionSupport.copyToMap(fields);
- }
-
-}
\ No newline at end of file
diff --git a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/ValidateDuoWebResponse.java b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/ValidateDuoWebResponse.java
deleted file mode 100644
index 2530e1bb3..000000000
--- a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/ValidateDuoWebResponse.java
+++ /dev/null
@@ -1,255 +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.shibboleth.idp.authn.duo.impl;
-
-import java.io.IOException;
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
-import java.security.Principal;
-import java.util.Map;
-import java.util.Set;
-import java.util.function.Function;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-import javax.security.auth.Subject;
-
-import org.opensaml.profile.action.ActionSupport;
-import org.opensaml.profile.action.EventIds;
-import org.opensaml.profile.context.ProfileRequestContext;
-import org.slf4j.Logger;
-
-import com.duosecurity.duoweb.DuoWebException;
-
-import net.shibboleth.idp.authn.AuthnAuditFields;
-import net.shibboleth.idp.authn.AuthnEventIds;
-import net.shibboleth.idp.authn.context.AuthenticationContext;
-import net.shibboleth.idp.authn.context.SubjectCanonicalizationContext;
-import net.shibboleth.idp.authn.duo.DuoIntegration;
-import net.shibboleth.idp.authn.duo.DuoPrincipal;
-import net.shibboleth.idp.authn.impl.AbstractAuditingValidationAction;
-import net.shibboleth.idp.profile.IdPAuditFields;
-import net.shibboleth.idp.session.context.navigate.CanonicalUsernameLookupStrategy;
-import net.shibboleth.shared.annotation.constraint.NonnullBeforeExec;
-import net.shibboleth.shared.annotation.constraint.NotEmpty;
-import net.shibboleth.shared.annotation.constraint.NotLive;
-import net.shibboleth.shared.annotation.constraint.Unmodifiable;
-import net.shibboleth.shared.collection.CollectionSupport;
-import net.shibboleth.shared.logic.Constraint;
-import net.shibboleth.shared.logic.FunctionSupport;
-import net.shibboleth.shared.primitive.LoggerFactory;
-
-import jakarta.servlet.ServletRequest;
-
-/**
- * An action that validates a DuoWeb response message and produces an
- * {@link net.shibboleth.idp.authn.AuthenticationResult} or records error state.
- *
- * <p>The username to cross-check comes from a lookup strategy, by default a {@link CanonicalUsernameLookupStrategy}
- * that returns a username produced by an earlier authentication flow, and on success the same name is populated into
- * a {@link SubjectCanonicalizationContext} as a pre-established result for the login flow.</p>
- *
- * @event {@link EventIds#PROCEED_EVENT_ID}
- * @event {@link EventIds#INVALID_PROFILE_CTX}
- * @event {@link AuthnEventIds#INVALID_CREDENTIALS}
- * @event {@link AuthnEventIds#NO_CREDENTIALS}
- * @post ProfileRequestContext.getSubcontext(SubjectCanonicalizationContext.class).getPrincipalName() != null
- *
- * @since 3.3.0
- */
-public class ValidateDuoWebResponse extends AbstractAuditingValidationAction {
-
- /** Signed response parameter name. */
- @Nonnull @NotEmpty public static final String RESPONSE_PARAM = "sig_response";
-
- /** Default prefix for metrics. */
- @Nonnull @NotEmpty private static final String DEFAULT_METRIC_NAME = "net.shibboleth.idp.authn.duo";
-
- /** Class logger. */
- @Nonnull private final Logger log = LoggerFactory.getLogger(ValidateDuoWebResponse.class);
-
- /** Lookp strategy for Duo integration. */
- @Nonnull private Function<ProfileRequestContext,DuoIntegration> duoIntegrationLookupStrategy;
-
- /** Lookup strategy for username to match against Duo identity. */
- @Nonnull private Function<ProfileRequestContext,String> usernameLookupStrategy;
-
- /** Duo integration to use. */
- @NonnullBeforeExec private DuoIntegration duoIntegration;
-
- /** Attempted username. */
- @NonnullBeforeExec @NotEmpty private String username;
-
- /** Signed response string. */
- @NonnullBeforeExec @NotEmpty private String signedResponse;
-
- /** Constructor. */
- public ValidateDuoWebResponse() {
- duoIntegrationLookupStrategy = FunctionSupport.constant(null);
- usernameLookupStrategy = new CanonicalUsernameLookupStrategy();
- setMetricName(DEFAULT_METRIC_NAME);
- }
-
- /**
- * Set DuoIntegration lookup strategy to use.
- *
- * @param strategy lookup strategy
- */
- public void setDuoIntegrationLookupStrategy(
- @Nonnull final Function<ProfileRequestContext,DuoIntegration> strategy) {
- checkSetterPreconditions();
- duoIntegrationLookupStrategy = Constraint.isNotNull(strategy, "DuoIntegration lookup strategy cannot be null");
- }
-
- /**
- * Set DuoIntegration details to use directly.
- *
- * @param duo Duo integration details
- */
- public void setDuoIntegration(@Nonnull final DuoIntegration duo) {
- checkSetterPreconditions();
- Constraint.isNotNull(duo, "DuoIntegration cannot be null");
- duoIntegrationLookupStrategy = FunctionSupport.constant(duo);
- }
-
- /**
- * Set the lookup strategy to use for the username to match against Duo identity.
- *
- * @param strategy lookup strategy
- */
- public void setUsernameLookupStrategy(@Nonnull final Function<ProfileRequestContext,String> strategy) {
- checkSetterPreconditions();
- usernameLookupStrategy = Constraint.isNotNull(strategy, "Username lookup strategy cannot be null");
- }
-
- /** {@inheritDoc} */
- @Override
- protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext,
- @Nonnull final AuthenticationContext authenticationContext) {
-
- if (!super.doPreExecute(profileRequestContext, authenticationContext)) {
- return false;
- }
-
- duoIntegration = duoIntegrationLookupStrategy.apply(profileRequestContext);
- if (duoIntegration == null) {
- log.warn("{} No DuoIntegration returned by lookup strategy", getLogPrefix());
- ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
- return false;
- }
-
- username = usernameLookupStrategy.apply(profileRequestContext);
- if (username == null) {
- log.warn("{} No principal name available to cross-check Duo result", getLogPrefix());
- handleError(profileRequestContext, authenticationContext, AuthnEventIds.NO_CREDENTIALS,
- AuthnEventIds.NO_CREDENTIALS);
- return false;
- }
-
- final ServletRequest servletRequest = getHttpServletRequest();
- if (servletRequest == null) {
- log.error("{} No ServletRequest available", getLogPrefix());
- handleError(profileRequestContext, authenticationContext, AuthnEventIds.NO_CREDENTIALS,
- AuthnEventIds.NO_CREDENTIALS);
- return false;
- }
-
- signedResponse = servletRequest.getParameter(RESPONSE_PARAM);
- if (signedResponse == null || signedResponse.isEmpty()) {
- log.warn("{} No signed Duo response in the request", getLogPrefix());
- handleError(profileRequestContext, authenticationContext, AuthnEventIds.NO_CREDENTIALS,
- AuthnEventIds.NO_CREDENTIALS);
- recordFailure(profileRequestContext);
- return false;
- }
-
- return true;
- }
-
- /** {@inheritDoc} */
- @Override
- protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext,
- @Nonnull final AuthenticationContext authenticationContext) {
-
- log.trace("{} Signed Duo response is: {}", getLogPrefix(), signedResponse);
-
- final String usernameFromDuo;
- try {
- usernameFromDuo = DuoSupport.validateSignedResponseToken(duoIntegration, signedResponse);
- } catch (final InvalidKeyException | NoSuchAlgorithmException | DuoWebException | IOException e) {
- log.warn("{} Error validating signed Duo response for username '{}'", getLogPrefix(), username, e);
- handleError(profileRequestContext, authenticationContext, e, AuthnEventIds.INVALID_CREDENTIALS);
- recordFailure(profileRequestContext);
- return;
- }
- if (!username.equals(usernameFromDuo)) {
- log.warn("{} Username '{}' from Duo response does not match previously established username '{}'",
- getLogPrefix(), usernameFromDuo, username);
- handleError(profileRequestContext, authenticationContext, AuthnEventIds.INVALID_CREDENTIALS,
- AuthnEventIds.INVALID_CREDENTIALS);
- recordFailure(profileRequestContext);
- } else {
- log.info("{} Duo authentication succeeded for '{}'", getLogPrefix(), usernameFromDuo);
- recordSuccess(profileRequestContext);
- buildAuthenticationResult(profileRequestContext, authenticationContext);
- }
- }
-
- /** {@inheritDoc} */
- @Override
- protected @Nonnull Subject populateSubject(@Nonnull final Subject subject) {
-
- assert isPreExecuteCalled();
- final DuoPrincipal princ = new DuoPrincipal(username);
- subject.getPrincipals().add(princ);
- final Set<Principal> princs = duoIntegration.getSupportedPrincipals(Principal.class);
- subject.getPrincipals().addAll(princs);
-
- return subject;
- }
-
- /** {@inheritDoc} */
- @Override
- protected void buildAuthenticationResult(@Nonnull final ProfileRequestContext profileRequestContext,
- @Nonnull final AuthenticationContext authenticationContext) {
- super.buildAuthenticationResult(profileRequestContext, authenticationContext);
-
- // Bypass c14n. We already operate on a canonical name, so just re-confirm it.
- profileRequestContext.ensureSubcontext(SubjectCanonicalizationContext.class).setPrincipalName(username);
- }
-
- /** {@inheritDoc} */
- @Override
- @Nullable @Unmodifiable @NotLive protected Map<String,String> getAuditFields(
- @Nonnull final ProfileRequestContext profileRequestContext) {
-
- if (username != null) {
- if (duoIntegration != null) {
- return Map.of(AuthnAuditFields.DUO_CLIENT_ID, duoIntegration.getIntegrationKey(),
- IdPAuditFields.USERNAME, username);
- } else {
- return CollectionSupport.singletonMap(IdPAuditFields.USERNAME, username);
- }
- } else if (duoIntegration != null) {
- return CollectionSupport.singletonMap(AuthnAuditFields.DUO_CLIENT_ID, duoIntegration.getIntegrationKey());
- }
-
- return super.getAuditFields(profileRequestContext);
- }
-
-}
diff --git a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/package-info.java b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/package-info.java
deleted file mode 100644
index d8616a9b3..000000000
--- a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/duo/impl/package-info.java
+++ /dev/null
@@ -1,24 +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.
- */
-
-/**
- * Implementation of Duo authentication.
- */
- at NonnullElements
-package net.shibboleth.idp.authn.duo.impl;
-
-import net.shibboleth.shared.annotation.constraint.NonnullElements;
diff --git a/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/duo/impl/DuoSupportTest.java b/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/duo/impl/DuoSupportTest.java
deleted file mode 100644
index 83db83866..000000000
--- a/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/duo/impl/DuoSupportTest.java
+++ /dev/null
@@ -1,33 +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.shibboleth.idp.authn.duo.impl;
-
-import java.time.ZonedDateTime;
-
-import org.testng.annotations.Test;
-
-/**
- * Unit test for Duo support code.
- */
-public class DuoSupportTest {
-
- @Test public void testDateFormatting() {
- DuoSupport.RFC_2822_DATE_FORMAT.format(ZonedDateTime.now());
- }
-
-}
\ No newline at end of file
diff --git a/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/duo/impl/ExtractDuoAuthenticationFromHeadersTest.java b/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/duo/impl/ExtractDuoAuthenticationFromHeadersTest.java
deleted file mode 100644
index 4a7d97657..000000000
--- a/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/duo/impl/ExtractDuoAuthenticationFromHeadersTest.java
+++ /dev/null
@@ -1,166 +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.shibboleth.idp.authn.duo.impl;
-
-
-import org.springframework.mock.web.MockHttpServletRequest;
-import org.springframework.webflow.execution.Event;
-import org.testng.Assert;
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Test;
-
-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.authn.impl.testing.BaseAuthenticationContextTest;
-import net.shibboleth.idp.profile.testing.ActionTestingSupport;
-import net.shibboleth.shared.component.ComponentInitializationException;
-import net.shibboleth.shared.testing.ConstantSupplier;
-
-/** {@link ExtractDuoAuthenticationFromHeaders} unit test. */
-public class ExtractDuoAuthenticationFromHeadersTest extends BaseAuthenticationContextTest {
-
- private ExtractDuoAuthenticationFromHeaders action;
-
- @BeforeMethod public void setUp() throws ComponentInitializationException {
- super.setUp();
-
- action = new ExtractDuoAuthenticationFromHeaders();
- final MockHttpServletRequest request = new MockHttpServletRequest();
- action.setHttpServletRequestSupplier(new ConstantSupplier<>(request));
- action.initialize();
- }
-
- @Test public void testNoServletNoAuto() throws ComponentInitializationException {
- action = new ExtractDuoAuthenticationFromHeaders();
- action.setAutoAuthenticationSupported(false);
- action.initialize();
- final Event event = action.execute(src);
-
- ActionTestingSupport.assertEvent(event, AuthnEventIds.NO_CREDENTIALS);
-
- final AuthenticationContext authCtx = prc.getSubcontext(AuthenticationContext.class);
- assert authCtx != null;
- final DuoAuthenticationContext duoCtx = authCtx.getSubcontext(DuoAuthenticationContext.class);
- Assert.assertNull(duoCtx);
- }
-
- @Test public void testNoAuto() throws ComponentInitializationException {
- action = new ExtractDuoAuthenticationFromHeaders();
- final MockHttpServletRequest request = new MockHttpServletRequest();
- action.setHttpServletRequestSupplier(new ConstantSupplier<>(request));
- action.setAutoAuthenticationSupported(false);
- action.initialize();
- final Event event = action.execute(src);
-
- ActionTestingSupport.assertEvent(event, AuthnEventIds.NO_CREDENTIALS);
-
- final AuthenticationContext authCtx = prc.getSubcontext(AuthenticationContext.class);
- assert authCtx != null;
- final DuoAuthenticationContext duoCtx = authCtx.getSubcontext(DuoAuthenticationContext.class);
- Assert.assertNull(duoCtx);
- }
-
- @Test public void testNoServletAuto() throws ComponentInitializationException {
- action = new ExtractDuoAuthenticationFromHeaders();
- action.initialize();
-
- final Event event = action.execute(src);
- ActionTestingSupport.assertProceedEvent(event);
-
- final AuthenticationContext authCtx = prc.getSubcontext(AuthenticationContext.class);
- assert authCtx != null;
- final DuoAuthenticationContext duoCtx = authCtx.getSubcontext(DuoAuthenticationContext.class);
- assert duoCtx != null;
- Assert.assertEquals(duoCtx.getFactor(), DuoAuthAPI.DUO_FACTOR_AUTO);
- Assert.assertEquals(duoCtx.getDeviceID(), DuoAuthAPI.DUO_DEVICE_AUTO);
- Assert.assertNull(duoCtx.getPasscode());
- }
-
- @Test public void testFactorAutoDevice() {
- assert action != null;
- final MockHttpServletRequest request = ((MockHttpServletRequest) action.getHttpServletRequest());
- assert request != null;
- request.addHeader(DuoAuthAPI.DUO_FACTOR_HEADER_NAME, DuoAuthAPI.DUO_FACTOR_PUSH);
-
- final Event event = action.execute(src);
- ActionTestingSupport.assertProceedEvent(event);
-
- final AuthenticationContext authCtx = prc.getSubcontext(AuthenticationContext.class);
- assert authCtx != null;
- final DuoAuthenticationContext duoCtx = authCtx.getSubcontext(DuoAuthenticationContext.class);
- assert duoCtx != null;
- Assert.assertEquals(duoCtx.getFactor(), DuoAuthAPI.DUO_FACTOR_PUSH);
- Assert.assertEquals(duoCtx.getDeviceID(), DuoAuthAPI.DUO_DEVICE_AUTO);
- Assert.assertNull(duoCtx.getPasscode());
- }
-
- @Test public void testDeviceAutoFactor() {
- assert action != null;
- final MockHttpServletRequest request = ((MockHttpServletRequest) action.getHttpServletRequest());
- assert request != null;
- request.addHeader(DuoAuthAPI.DUO_DEVICE_HEADER_NAME, "foo");
-
- final Event event = action.execute(src);
- ActionTestingSupport.assertProceedEvent(event);
-
- final AuthenticationContext authCtx = prc.getSubcontext(AuthenticationContext.class);
- assert authCtx != null;
- final DuoAuthenticationContext duoCtx = authCtx.getSubcontext(DuoAuthenticationContext.class);
- assert duoCtx != null;
- Assert.assertEquals(duoCtx.getFactor(), DuoAuthAPI.DUO_FACTOR_AUTO);
- Assert.assertEquals(duoCtx.getDeviceID(), "foo");
- Assert.assertNull(duoCtx.getPasscode());
- }
-
- @Test public void testNoPasscode() {
- assert action != null;
- final MockHttpServletRequest request = ((MockHttpServletRequest) action.getHttpServletRequest());
- assert request != null;
- request.addHeader(DuoAuthAPI.DUO_FACTOR_HEADER_NAME, DuoAuthAPI.DUO_FACTOR_PASSCODE);
-
- final Event event = action.execute(src);
- ActionTestingSupport.assertEvent(event, AuthnEventIds.NO_CREDENTIALS);
-
- final AuthenticationContext authCtx = prc.getSubcontext(AuthenticationContext.class);
- assert authCtx != null;
- final DuoAuthenticationContext duoCtx = authCtx.getSubcontext(DuoAuthenticationContext.class);
- assert duoCtx == null;
- }
-
- @Test public void testPasscode() {
- assert action != null;
- final MockHttpServletRequest request = ((MockHttpServletRequest) action.getHttpServletRequest());
- assert request != null;
- request.addHeader(DuoAuthAPI.DUO_FACTOR_HEADER_NAME, DuoAuthAPI.DUO_FACTOR_PASSCODE);
- request.addHeader(DuoAuthAPI.DUO_PASSCODE_HEADER_NAME, "foo");
-
- final Event event = action.execute(src);
- ActionTestingSupport.assertProceedEvent(event);
-
- final AuthenticationContext authCtx = prc.getSubcontext(AuthenticationContext.class);
- assert authCtx != null;
- final DuoAuthenticationContext duoCtx = authCtx.getSubcontext(DuoAuthenticationContext.class);
- assert duoCtx != null;
- Assert.assertEquals(duoCtx.getFactor(), DuoAuthAPI.DUO_FACTOR_PASSCODE);
- Assert.assertNull(duoCtx.getDeviceID());
- Assert.assertEquals(duoCtx.getPasscode(), "foo");
- }
-
-}
\ No newline at end of file
diff --git a/idp-conf-impl/src/main/resources/net/shibboleth/idp/conf/authn-system.xml b/idp-conf-impl/src/main/resources/net/shibboleth/idp/conf/authn-system.xml
index fc6b84a77..763539d01 100644
--- a/idp-conf-impl/src/main/resources/net/shibboleth/idp/conf/authn-system.xml
+++ b/idp-conf-impl/src/main/resources/net/shibboleth/idp/conf/authn-system.xml
@@ -326,15 +326,6 @@
</constructor-arg>
</bean>
- <!-- TBD: If still needed, likely moved to Duo OIDC plugin. -->
- <bean p:id="duo" class="net.shibboleth.idp.authn.principal.GenericPrincipalService"
- c:claz="net.shibboleth.idp.authn.duo.DuoPrincipal">
- <constructor-arg name="serializer">
- <bean class="net.shibboleth.idp.authn.principal.SimplePrincipalSerializer"
- c:claz="net.shibboleth.idp.authn.duo.DuoPrincipal" c:name="DUO" />
- </constructor-arg>
- </bean>
-
<bean p:id="totp" class="net.shibboleth.idp.authn.principal.GenericPrincipalService"
c:claz="net.shibboleth.idp.authn.principal.TOTPPrincipal">
<constructor-arg name="serializer">
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list