Class LegacyAwareDaoAuthenticationProvider

java.lang.Object
org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider
org.springframework.security.authentication.dao.DaoAuthenticationProvider
ubic.gemma.core.security.authentication.LegacyAwareDaoAuthenticationProvider
All Implemented Interfaces:
org.springframework.beans.factory.Aware, org.springframework.beans.factory.InitializingBean, org.springframework.context.MessageSourceAware, org.springframework.security.authentication.AuthenticationProvider

public class LegacyAwareDaoAuthenticationProvider extends org.springframework.security.authentication.dao.DaoAuthenticationProvider
DaoAuthenticationProvider that understands Gemma's two pre-Phase-2 legacy password formats, both bare 40-char hex SHA-1 with no prefix:
  1. Username-salt (post-2009-11-23): SHA-1(rawPassword + "{" + username + "}") — configured via <s:salt-source user-property="username"/>. See sql/init-data.sql.
  2. Fixed system-wide salt (pre-2009-11-23): SHA-1(rawPassword + "{gooblyfoobly}") — configured via SystemWideSaltSource with gemma.salt=gooblyfoobly in build.properties (commit 66f574e926, 2008-10-02). Users created before the 2009 salt-source switch (notably the original administrator account) still have hashes in this format in production gemd if their password hasn't been rotated.

The two are structurally indistinguishable (both 40-char SHA-1 hex), so verification tries username-salt first, then fixed-salt; only if both miss is the password rejected.

For any other stored hash format ({bcrypt}-prefixed or bare BCrypt are the currently supported alternatives) verification falls through to the stock DaoAuthenticationProvider machinery, which delegates to the configured PasswordEncoder (GemmaLegacyAwarePasswordEncoder).

Why this exists (Phase 3 cloud-readiness)

Spring Security 6's PasswordEncoder interface is username-agnostic and the username-as-salt scheme requires the username at verify time. The previous Phase-2 implementation pushed the username through a ThreadLocal on the encoder before super.additionalAuthenticationChecks ran. That ThreadLocal is hostile to async / reactive flows and per-request thread reuse, which is why it has been removed.

Now the legacy SHA-1 verification happens here, with the authoritative username sourced from userDetails.getUsername() (loaded by UserDetailsService) — no thread-bound state of any kind.

Password upgrade flow

After additionalAuthenticationChecks returns successfully, the stock DaoAuthenticationProvider.authenticate(...) does:
  if (passwordEncoder.upgradeEncoding(user.getPassword())) {
      String newPassword = passwordEncoder.encode(presented);
      user = userDetailsPasswordService.updatePassword(user, newPassword);
  }
GemmaLegacyAwarePasswordEncoder.upgradeEncoding(String) returns true for legacy hashes, so a legacy match here triggers an automatic re-encode to {bcrypt} and a write-back via UserManagerImpl (which implements UserDetailsPasswordService).
Author:
Gemma
  • Field Summary

    Fields
    Modifier and Type
    Field
    Description
    static final String
    Historical SystemWideSaltSource value from build.properties (commit 66f574e926, 2008-10-02).

    Fields inherited from class org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider

    hideUserNotFoundExceptions, logger, messages
  • Constructor Summary

    Constructors
    Constructor
    Description
     
  • Method Summary

    Modifier and Type
    Method
    Description
    protected void
    additionalAuthenticationChecks(org.springframework.security.core.userdetails.UserDetails userDetails, org.springframework.security.authentication.UsernamePasswordAuthenticationToken authentication)
     
     
    void
    Override the system-wide salt used for the pre-2009 fixed-salt fallback.

    Methods inherited from class org.springframework.security.authentication.dao.DaoAuthenticationProvider

    createSuccessAuthentication, doAfterPropertiesSet, getPasswordEncoder, getUserDetailsService, retrieveUser, setCompromisedPasswordChecker, setPasswordEncoder, setUserDetailsPasswordService, setUserDetailsService

    Methods inherited from class org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider

    afterPropertiesSet, authenticate, getPostAuthenticationChecks, getPreAuthenticationChecks, getUserCache, isForcePrincipalAsString, isHideUserNotFoundExceptions, setAlwaysPerformAdditionalChecksOnUser, setAuthoritiesMapper, setForcePrincipalAsString, setHideUserNotFoundExceptions, setMessageSource, setPostAuthenticationChecks, setPreAuthenticationChecks, setUserCache, supports

    Methods inherited from class Object

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • Field Details

    • SYSTEM_WIDE_SALT

      public static final String SYSTEM_WIDE_SALT
      Historical SystemWideSaltSource value from build.properties (commit 66f574e926, 2008-10-02). Used by the administrator row and any other account whose password has not been rotated since the 2009-11-23 switch to username-salt. Default; override via setSystemWideSalt(String) (or the gemma.legacy.salt property if Spring-wired) if the production salt has been rotated since 2008.
      See Also:
  • Constructor Details

    • LegacyAwareDaoAuthenticationProvider

      public LegacyAwareDaoAuthenticationProvider()
  • Method Details

    • setSystemWideSalt

      public void setSystemWideSalt(String salt)
      Override the system-wide salt used for the pre-2009 fixed-salt fallback. Setter form so Spring can inject from a property without restructuring the constructor.
    • getSystemWideSalt

      public String getSystemWideSalt()
    • additionalAuthenticationChecks

      protected void additionalAuthenticationChecks(org.springframework.security.core.userdetails.UserDetails userDetails, org.springframework.security.authentication.UsernamePasswordAuthenticationToken authentication) throws org.springframework.security.core.AuthenticationException
      Overrides:
      additionalAuthenticationChecks in class org.springframework.security.authentication.dao.DaoAuthenticationProvider
      Throws:
      org.springframework.security.core.AuthenticationException