[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