[java-identity-provider] branch main updated: IDP-2286 - Add equivalent of PasswordlessCookieManager from Duo plugin

Scott Cantor cantor.2 at osu.edu
Tue Mar 25 18:47:09 UTC 2025


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=fb4aebcad09fefeda68d2680418188fb4aa9ed84

The following commit(s) were added to refs/heads/main by this push:
     new fb4aebcad IDP-2286 - Add equivalent of PasswordlessCookieManager from Duo plugin
fb4aebcad is described below

commit fb4aebcad09fefeda68d2680418188fb4aa9ed84
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Tue Mar 25 14:47:06 2025 -0400

    IDP-2286 - Add equivalent of PasswordlessCookieManager from Duo plugin
    
    https://shibboleth.atlassian.net/browse/IDP-2286
    
    Added utility bean for managing cached username.
    Converted original Password code to use new utility bean.
---
 .../idp/authn/impl/PrePopulateUsername.java        | 57 ++----------------
 .../idp/authn/impl/ValidateCredentials.java        | 70 ++++------------------
 .../net/shibboleth/idp/conf/global-system.xml      | 10 ++++
 .../idp/flows/authn/password-authn-beans.xml       |  8 +--
 .../idp/module/conf/authn/authn.properties         |  2 +-
 .../idp/module/conf/authn/authn.properties         |  5 ++
 6 files changed, 33 insertions(+), 119 deletions(-)

diff --git a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/PrePopulateUsername.java b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/PrePopulateUsername.java
index 7e735cf38..da7d260f7 100644
--- a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/PrePopulateUsername.java
+++ b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/PrePopulateUsername.java
@@ -36,12 +36,9 @@ import net.shibboleth.shared.annotation.constraint.NonnullBeforeExec;
 import net.shibboleth.shared.annotation.constraint.NotEmpty;
 import net.shibboleth.shared.collection.CollectionSupport;
 import net.shibboleth.shared.logic.Constraint;
-import net.shibboleth.shared.net.CookieManager;
-import net.shibboleth.shared.net.URISupport;
 import net.shibboleth.shared.primitive.LoggerFactory;
 import net.shibboleth.shared.primitive.StringSupport;
-import net.shibboleth.shared.security.DataSealer;
-import net.shibboleth.shared.security.DataSealerException;
+import net.shibboleth.shared.security.EncryptedCookieManager;
 
 /**
  * An action to populate a username into a cleared {@link UsernamePasswordContext}, either from a form
@@ -65,15 +62,9 @@ public class PrePopulateUsername extends AbstractExtractionAction {
 
     /** Form parameter name to carry username. */
     @Nonnull @NotEmpty private String usernameFieldName;
-
-    /** Username cookie name. */
-    @Nullable @NotEmpty private String cookieName;
     
     /** Optional cookie manager to use. */
-    @Nullable private CookieManager cookieManager;
-
-    /** Optional data sealer to use. */
-    @Nullable private DataSealer dataSealer;
+    @Nullable private EncryptedCookieManager cookieManager;
     
     /** Order to pull username from. */
     @Nonnull private List<String> precedence;
@@ -117,37 +108,15 @@ public class PrePopulateUsername extends AbstractExtractionAction {
     }
 
     /**
-     * Set cookie name to use for cached username.
-     * 
-     * @param name cookie name
-     */
-    public void setCookieName(@Nullable final String name) {
-        checkSetterPreconditions();
-
-        cookieName = StringSupport.trimOrNull(name);
-    }
-
-    /**
-     * Sets optional {@link CookieManager} to use.
+     * Sets optional {@link EncryptedCookieManager} to use.
      * 
      * @param manager cookie manager
      */
-    public void setCookieManager(@Nullable final CookieManager manager) {
+    public void setEncryptedCookieManager(@Nullable final EncryptedCookieManager manager) {
         checkSetterPreconditions();
         
         cookieManager = manager;
     }
-
-    /**
-     * Sets optional {@link DataSealer} to use.
-     * 
-     * @param sealer data sealer
-     */
-    public void setDataSealer(@Nullable final DataSealer sealer) {
-        checkSetterPreconditions();
-        
-        dataSealer = sealer;
-    }
     
     /**
      * Sets the precedence rules to use in populating the username.
@@ -258,23 +227,7 @@ public class PrePopulateUsername extends AbstractExtractionAction {
      * @return username from existing sealed cookie, or null
      */
     @Nullable private String getUsernameFromCookie(@Nonnull final ProfileRequestContext profileRequestContext) {
-        
-        if (cookieManager != null && dataSealer != null && cookieName != null) {
-            final String cookie = URISupport.doURLDecode(cookieManager.getCookieValue(cookieName, null));
-            if (cookie != null) {
-                try {
-                    assert dataSealer != null;
-                    return dataSealer.unwrap(cookie);
-                } catch (final DataSealerException e) {
-                    log.warn("{} Unable to unwrap sealed username cookie", getLogPrefix(), e);
-                    assert cookieName != null;
-                    assert cookieManager != null;
-                    cookieManager.unsetCookie(cookieName);
-                }
-            }
-        }
-        
-        return null;
+        return cookieManager != null ? cookieManager.readCookie() : null;
     }
     
     /**
diff --git a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/ValidateCredentials.java b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/ValidateCredentials.java
index 9bd6d9866..9fb222720 100644
--- a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/ValidateCredentials.java
+++ b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/ValidateCredentials.java
@@ -29,8 +29,6 @@ import org.opensaml.profile.action.EventIds;
 import org.opensaml.profile.context.ProfileRequestContext;
 import org.slf4j.Logger;
 
-import com.google.common.net.UrlEscapers;
-
 import net.shibboleth.idp.authn.AccountLockoutManager;
 import net.shibboleth.idp.authn.AuthenticationResult;
 import net.shibboleth.idp.authn.AuthnAuditFields;
@@ -44,11 +42,8 @@ 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.net.CookieManager;
 import net.shibboleth.shared.primitive.LoggerFactory;
-import net.shibboleth.shared.primitive.StringSupport;
-import net.shibboleth.shared.security.DataSealer;
-import net.shibboleth.shared.security.DataSealerException;
+import net.shibboleth.shared.security.EncryptedCookieManager;
 
 /**
  * An action that processes a list of {@link CredentialValidator} objects to produce an {@link AuthenticationResult}.
@@ -289,53 +284,21 @@ public class ValidateCredentials extends AbstractAuditingValidationAction implem
      * @since 4.1.0
      */
     public static class UsernamePasswordCleanupHook implements Consumer<ProfileRequestContext> {
-
-        /** Class logger. */
-        @Nonnull private final Logger log = LoggerFactory.getLogger(UsernamePasswordCleanupHook.class);
         
-        /** Username cookie name. */
-        @Nullable @NotEmpty private String cookieName;
-
         /** Optional cookie manager to use. */
-        @Nullable private CookieManager cookieManager;
-
-        /** Optional data sealer to use. */
-        @Nullable private DataSealer dataSealer;
-
-        /**
-         * Set cookie name to use for cached username.
-         * 
-         * @param name cookie name
-         * 
-         * @since 5.1.0
-         */
-        public void setCookieName(@Nullable final String name) {
-            cookieName = StringSupport.trimOrNull(name);
-        }
+        @Nullable private EncryptedCookieManager cookieManager;
         
         /**
-         * Sets optional {@link CookieManager} to use.
+         * Sets optional {@link EncryptedCookieManager} to use.
          * 
          * @param manager cookie manager
          * 
-         * @since 5.1.0
+         * @since 5.2.0
          */
-        public void setCookieManager(@Nullable final CookieManager manager) {
+        public void setEncryptedCookieManager(@Nullable final EncryptedCookieManager manager) {
             cookieManager = manager;
         }
-
-        /**
-         * Sets optional {@link DataSealer} to use.
-         * 
-         * @param sealer data sealer
-         * 
-         * @since 5.1.0
-         */
-        public void setDataSealer(@Nullable final DataSealer sealer) {
-            dataSealer = sealer;
-        }
         
-// Checkstyle: CyclomaticComplexity OFF
         /** {@inheritDoc} */
         public void accept(@Nullable final ProfileRequestContext input) {
             
@@ -350,31 +313,18 @@ public class ValidateCredentials extends AbstractAuditingValidationAction implem
                 return;
             }
             
-            final String localCookieName = cookieName;
             if (authnCtx.isResultCacheable()) {
-                if (cookieManager != null && dataSealer != null && localCookieName != null) {
-                    String wrapped = upCtx.getUsername();
-                    if (wrapped != null) {
-                        try {
-                            assert dataSealer != null;
-                            wrapped = dataSealer.wrap(wrapped);
-                            assert cookieManager != null;
-                            cookieManager.addCookie(localCookieName,
-                                    UrlEscapers.urlFormParameterEscaper().escape(wrapped));
-                        } catch (final DataSealerException e) {
-                            wrapped = null;
-                            log.warn("Error sealing username cookie", e);
-                        }
-                    }
+                final String username = upCtx.getUsername();
+                if (cookieManager != null && username != null) {
+                    cookieManager.writeCookie(username);
                 }
-            } else if (cookieManager != null && localCookieName != null) {
-                cookieManager.unsetCookie(localCookieName);
+            } else if (cookieManager != null) {
+                cookieManager.clearCookie();
             }
 
             upCtx.setPassword(null);
             authnCtx.removeSubcontext(upCtx);
         }
     }
-// Checkstyle: CyclomaticComplexity ON
 
 }
\ No newline at end of file
diff --git a/idp-conf-impl/src/main/resources/net/shibboleth/idp/conf/global-system.xml b/idp-conf-impl/src/main/resources/net/shibboleth/idp/conf/global-system.xml
index 90dc1c19b..bcf526d91 100644
--- a/idp-conf-impl/src/main/resources/net/shibboleth/idp/conf/global-system.xml
+++ b/idp-conf-impl/src/main/resources/net/shibboleth/idp/conf/global-system.xml
@@ -363,6 +363,16 @@
         p:cookiePath="%{idp.cookie.path:/}"
         p:maxAge="%{idp.cookie.maxAge:31536000}" />
 
+    <!--
+    Helper for caching username in cookie in authn flows. This is global for optional use by deployers in custom logic.
+    -->
+    <bean id="shibboleth.authn.UsernameCookieManager" lazy-init="true"
+        class="net.shibboleth.shared.security.EncryptedCookieManager"
+        p:dataSealer="#{'%{idp.authn.usernameCookieName:}'.trim().isEmpty() ? null : getObject('shibboleth.DataSealer')}"
+        p:cookieManager-ref="shibboleth.PersistentCookieManager"
+        p:cookieName="%{idp.authn.usernameCookieName:}" />
+        
+
     <bean id="shibboleth.StorageService" lazy-init="true"
         class="%{idp.storage.StorageService:org.opensaml.storage.impl.MemoryStorageService}"
         p:cleanupInterval="%{idp.storage.cleanupInterval:PT10M}" />
diff --git a/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/password-authn-beans.xml b/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/password-authn-beans.xml
index eb489d61f..e95d683c4 100644
--- a/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/password-authn-beans.xml
+++ b/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/password-authn-beans.xml
@@ -52,9 +52,7 @@
     <bean id="PrePopulateUsername"
             class="net.shibboleth.idp.authn.impl.PrePopulateUsername" scope="prototype"
             p:httpServletRequestSupplier-ref="shibboleth.HttpServletRequestSupplier"
-            p:dataSealer="#{'%{idp.authn.usernameCookieName:}'.trim().isEmpty() ? null : getObject('shibboleth.DataSealer')}"
-            p:cookieManager="#{'%{idp.authn.usernameCookieName:}'.trim().isEmpty() ? null : getObject('shibboleth.PersistentCookieManager')}"
-            p:cookieName="%{idp.authn.usernameCookieName:}"
+            p:encryptedCookieManager="#{'%{idp.authn.usernameCookieName:}'.trim().isEmpty() ? null : getObject('shibboleth.authn.UsernameCookieManager')}"
             p:usernameFieldName="#{getObject('shibboleth.authn.Password.UsernameFieldName') ?: '%{idp.authn.Password.usernameFieldName:j_username}'.trim()}">
         <property name="precedence">
             <bean parent="shibboleth.CommaDelimStringArray"
@@ -78,9 +76,7 @@
         p:usableFlows-ref="UsableC14NFlows" />
     
     <bean id="DefaultCleanupHook" class="net.shibboleth.idp.authn.impl.ValidateCredentials.UsernamePasswordCleanupHook"
-        p:dataSealer="#{'%{idp.authn.usernameCookieName:}'.trim().isEmpty() ? null : getObject('shibboleth.DataSealer')}"
-        p:cookieManager="#{'%{idp.authn.usernameCookieName:}'.trim().isEmpty() ? null : getObject('shibboleth.PersistentCookieManager')}"
-        p:cookieName="%{idp.authn.usernameCookieName:}" />
+        p:encryptedCookieManager="#{'%{idp.authn.usernameCookieName:}'.trim().isEmpty() ? null : getObject('shibboleth.authn.UsernameCookieManager')}" />
     
     <!-- New action bean that uses CredentialValidator chains. -->
     <bean id="ValidateCredentials"
diff --git a/idp-conf-impl/src/main/resources/net/shibboleth/idp/module/conf/authn/authn.properties b/idp-conf-impl/src/main/resources/net/shibboleth/idp/module/conf/authn/authn.properties
index 3d412208c..018ed29f6 100644
--- a/idp-conf-impl/src/main/resources/net/shibboleth/idp/module/conf/authn/authn.properties
+++ b/idp-conf-impl/src/main/resources/net/shibboleth/idp/module/conf/authn/authn.properties
@@ -27,7 +27,7 @@
 # Login flow audit logging (defaults false for log compatibility)
 #idp.authn.audit.enabled = false
 
-# Uncomment to cache username in cookie (e.g. __Host-shib_idp_username)
+# Set this to cache username in cookie (e.g. __Host-shib_idp_username)
 #idp.authn.usernameCookieName =
 # Precedence for pulling existing username into authentication forms
 #idp.authn.usernamePrecedence = form,session,cookie
diff --git a/idp-conf-impl/src/test/resources/net/shibboleth/idp/module/conf/authn/authn.properties b/idp-conf-impl/src/test/resources/net/shibboleth/idp/module/conf/authn/authn.properties
index 54b659f84..ee5511223 100644
--- a/idp-conf-impl/src/test/resources/net/shibboleth/idp/module/conf/authn/authn.properties
+++ b/idp-conf-impl/src/test/resources/net/shibboleth/idp/module/conf/authn/authn.properties
@@ -27,6 +27,11 @@
 # Login flow audit logging (defaults false for log compatibility)
 idp.authn.audit.enabled = true
 
+# Set this to cache username in cookie (e.g. __Host-shib_idp_username)
+idp.authn.usernameCookieName = __Host-shib_idp_username
+# Precedence for pulling existing username into authentication forms
+#idp.authn.usernamePrecedence = form,session,cookie
+
 # Revocation (administrative logout)
 #idp.authn.revocation = false
 #idp.authn.revocation.lifetime = %{idp.authn.defaultAuthnLifetime:PT12H}

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


More information about the commits mailing list