Class MethodSecurityConfig

java.lang.Object
org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration
ubic.gemma.core.security.MethodSecurityConfig
All Implemented Interfaces:
org.springframework.beans.factory.Aware, org.springframework.beans.factory.BeanFactoryAware, org.springframework.beans.factory.SmartInitializingSingleton, org.springframework.context.annotation.ImportAware

@Configuration @EnableGlobalMethodSecurity(securedEnabled=true, prePostEnabled=true, order=1, proxyTargetClass=false) public class MethodSecurityConfig extends org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration
Method-security configuration for Gemma, replacing the deprecated XML <s:global-method-security> block in applicationContext-security.xml.

Despite the user-facing renaming to @EnableMethodSecurity, Spring Security 6 still ships the legacy EnableGlobalMethodSecurity / GlobalMethodSecurityConfiguration stack that uses AccessDecisionManager, AfterInvocationManager, and RunAsManager. The new @EnableMethodSecurity annotation is an AuthorizationManager-based replacement that has no AfterInvocationManager concept — porting Gemma's after-invocation providers to the new model would require a full re-architecture (custom AuthorizationManager + a post-invocation MethodInterceptor). We deliberately stay on the legacy stack via @EnableGlobalMethodSecurity so the existing after-invocation provider beans, the existing accessDecisionManager (UnanimousBased + voter list), and the existing runAsManager keep working without modification. AfterInvocationProviderManager and friends are marked deprecated in Spring 6 but remain fully functional through the 6.x line.

Why this migration. The deprecated XML <s:global-method-security> namespace was suspected (Phase 2) of not reliably wiring the configured DefaultMethodSecurityExpressionHandler into the SpEL evaluation path used by @PreAuthorize("hasPermission(...)"), and a hasAuthority('GROUP_ADMIN') or ... workaround was kept in UserService.removeUserFromGroup. Re-testing under the Java config showed the suspicion was wrong: GlobalMethodSecurityConfiguration.setMethodSecurityExpressionHandler(List) is autowired by Spring and — when the context contains a single MethodSecurityExpressionHandler bean (gsec's securityExpressionHandler) — that bean is injected and the SpEL voter calls the correct handler with the correct PermissionEvaluator/RoleHierarchy. The hasPermission failure on the admin test path is a separate ACL-evaluation issue (likely a Sid / permission-mask / cache mismatch in the AclPermissionEvaluatorAclImpl.isGranted chain) and is tracked as a Phase-3 follow-up; the workaround stays for now. This migration still delivers (1) deprecated-XML removal, (2) explicit Java-config control over the method-security chain so any future SpEL wiring needs are obvious, and (3) a survivable path for follow-up work on the underlying ACL bug without having to touch the XML namespace again.

Bean references. All of the collaborators (permissionEvaluator, roleHierarchy, accessDecisionManager, runAsManager, and the 14 after-invocation provider beans) are defined elsewhere (gsec's applicationContext-gsec.xml and Gemma's applicationContext-security.xml) and only referenced here — never duplicated.

  • Constructor Summary

    Constructors
    Constructor
    Description
     
  • Method Summary

    Modifier and Type
    Method
    Description
    protected org.springframework.security.access.AccessDecisionManager
    Return the existing accessDecisionManager bean (UnanimousBased with all the securable read/edit + collection + map voters wired in gsec XML).
    protected org.springframework.security.access.intercept.AfterInvocationManager
    Aggregate the after-invocation providers from AFTER_INVOCATION_PROVIDER_BEAN_NAMES into an AfterInvocationProviderManager.
    protected org.springframework.security.access.expression.method.MethodSecurityExpressionHandler
    Build a MethodSecurityExpressionHandler wired with the lab's permission evaluator (gsec's AclPermissionEvaluator) and the role hierarchy.
    org.springframework.security.access.PermissionEvaluator
    permissionEvaluator(org.springframework.security.acls.model.AclService aclService, org.springframework.beans.factory.ObjectProvider<org.springframework.security.acls.model.ObjectIdentityRetrievalStrategy> oirsProvider, org.springframework.beans.factory.ObjectProvider<org.springframework.security.acls.model.SidRetrievalStrategy> srsProvider)
    The AclPermissionEvaluator that backs @PreAuthorize("hasPermission(...)") and @PostAuthorize("hasPermission(...)") SpEL.
    protected org.springframework.security.access.intercept.RunAsManager
    Return the existing runAsManager bean (RunAsManagerImpl with the GROUP_ role prefix and the configured run-as key).

    Methods inherited from class org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration

    afterSingletonsInstantiated, authenticationManager, configure, customMethodSecurityMetadataSource, getExpressionHandler, methodSecurityInterceptor, methodSecurityMetadataSource, preInvocationAuthorizationAdvice, setBeanFactory, setImportMetadata, setMethodSecurityExpressionHandler, setObjectPostProcessor, setObjectPostProcessor

    Methods inherited from class Object

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

    • MethodSecurityConfig

      public MethodSecurityConfig()
  • Method Details

    • permissionEvaluator

      @Bean(name="permissionEvaluator") public org.springframework.security.access.PermissionEvaluator permissionEvaluator(org.springframework.security.acls.model.AclService aclService, org.springframework.beans.factory.ObjectProvider<org.springframework.security.acls.model.ObjectIdentityRetrievalStrategy> oirsProvider, org.springframework.beans.factory.ObjectProvider<org.springframework.security.acls.model.SidRetrievalStrategy> srsProvider)
      The AclPermissionEvaluator that backs @PreAuthorize("hasPermission(...)") and @PostAuthorize("hasPermission(...)") SpEL. Declared here (rather than left in gsec's applicationContext-gsec.xml) so the SpEL evaluator bean owns its evaluator dependency directly — per the gsec absorption roadmap, which routes the lab's permissionEvaluator + securityExpressionHandler to this class.

      Constructor-wired with AclService only; the ObjectIdentityRetrievalStrategy and SidRetrievalStrategy are applied via setters when those beans are present in the context (they're defined in gsec XML / GemmaAclConfiguration). The fallback to Spring Security's stock strategies (used when the optional beans are absent) is what lets minimal IT contexts that don't import the full gsec stack still load cleanly.

    • createExpressionHandler

      protected org.springframework.security.access.expression.method.MethodSecurityExpressionHandler createExpressionHandler()
      Build a MethodSecurityExpressionHandler wired with the lab's permission evaluator (gsec's AclPermissionEvaluator) and the role hierarchy.

      Note: GlobalMethodSecurityConfiguration.setMethodSecurityExpressionHandler(List) is autowired by Spring and, when the application context contains exactly one MethodSecurityExpressionHandler bean (the securityExpressionHandler from gsec's applicationContext-gsec.xml), that bean is injected into the framework's expressionHandler field at startup and createExpressionHandler() is never invoked. The handler the SpEL voter ends up using is therefore the gsec one — already wired with the same permissionEvaluator and roleHierarchy we'd attach here. This override exists so the legacy XML bean could be retired in a follow-up without losing the SpEL wiring (it becomes the active definition the moment the XML bean is removed).

      Overrides:
      createExpressionHandler in class org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration
    • accessDecisionManager

      protected org.springframework.security.access.AccessDecisionManager accessDecisionManager()
      Return the existing accessDecisionManager bean (UnanimousBased with all the securable read/edit + collection + map voters wired in gsec XML). Falls back to the superclass default if the bean isn't present — that shouldn't happen in Gemma, but the defensive lookup keeps test contexts that don't import the gsec XML from blowing up at startup.
      Overrides:
      accessDecisionManager in class org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration
    • afterInvocationManager

      protected org.springframework.security.access.intercept.AfterInvocationManager afterInvocationManager()
      Aggregate the after-invocation providers from AFTER_INVOCATION_PROVIDER_BEAN_NAMES into an AfterInvocationProviderManager. This preserves the post-invocation filtering chain (ACL_READ filters, value-object filters, @PostAuthorize / @PostFilter advice) that the XML <s:after-invocation-provider> elements used to wire.

      AfterInvocationProviderManager is deprecated in Spring 6 but still functional; this is the documented bridge for code that hasn't yet migrated to the AuthorizationManager model. Removing the bridge would require rewriting all 14 providers, which is out of scope.

      Overrides:
      afterInvocationManager in class org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration
    • runAsManager

      protected org.springframework.security.access.intercept.RunAsManager runAsManager()
      Return the existing runAsManager bean (RunAsManagerImpl with the GROUP_ role prefix and the configured run-as key). This is what @Secured("RUN_AS_ADMIN") needs to elevate authentications for signup / user-management flows.
      Overrides:
      runAsManager in class org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration