Class SecurityConfig

java.lang.Object
ubic.gemma.core.security.SecurityConfig

@Configuration @ImportResource("classpath:ubic/gemma/core/security/applicationContext-*.xml") public class SecurityConfig extends Object
Core Spring Security wiring for Gemma, replacing the legacy applicationContext-security.xml.

This class is the Phase-3 XML→Java config migration for everything in that XML file except the parts that already moved:

What this class still defines:

  • Re-imports the gsec context (gsec's applicationContext-gsec.xml defined anonymousAuthenticationProvider, permissionEvaluator, roleHierarchy, the 14 other after-invocation providers, etc.). Done via ImportResource since the gsec artifact still ships its bean definitions as XML.
  • passwordEncoder (Gemma-specific legacy-aware encoder).
  • daoAuthenticationProvider (Gemma's LegacyAwareDaoAuthenticationProvider binding the username to the encoder's ThreadLocal for SHA-1 legacy hashes).
  • runAsManager + runAsAuthenticationProvider (both keyed by ${gemma.runas.password}).
  • authenticationManager (Spring Security 6 ProviderManager wiring up the three providers in the legacy XML's order).
  • sessionRegistry (used by the web-side concurrency-control filter).
  • authenticationLoggerListener (Spring's stock LoggerListener).
  • Four Gemma-specific after-invocation provider beans (composite-sequence, data-vector, differential-expression-result collection filters) defined here so they can be looked up by name from MethodSecurityConfig.AFTER_INVOCATION_PROVIDER_BEAN_NAMES. Only three of the four are currently wired; the fourth (and a fifth — composite sequence single) are present-but-unwired pending future hook-up.
  • AspectJ autoproxy now consolidated on ComponentScanConfig via @EnableAspectJAutoProxy (replaces the legacy <aop:aspectj-autoproxy/> declarations from this XML plus applicationContext-hibernate.xml and applicationContext-serviceBeans.xml).

Spring Security 6 idioms. This is the modern stack: an explicit ProviderManager bean rather than the deprecated XML <s:authentication-manager> namespace, no WebSecurityConfigurerAdapter, no SecurityFilterChain (URL-filter chains for Gemma live in gemma-web/src/main/resources/ubic/gemma/applicationContext-security.xml and are out of scope for this migration).

Bean naming. @Bean(name = "...") is used everywhere a downstream consumer references the bean id literally (e.g. the four after-invocation provider beans looked up by name from MethodSecurityConfig; the passwordEncoder that gsec's userManager consumes by id; the runAsManager that MethodSecurityConfig.runAsManager() pulls via @Qualifier("runAsManager")). For beans referenced only by type the default method-name id is used.

userDetailsManager / groupManager aliases. gsec expects two UserDetailsService-shaped beans by those names. Both are aliases for Gemma's userManager (UserManagerImpl). The XML used <alias>; Java config does this with secondary @Bean methods that return the same instance under the required name, achieved by injecting the existing bean and re-exposing it.

  • Constructor Details

    • SecurityConfig

      public SecurityConfig()
  • Method Details

    • userManagerAliases

      @Bean public static org.springframework.beans.factory.config.BeanFactoryPostProcessor userManagerAliases()
      Register the userDetailsManager and groupManager aliases for the userManager bean (UserManagerImpl). gsec's applicationContext-gsec.xml consumes both names by reference (see the securityService constructor); the legacy XML used <alias> elements to satisfy that contract.

      Java config has no native alias declaration, so we register the aliases via a BeanFactoryPostProcessor that runs before any bean is instantiated. Both aliases must be wired at registry level (not via additional @Bean methods) so the underlying singleton is the actual userManager instance — gsec's code that pulls userDetailsManager and userService gets the same object back, preserving the legacy semantics.

      Declared static so Spring instantiates it early (before any @Configuration class processing) without leaking a partial SecurityConfig instance.

    • passwordEncoder

      @Bean(name="passwordEncoder") public org.springframework.security.crypto.password.PasswordEncoder passwordEncoder()
      Gemma-specific legacy-aware password encoder. Recognizes both the legacy SHA-1(rawPassword + "{" + username + "}") format (bare 40-hex digest, no prefix -- see sql/init-data.sql) AND new BCrypt hashes ({bcrypt} prefix per DelegatingPasswordEncoder convention). New encodings produce BCrypt; legacy hashes are flagged for upgrade-on-next-login via PasswordEncoder.upgradeEncoding(String).

      Declared with the explicit id passwordEncoder so it is injected by id into daoAuthenticationProvider below AND into ubic.gemma.core.security.authentication.UserManagerImpl (which still uses field injection by type but which gsec's XML may shadow with a fallback bean of the same id).

    • daoAuthenticationProvider

      @Bean(name="daoAuthenticationProvider") public LegacyAwareDaoAuthenticationProvider daoAuthenticationProvider(@Qualifier("userManager") org.springframework.security.core.userdetails.UserDetailsService userManager, @Qualifier("passwordEncoder") org.springframework.security.crypto.password.PasswordEncoder passwordEncoder, @Value("${gemma.legacy.salt:gooblyfoobly}") String legacySalt)
      Custom DaoAuthenticationProvider that binds the username to the password encoder's ThreadLocal so GemmaLegacyAwarePasswordEncoder can recompute the legacy SHA-1(rawPassword + "{" + username + "}") hash.

      The XML configured this as a plain bean rather than via the <s:authentication-provider user-service-ref="..."> namespace shortcut, because that shortcut only builds a vanilla DaoAuthenticationProvider. The Java equivalent is the same: build the provider, set its UserDetailsService and PasswordEncoder explicitly.

    • runAsManager

      @Bean(name="runAsManager") public org.springframework.security.access.intercept.RunAsManager runAsManager(@Value("${gemma.runas.password}") String runAsPassword)
      RunAsManagerImpl keyed by ${gemma.runas.password} with role prefix GROUP_. This is the manager consulted by the method-security interceptor when @Secured("RUN_AS_ADMIN") is encountered, producing a temporary authentication with the GROUP_RUN_AS_ADMIN authority.
    • runAsAuthenticationProvider

      @Bean(name="runAsAuthenticationProvider") public org.springframework.security.access.intercept.RunAsImplAuthenticationProvider runAsAuthenticationProvider(@Value("${gemma.runas.password}") String runAsPassword)
      Provider that validates the temporary RunAsUserToken produced by runAsManager(String). The key MUST match the run-as manager's key, else AuthorizationServiceException is thrown at the authenticate call.
    • authenticationManager

      @Bean(name="authenticationManager") public org.springframework.security.authentication.AuthenticationManager authenticationManager(@Qualifier("daoAuthenticationProvider") org.springframework.security.authentication.AuthenticationProvider daoAuthenticationProvider, @Qualifier("runAsAuthenticationProvider") org.springframework.security.authentication.AuthenticationProvider runAsAuthenticationProvider, @Qualifier("anonymousAuthenticationProvider") org.springframework.security.authentication.AuthenticationProvider anonymousAuthenticationProvider)
      The Gemma AuthenticationManager.

      Spring Security 6 retires the deprecated XML <s:authentication-manager> namespace in favour of an explicit ProviderManager bean. Order of providers preserved from the XML (daoAuthenticationProvider first, then runAsAuthenticationProvider, then anonymousAuthenticationProvider). The first provider that supports the incoming token wins; ordering matters for the case where multiple providers claim a token type.

      The XML used alias="authenticationManager" which produced a bean registered under both the namespace's internal id AND authenticationManager; the explicit @Bean(name = "authenticationManager") produces the same effective registration.

      anonymousAuthenticationProvider comes from gsec's applicationContext-gsec.xml (re-imported here via ImportResource).

    • sessionRegistry

      @Bean(name="sessionRegistry") public org.springframework.security.core.session.SessionRegistry sessionRegistry()
      Session registry consumed by the <s:concurrency-control> element in gemma-web's applicationContext-security.xml. Single-session enforcement is wired on the web side; this bean is the shared store.

      Works in conjunction with the HttpSessionEventPublisher configured in web.xml.

    • authenticationLoggerListener

      @Bean(name="authenticationLoggerListener") public org.springframework.security.authentication.event.LoggerListener authenticationLoggerListener()
      Stock Spring Security LoggerListener that logs authentication events at INFO.
    • afterAclCompositeSequenceCollectionRead

      @Bean(name="afterAclCompositeSequenceCollectionRead") public AclEntryAfterInvocationCompositeSequenceCollectionByArrayDesignFilteringProvider afterAclCompositeSequenceCollectionRead(@Qualifier("aclService") org.springframework.security.acls.model.AclService aclService, org.springframework.security.acls.model.ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy, org.springframework.security.acls.model.SidRetrievalStrategy sidRetrievalStrategy)
    • afterAclCompositeSequenceRead

      @Bean(name="afterAclCompositeSequenceRead") public AclEntryAfterInvocationCompositeSequenceByArrayDesignFilteringProvider afterAclCompositeSequenceRead(@Qualifier("aclService") org.springframework.security.acls.model.AclService aclService, org.springframework.security.acls.model.ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy, org.springframework.security.acls.model.SidRetrievalStrategy sidRetrievalStrategy)
    • afterAclDataVectorCollectionRead

      @Bean(name="afterAclDataVectorCollectionRead") public AclEntryAfterInvocationDataVectorCollectionByExpressionExperimentFilteringProvider afterAclDataVectorCollectionRead(@Qualifier("aclService") org.springframework.security.acls.model.AclService aclService, org.springframework.security.acls.model.ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy, org.springframework.security.acls.model.SidRetrievalStrategy sidRetrievalStrategy)
    • afterAclDifferentialExpressionAnalysisResultCollectionRead

      @Bean(name="afterAclDifferentialExpressionAnalysisResultCollectionRead") public AclEntryAfterInvocationDifferentialExpressionAnalysisResultCollectionByResultSetFilteringProvider afterAclDifferentialExpressionAnalysisResultCollectionRead(@Qualifier("aclService") org.springframework.security.acls.model.AclService aclService, org.springframework.security.acls.model.ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy, org.springframework.security.acls.model.SidRetrievalStrategy sidRetrievalStrategy)
    • gemmaAfterAclReadQuiet

      @Bean(name="gemmaAfterAclReadQuiet") public AclEntryAfterInvocationQuietReadProvider gemmaAfterAclReadQuiet(@Qualifier("aclService") org.springframework.security.acls.model.AclService aclService, org.springframework.security.acls.model.ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy, org.springframework.security.acls.model.SidRetrievalStrategy sidRetrievalStrategy)
      Gemma-owned replacement for gsec's afterAclReadQuiet. Same semantics (READ-or-ADMIN check, null on denial) but lives in gemma-core. Used by 17 @Secured({..., "AFTER_ACL_READ_QUIET"}) call sites. Cannot be expressed as @PostAuthorize: that annotation has no "return null on denial" mode.
    • gemmaAfterAclValueObjectRead

      @Bean(name="gemmaAfterAclValueObjectRead") public AclEntryAfterInvocationValueObjectReadProvider gemmaAfterAclValueObjectRead(@Qualifier("aclService") org.springframework.security.acls.model.AclService aclService, org.springframework.security.acls.model.ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy, org.springframework.security.acls.model.SidRetrievalStrategy sidRetrievalStrategy)
      Gemma-owned replacement for gsec's afterAclValueObject. Single-object ACL READ check that also populates the SecureValueObject security-metadata fields (isPublic / isShared / userOwned / userCanWrite) as a side-effect of the ACL fetch. Cannot be expressed as @PostAuthorize: the side-effect on the returned VO is load-bearing (drives lock / share / edit UI affordances in the web layer).
    • gemmaAfterAclValueObjectCollectionRead

      @Bean(name="gemmaAfterAclValueObjectCollectionRead") public AclEntryAfterInvocationValueObjectCollectionReadProvider gemmaAfterAclValueObjectCollectionRead(@Qualifier("aclService") org.springframework.security.acls.model.AclService aclService, org.springframework.security.acls.model.ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy, org.springframework.security.acls.model.SidRetrievalStrategy sidRetrievalStrategy)
      Gemma-owned replacement for gsec's afterAclValueObjectCollection. Bulk ACL READ filter over a collection of SecureValueObjects, populating per-row security-metadata fields on every retained VO. Cannot be expressed as @PostFilter: the per-row side-effect is load-bearing.
    • gemmaAfterAclValueObjectMapRead

      @Bean(name="gemmaAfterAclValueObjectMapRead") public AclEntryAfterInvocationValueObjectMapReadProvider gemmaAfterAclValueObjectMapRead(@Qualifier("aclService") org.springframework.security.acls.model.AclService aclService, org.springframework.security.acls.model.ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy, org.springframework.security.acls.model.SidRetrievalStrategy sidRetrievalStrategy)
      Gemma-owned replacement for gsec's afterAclValueObjectMap. Bulk ACL READ filter over a Map whose keys are SecureValueObjects, populating per-key security-metadata fields on every retained key VO. Map values are NOT checked.
    • gemmaAfterAclMyDataRead

      @Bean(name="gemmaAfterAclMyDataRead") public AclEntryAfterInvocationOwnedCollectionFilteringProvider gemmaAfterAclMyDataRead(@Qualifier("aclService") org.springframework.security.acls.model.AclService aclService, org.springframework.security.acls.model.ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy, org.springframework.security.acls.model.SidRetrievalStrategy sidRetrievalStrategy)
      Gemma-owned replacement for gsec's afterAclMyDataRead. Owner-and-permission filter (ADMIN-or-WRITE) over Securable collections.
    • gemmaAfterAclMyPrivateDataRead

      @Bean(name="gemmaAfterAclMyPrivateDataRead") public AclEntryAfterInvocationPrivateCollectionFilteringProvider gemmaAfterAclMyPrivateDataRead(@Qualifier("aclService") org.springframework.security.acls.model.AclService aclService, org.springframework.security.acls.model.ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy, org.springframework.security.acls.model.SidRetrievalStrategy sidRetrievalStrategy)
      Gemma-owned replacement for gsec's afterAclMyPrivateDataRead. Private-and-readable filter (ADMIN-or-WRITE-or-READ) over Securable collections.
    • gemmaAfterAclStreamRead

      @Bean(name="gemmaAfterAclStreamRead") public AclEntryAfterInvocationStreamFilteringProvider gemmaAfterAclStreamRead(@Qualifier("aclService") AclService aclService, org.springframework.security.acls.model.ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy, org.springframework.security.acls.model.SidRetrievalStrategy sidRetrievalStrategy)
      Gemma-owned replacement for gsec's afterAclStreamRead. Lazy READ-or-ADMIN filter for Stream<? extends Securable> return types.