[java-identity-provider] 20/28: IDP-2220 - Add username caching for login form

Scott Cantor cantor.2 at osu.edu
Wed Jan 31 14:52:06 UTC 2024


This is an automated email from the git hooks/post-receive script.

scantor pushed a commit to branch dev/thymeleaf
in repository java-identity-provider.

View the commit online:
http://git.shibboleth.net/view/?p=java-identity-provider.git;a=commit;h=84387cd3283c2582ce27b54a7e08168e0c2825cb

commit 84387cd3283c2582ce27b54a7e08168e0c2825cb
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Wed Jan 10 13:09:55 2024 -0500

    IDP-2220 - Add username caching for login form
    
    https://shibboleth.atlassian.net/browse/IDP-2220
    
    Rework config around precedence property.
    Fix Javascript in login form.
---
 .../idp/authn/impl/PrePopulateUsername.java        | 82 ++++++++++++++--------
 .../idp/flows/authn/password-authn-beans.xml       | 18 +++--
 .../net/shibboleth/idp/module/views/login.vm       |  2 +-
 3 files changed, 65 insertions(+), 37 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 eb88d118f..7e735cf38 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
@@ -14,6 +14,8 @@
 
 package net.shibboleth.idp.authn.impl;
 
+import java.util.Collection;
+import java.util.List;
 import java.util.function.Function;
 
 import javax.annotation.Nonnull;
@@ -32,6 +34,7 @@ import net.shibboleth.idp.session.IdPSession;
 import net.shibboleth.idp.session.context.SessionContext;
 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;
@@ -72,8 +75,8 @@ public class PrePopulateUsername extends AbstractExtractionAction {
     /** Optional data sealer to use. */
     @Nullable private DataSealer dataSealer;
     
-    /** Whether to pull username from existing session or not. */
-    private boolean checkSession;
+    /** Order to pull username from. */
+    @Nonnull private List<String> precedence;
     
     /** Context to operate on. */
     @NonnullBeforeExec private UsernamePasswordContext usernameContext;
@@ -85,6 +88,7 @@ public class PrePopulateUsername extends AbstractExtractionAction {
                         new ChildContextLookup<>(AuthenticationContext.class));
             
         usernameFieldName = "j_username";
+        precedence = CollectionSupport.listOf("form", "session", "cookie");
     }
     
     /**
@@ -146,16 +150,16 @@ public class PrePopulateUsername extends AbstractExtractionAction {
     }
     
     /**
-     * Sets whether tp pull username from existing session as a fallback.
+     * Sets the precedence rules to use in populating the username.
      * 
-     * <p>Defaults to false.</p>
+     * <p>Valid tokens are "form", "session", and "cookie" and that is the default order.</p>
      * 
-     * @param flag flag to set
+     * @param order precedence to use
      */
-    public void setCheckSession(final boolean flag) {
+    public void setPrecedence(@Nonnull final Collection<String> order) {
         checkSetterPreconditions();
 
-        checkSession = flag;
+        precedence = CollectionSupport.copyToList(StringSupport.normalizeStringCollection(order));
     }
 
     /** {@inheritDoc} */
@@ -176,6 +180,7 @@ public class PrePopulateUsername extends AbstractExtractionAction {
         return true;
     }
 
+// Checkstyle: CyclomaticComplexity OFF
     /** {@inheritDoc} */
     @Override
     protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext,
@@ -183,29 +188,48 @@ public class PrePopulateUsername extends AbstractExtractionAction {
         
         usernameContext.setUsername(null);
         usernameContext.setPassword(null);
-
-        String username = getUsernameFromForm(profileRequestContext);
-        if (username != null && !username.isEmpty()) {
-            log.debug("{} Populating username '{}' from form submission into UsernamePasswordContext",
-                    getLogPrefix(), username);
-            usernameContext.setUsername(username);
-            return;
-        }
-
-        username = getUsernameFromCookie(profileRequestContext);
-        if (username != null && !username.isEmpty()) {
-            log.debug("{} Populating cached username '{}' from cookie into UsernamePasswordContext",
-                    getLogPrefix(), username);
-            usernameContext.setUsername(username);
-        }
-
-        username = getUsernameFromSession(profileRequestContext, authenticationContext);
-        if (username != null && !username.isEmpty()) {
-            log.debug("{} Populating username '{}' from session into UsernamePasswordContext", getLogPrefix(),
-                    username);
-            usernameContext.setUsername(username);
+        
+        String username;
+        
+        for (final String source : precedence) {
+            switch (source) {
+                case "form":
+                    username = getUsernameFromForm(profileRequestContext);
+                    if (username != null && !username.isEmpty()) {
+                        log.debug("{} Populating username '{}' from form submission into UsernamePasswordContext",
+                                getLogPrefix(), username);
+                        usernameContext.setUsername(username);
+                        return;
+                    }
+                    break;
+                    
+                case "session":
+                    username = getUsernameFromSession(profileRequestContext, authenticationContext);
+                    if (username != null && !username.isEmpty()) {
+                        log.debug("{} Populating username '{}' from session into UsernamePasswordContext",
+                                getLogPrefix(), username);
+                        usernameContext.setUsername(username);
+                        return;
+                    }
+                    break;
+                    
+                case "cookie":
+                    username = getUsernameFromCookie(profileRequestContext);
+                    if (username != null && !username.isEmpty()) {
+                        log.debug("{} Populating cached username '{}' from cookie into UsernamePasswordContext",
+                                getLogPrefix(), username);
+                        usernameContext.setUsername(username);
+                        return;
+                    }
+                    break;
+                    
+                default:
+                    log.warn("{} Unsupported precedence value for username population: {}", getLogPrefix(), source);
+                    break;
+            }
         }
     }
+// Checkstyle: CyclomaticComplexity ON
     
     /**
      * Gets the username from a form submission.
@@ -264,7 +288,7 @@ public class PrePopulateUsername extends AbstractExtractionAction {
     @Nullable private String getUsernameFromSession(@Nonnull final ProfileRequestContext profileRequestContext,
             @Nonnull final AuthenticationContext authenticationContext) {
         
-        if (checkSession && !authenticationContext.getActiveResults().isEmpty()) {
+        if (!authenticationContext.getActiveResults().isEmpty()) {
             final SessionContext sessionContext = profileRequestContext.getSubcontext(SessionContext.class);
             if (sessionContext != null) {
                 final IdPSession idpSession = sessionContext.getIdPSession();
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 d1a56aa5e..0fe18252f 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
@@ -44,13 +44,17 @@
         p:httpServletRequestSupplier-ref="shibboleth.HttpServletRequestSupplier" />
     
     <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:usernameFieldName="#{getObject('shibboleth.authn.Password.UsernameFieldName') ?: '%{idp.authn.Password.usernameFieldName:j_username}'.trim()}"
-        p:checkSession="%{idp.authn.usernameFromSession:false}" />
+            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:usernameFieldName="#{getObject('shibboleth.authn.Password.UsernameFieldName') ?: '%{idp.authn.Password.usernameFieldName:j_username}'.trim()}">
+        <property name="precedence">
+            <bean parent="shibboleth.CommaDelimStringArray"
+                c:_0="#{'%{idp.authn.usernamePrecedence:form,session,cookie}'.trim()}" />
+        </property>
+    </bean>
     
     <bean id="ExtractUsernamePasswordFromFormRequest"
         class="net.shibboleth.idp.authn.impl.ExtractUsernamePasswordFromFormRequest" scope="prototype"
diff --git a/idp-conf-impl/src/main/resources/net/shibboleth/idp/module/views/login.vm b/idp-conf-impl/src/main/resources/net/shibboleth/idp/module/views/login.vm
index 1cf1eb4f0..16fe5e9a8 100644
--- a/idp-conf-impl/src/main/resources/net/shibboleth/idp/module/views/login.vm
+++ b/idp-conf-impl/src/main/resources/net/shibboleth/idp/module/views/login.vm
@@ -112,7 +112,7 @@ $response.addHeader("Content-Security-Policy", "script-src-attr 'unsafe-hashes'
         <!--
         const ufield = document.getElementById('username');
         const pfield = document.getElementById('password');
-        if (ufield.value == null) {
+        if (ufield.value.trim().length == 0) {
             ufield.focus();
         } else {
             pfield.focus();

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


More information about the commits mailing list