This is an automated email from the ASF dual-hosted git repository.

markt pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tomcat.git

commit 7f59ce77f4ae1c8d4912af7d60b9c31db1290821
Author: Mark Thomas <[email protected]>
AuthorDate: Fri Jan 9 16:39:56 2026 +0000

    Add ssoReauthenticationMode attribute to built-in Authenticators
---
 .../catalina/authenticator/AuthenticatorBase.java  |  85 ++++++++++++----
 .../authenticator/DigestAuthenticator.java         |   2 -
 .../catalina/authenticator/SSLAuthenticator.java   |   2 -
 .../catalina/authenticator/SingleSignOn.java       |   9 ++
 .../authenticator/SpnegoAuthenticator.java         |   2 -
 webapps/docs/changelog.xml                         |   5 +
 webapps/docs/config/valve.xml                      | 110 +++++++++++++++++++++
 7 files changed, 190 insertions(+), 25 deletions(-)

diff --git a/java/org/apache/catalina/authenticator/AuthenticatorBase.java 
b/java/org/apache/catalina/authenticator/AuthenticatorBase.java
index de9ecc6c26..dd0a905ef0 100644
--- a/java/org/apache/catalina/authenticator/AuthenticatorBase.java
+++ b/java/org/apache/catalina/authenticator/AuthenticatorBase.java
@@ -219,6 +219,8 @@ public abstract class AuthenticatorBase extends ValveBase 
implements Authenticat
      */
     protected SingleSignOn sso = null;
 
+    private SsoReauthenticationMode ssoReauthenticationMode = 
SsoReauthenticationMode.DEFAULT;
+
     private AllowCorsPreflight allowCorsPreflight = AllowCorsPreflight.NEVER;
 
     private volatile String jaspicAppContextID = null;
@@ -228,6 +230,15 @@ public abstract class AuthenticatorBase extends ValveBase 
implements Authenticat
 
     // ------------------------------------------------------------- Properties
 
+    public String getSsoReauthenticationMode() {
+        return ssoReauthenticationMode.name().toLowerCase(Locale.ENGLISH);
+    }
+
+    public void setSsoReauthenticationMode(String ssoReauthenticationMode) {
+        this.ssoReauthenticationMode =
+                
SsoReauthenticationMode.valueOf(ssoReauthenticationMode.trim().toUpperCase(Locale.ENGLISH));
+    }
+
     public String getAllowCorsPreflight() {
         return allowCorsPreflight.name().toLowerCase(Locale.ENGLISH);
     }
@@ -898,38 +909,66 @@ public abstract class AuthenticatorBase extends ValveBase 
implements Authenticat
          * Which cached authentication methods are used depends on the 
configuration of the SSO Valve and/or the
          * Authenticator.
          *
-         * If the SSO Valve is configured to require re-authentication, any 
cached Principal will not be used.
+         * If the SSO Valve is configured to require re-authentication, any 
cached Principal will not be used unless the
+         * Authenticator is explicitly configured (via 
ssoReauthenticationMode) to use it.
          *
          * If the SSO Valve is configured to require re-authentication, 
whether the cached user name and password can be
-         * used will be determined by the calling Authenticator type.
+         * used will be determined by the calling Authenticator type unless 
the Authenticator's ssoReauthenticationMode
+         * is explicitly configured.
          */
 
-        // Has the user already been authenticated?
-        Principal principal = request.getUserPrincipal();
+        // Determine which - if any - checks for cached authentication will be 
made.
+        boolean checkPrincipal = false;
+        boolean checkPassword = false;
+
+        // Will be null if SSO is not configured or there is no current SSO 
session
         String ssoId = (String) request.getNote(Constants.REQ_SSOID_NOTE);
-        if (principal != null) {
-            if (log.isDebugEnabled()) {
-                log.debug(sm.getString("authenticator.check.found", 
principal.getName()));
-            }
-            // Associate the session with any existing SSO session. Even if
-            // useSSO is false, this will ensure coordinated session
-            // invalidation at log out.
+
+        if (sso == null) {
+            // There is no SSO - check in case some other component has set 
the Principal
+            checkPrincipal = true;
+        } else if (ssoReauthenticationMode == SsoReauthenticationMode.DEFAULT 
&& !sso.getRequireReauthentication() ||
+                ssoReauthenticationMode == SsoReauthenticationMode.PRINCIPAL) {
+            checkPrincipal = true;
+            // If checkPrincipal is enabled then checkPassword is enabled if 
there is an SSO session
             if (ssoId != null) {
-                associate(ssoId, request.getSessionInternal(true));
+                checkPassword = true;
+            }
+        } else if (ssoId != null && (ssoReauthenticationMode == 
SsoReauthenticationMode.PASSWORD ||
+                sso.getRequireReauthentication() && 
useSsoCachedUserAndPassword)) {
+            checkPassword = true;
+        }
+
+        // Check for a cached Principal. Most likely from SSO but could be 
another component.
+        if (checkPrincipal) {
+            if (ssoId != null && sso.getRequireReauthentication()) {
+                // There is a valid SSO session but SSO Valve won't have 
cached the Principal.
+                sso.populateRequestFromSsoEntry(request, ssoId);
+            }
+
+            // Has the user already been authenticated?
+            Principal principal = request.getUserPrincipal();
+            if (principal != null) {
+                if (log.isDebugEnabled()) {
+                    log.debug(sm.getString("authenticator.check.found", 
principal.getName()));
+                }
+                // Associate the session with any existing SSO session. Even if
+                // useSSO is false, this will ensure coordinated session
+                // invalidation at log out.
+                if (ssoId != null) {
+                    associate(ssoId, request.getSessionInternal(true));
+                }
+                return true;
             }
-            return true;
         }
 
-        // Is there an SSO session against which we can try to reauthenticate?
-        if (useSsoCachedUserAndPassword && ssoId != null) {
+        // Check for a user and password cached by SSO
+        if (checkPassword) {
             if (log.isDebugEnabled()) {
                 log.debug(sm.getString("authenticator.check.sso", ssoId));
             }
             /*
-             * Try to reauthenticate using data cached by SSO. If this fails, 
either the original SSO logon was of
-             * DIGEST or SSL (which we can't reauthenticate ourselves because 
there is no cached username and password),
-             * or the realm denied the user's reauthentication for some 
reason. In either case we have to prompt the
-             * user for a logon
+             * Try to reauthenticate using data cached by SSO. If this fails 
we have to prompt the user for credentials.
              */
             if (reauthenticateFromSSO(ssoId, request)) {
                 return true;
@@ -1312,4 +1351,12 @@ public abstract class AuthenticatorBase extends 
ValveBase implements Authenticat
         FILTER,
         ALWAYS
     }
+
+
+    protected enum SsoReauthenticationMode {
+        DEFAULT,
+        PRINCIPAL,
+        PASSWORD,
+        FULL
+    }
 }
diff --git a/java/org/apache/catalina/authenticator/DigestAuthenticator.java 
b/java/org/apache/catalina/authenticator/DigestAuthenticator.java
index cfb79f0856..d36a8af856 100644
--- a/java/org/apache/catalina/authenticator/DigestAuthenticator.java
+++ b/java/org/apache/catalina/authenticator/DigestAuthenticator.java
@@ -273,8 +273,6 @@ public class DigestAuthenticator extends AuthenticatorBase {
          * authentication. Reauthenticating with the cached user name and 
password should be sufficient for DIGEST in
          * that scenario. However, the original behaviour to reauthenticate 
has been retained in case of any (very
          * unlikely) backwards compatibility issues.
-         *
-         * TODO: Make the reauthentication behaviour configurable per 
authenticator.
          */
         if (checkForCachedAuthentication(request, response, false)) {
             return true;
diff --git a/java/org/apache/catalina/authenticator/SSLAuthenticator.java 
b/java/org/apache/catalina/authenticator/SSLAuthenticator.java
index 2883f35410..fb4518f862 100644
--- a/java/org/apache/catalina/authenticator/SSLAuthenticator.java
+++ b/java/org/apache/catalina/authenticator/SSLAuthenticator.java
@@ -69,8 +69,6 @@ public class SSLAuthenticator extends AuthenticatorBase {
          * since it will not make any TLS information (client certificate etc) 
available that a web application may
          * depend on. Therefore, the reauthentication behaviour for 
CLIENT-CERT is to perform a normal CLIENT-CERT
          * authentication.
-         *
-         * TODO: Make the reauthentication behaviour configurable per 
authenticator.
          */
         if (checkForCachedAuthentication(request, response, false)) {
             return true;
diff --git a/java/org/apache/catalina/authenticator/SingleSignOn.java 
b/java/org/apache/catalina/authenticator/SingleSignOn.java
index 30f98f7daf..1ae7f90f62 100644
--- a/java/org/apache/catalina/authenticator/SingleSignOn.java
+++ b/java/org/apache/catalina/authenticator/SingleSignOn.java
@@ -506,6 +506,15 @@ public class SingleSignOn extends ValveBase {
     }
 
 
+    protected void populateRequestFromSsoEntry(Request request, String ssoId) {
+        SingleSignOnEntry entry = cache.get(ssoId);
+        if (entry != null) {
+            request.setAuthType(entry.getAuthType());
+            request.setUserPrincipal(entry.getPrincipal());
+        }
+    }
+
+
     /**
      * Register the specified Principal as being associated with the specified 
value for the single sign on identifier.
      *
diff --git a/java/org/apache/catalina/authenticator/SpnegoAuthenticator.java 
b/java/org/apache/catalina/authenticator/SpnegoAuthenticator.java
index 018d59b15d..17a376b017 100644
--- a/java/org/apache/catalina/authenticator/SpnegoAuthenticator.java
+++ b/java/org/apache/catalina/authenticator/SpnegoAuthenticator.java
@@ -143,8 +143,6 @@ public class SpnegoAuthenticator extends AuthenticatorBase {
          * Reauthenticating with the cached user name and password may not be 
sufficient for SPNEGO since it will not
          * make the delegated credentials available that a web application may 
depend on. Therefore, the
          * reauthentication behaviour for SPNEGO is to perform a normal SPNEGO 
authentication.
-         *
-         * TODO: Make the reauthentication behaviour configurable per 
authenticator.
          */
         if (checkForCachedAuthentication(request, response, false)) {
             return true;
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index e1ec12b582..8aa824209b 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -211,6 +211,11 @@
         recommended versions for Tomcat Native 1.x to 1.3.4. Update the minimum
         and recommended versions for Tomcat Native 2.x to 2.0.12. (markt)
       </update>
+      <add>
+        Add a new <code>ssoReauthenticationMode</code> to the Tomcat provided
+        Authenticators that provides a per Authenticator override of the SSO
+        Valve <code>requireReauthentication</code> attribute. (markt)
+      </add>
     </changelog>
   </subsection>
   <subsection name="Coyote">
diff --git a/webapps/docs/config/valve.xml b/webapps/docs/config/valve.xml
index 7f0ade45c6..07947471f3 100644
--- a/webapps/docs/config/valve.xml
+++ b/webapps/docs/config/valve.xml
@@ -1427,6 +1427,28 @@
         If not specified, the default value is <code>false</code>.</p>
       </attribute>
 
+      <attribute name="ssoReauthenticationMode" required="false">
+        <p>Provides a per Authenicator override of the SSO Valve attribute
+        <code>requireReauthentication</code>. It has the following options:</p>
+        <dl>
+          <dt>default</dt>
+          <dd>Reauthentication behaviour depends on the SSO Valve configuration
+              and the authentictaor type.</dd>
+          <dt>principal</dt>
+          <dd>The authenticator will look first for a cached Principal. If none
+              is found, the authenticator will look for a cached user name and
+              password and attempt to use them to reauthenticate. If that 
fails,
+              a normal authentication will be performed.</dd>
+          <dt>password</dt>
+          <dd>The authenticator will look for a cached user name and password
+              and attempt to use them to reauthenticate. If that fails, a 
normal
+              authentication will be performed.</dd>
+          <dt>full</dt>
+          <dd>Only a normal authentication will be performed. The SSO Valve is
+              effectively ignored.</dd>
+        </dl>
+      </attribute>
+
     </attributes>
 
   </subsection>
@@ -1609,6 +1631,28 @@
         If not specified, the default value is <code>false</code>.</p>
       </attribute>
 
+      <attribute name="ssoReauthenticationMode" required="false">
+        <p>Provides a per Authenicator override of the SSO Valve attribute
+        <code>requireReauthentication</code>. It has the following options:</p>
+        <dl>
+          <dt>default</dt>
+          <dd>Reauthentication behaviour depends on the SSO Valve configuration
+              and the authentictaor type.</dd>
+          <dt>principal</dt>
+          <dd>The authenticator will look first for a cached Principal. If none
+              is found, the authenticator will look for a cached user name and
+              password and attempt to use them to reauthenticate. If that 
fails,
+              a normal authentication will be performed.</dd>
+          <dt>password</dt>
+          <dd>The authenticator will look for a cached user name and password
+              and attempt to use them to reauthenticate. If that fails, a 
normal
+              authentication will be performed.</dd>
+          <dt>full</dt>
+          <dd>Only a normal authentication will be performed. The SSO Valve is
+              effectively ignored.</dd>
+        </dl>
+      </attribute>
+
       <attribute name="validateUri" required="false">
         <p>Should the URI be validated as required by RFC2617? If not 
specified,
         the default value of <code>true</code> will be used. This should
@@ -1766,6 +1810,28 @@
         If not specified, the default value is <code>false</code>.</p>
       </attribute>
 
+      <attribute name="ssoReauthenticationMode" required="false">
+        <p>Provides a per Authenicator override of the SSO Valve attribute
+        <code>requireReauthentication</code>. It has the following options:</p>
+        <dl>
+          <dt>default</dt>
+          <dd>Reauthentication behaviour depends on the SSO Valve configuration
+              and the authentictaor type.</dd>
+          <dt>principal</dt>
+          <dd>The authenticator will look first for a cached Principal. If none
+              is found, the authenticator will look for a cached user name and
+              password and attempt to use them to reauthenticate. If that 
fails,
+              a normal authentication will be performed.</dd>
+          <dt>password</dt>
+          <dd>The authenticator will look for a cached user name and password
+              and attempt to use them to reauthenticate. If that fails, a 
normal
+              authentication will be performed.</dd>
+          <dt>full</dt>
+          <dd>Only a normal authentication will be performed. The SSO Valve is
+              effectively ignored.</dd>
+        </dl>
+      </attribute>
+
     </attributes>
 
   </subsection>
@@ -1882,6 +1948,28 @@
         specified, the platform default provider will be used.</p>
       </attribute>
 
+      <attribute name="ssoReauthenticationMode" required="false">
+        <p>Provides a per Authenicator override of the SSO Valve attribute
+        <code>requireReauthentication</code>. It has the following options:</p>
+        <dl>
+          <dt>default</dt>
+          <dd>Reauthentication behaviour depends on the SSO Valve configuration
+              and the authentictaor type.</dd>
+          <dt>principal</dt>
+          <dd>The authenticator will look first for a cached Principal. If none
+              is found, the authenticator will look for a cached user name and
+              password and attempt to use them to reauthenticate. If that 
fails,
+              a normal authentication will be performed.</dd>
+          <dt>password</dt>
+          <dd>The authenticator will look for a cached user name and password
+              and attempt to use them to reauthenticate. If that fails, a 
normal
+              authentication will be performed.</dd>
+          <dt>full</dt>
+          <dd>Only a normal authentication will be performed. The SSO Valve is
+              effectively ignored.</dd>
+        </dl>
+      </attribute>
+
     </attributes>
 
   </subsection>
@@ -2060,6 +2148,28 @@
         If not specified, the default value is <code>false</code>.</p>
       </attribute>
 
+      <attribute name="ssoReauthenticationMode" required="false">
+        <p>Provides a per Authenicator override of the SSO Valve attribute
+        <code>requireReauthentication</code>. It has the following options:</p>
+        <dl>
+          <dt>default</dt>
+          <dd>Reauthentication behaviour depends on the SSO Valve configuration
+              and the authentictaor type.</dd>
+          <dt>principal</dt>
+          <dd>The authenticator will look first for a cached Principal. If none
+              is found, the authenticator will look for a cached user name and
+              password and attempt to use them to reauthenticate. If that 
fails,
+              a normal authentication will be performed.</dd>
+          <dt>password</dt>
+          <dd>The authenticator will look for a cached user name and password
+              and attempt to use them to reauthenticate. If that fails, a 
normal
+              authentication will be performed.</dd>
+          <dt>full</dt>
+          <dd>Only a normal authentication will be performed. The SSO Valve is
+              effectively ignored.</dd>
+        </dl>
+      </attribute>
+
       <attribute name="storeDelegatedCredential" required="false">
         <p>Controls if the user&apos; delegated credential will be stored in
         the user Principal. If available, the delegated credential will be


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to