Search code examples
springspring-securityspring-el

Spring security different roles DTAP


We currently have different roles in our environments, for example in development we have roles called USR and ADM, while in production they use full names for example USER, ADMIN and ADMINISTRATOR.

My idea to resolve this problem is to use a property file and a placeholder for the rolenames, for example, this is my properties file:

role.user='USER'
role.admin='ADMIN', 'ADMINISTRATOR'

In my AppConfig I added the following annotation on top of the class:

@PropertySource("classpath:roles.properties")
public class AppConfig {

}

And in my service I'm now using:

@PreAuthorize("hasAnyRole(${role.admin})")
public Item deleteItem(int id) {

}

However, this results in the following exception:

Caused by: org.springframework.expression.spel.SpelParseException: EL1043E:(pos 12): Unexpected token.  Expected 'rparen())' but was 'lcurly({)'

Because it says it's not expexting the curly brace, I also tried the following: @PreAuthorize("hasAnyRole(role.admin)") which results in:

Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1008E:(pos 11): Property or field 'role' cannot be found on object of type 'org.springframework.security.access.expression.method.MethodSecurityExpressionRoot' - maybe not public?

At least the expression itself looks valid now, but it seems it's not looking at the properties file no, but at a property of a specific class.

Does anyone have an idea to solve this? Or is there another/better solution of resolving environment-specific roles?


Solution

  • The Spring EL used in @PreAuthorize does only have:

    • Access to methods and properties of SecurityExpressionRoot.

    • Access method arguments (requires compilation with debug info or custom ParameterNameDiscoverer):

    See this answer https://stackoverflow.com/a/3786581/883859

    You can get access to other beans via @ so

     public interface RoleNameGetter {
        String getSuperUserRole();
        ...
     }
    

    And

     @Configuration
     @PropertySource("classpath:xyz.properties")
     @EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true)
     public class Configuration {
        @Autowired
        protected Environment env;
    
        @Bean
        RoleNameGetter roleNameGetter() {
          return new RoleNameGetter() {
            @Override
            public String getSuperUserRole() {
                return env.getProperty("superuser_role_name");
            }
        };
    }
    

    Allows for access to role names from the property file in the Spring EL used in @PreAuthorize, like this:

      @PreAuthorize("hasAnyAuthority(@roleNameGetter.getSuperUserRole(),@roleNameGetter.getNormalUserRole())")
      public void end() {...}