3

So I have a SpringBoot application with JavaConfig using spring-security-acl (version 3.2.7.RELEASE) following this tutorial http://krams915.blogspot.com/2011/01/spring-security-3-full-acl-tutorial.html except I'm also using OAuth. The problem is that the AclPermissionEvaluator is, at runtime, always replaced by DenyAllPermissionEvaluator.

TLDR; how do I get spring-security-acl to work with OAuth2Authentication? each requires @EnableGlobalMethodSecurity but in different locations (acl on the GlobalMethodSecurityConfiguration sub-class, and OAuth2 on the Application). Yet you can't have it defined twice. Similar to this but with OAuth2: How to configure Spring ACL without XML file )

pom.xml

<project>
<parent>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-parent</artifactId>
    <!-- <version>Brixton.M4</version> -->
    <version>Angel.SR3</version>
</parent>
<dependencies>
<!-- Spring-boot and cloud -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-core</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-config</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-acl</artifactId>
    <!-- <version>4.1.0.BUILD-SNAPSHOT</version>  -->   
    <!-- this is intentional to support non-numeric ID's e.g. UUIDs-->
</dependency>
<dependency>
        <groupId>org.postgresql</groupId>
        <artifactId>postgresql</artifactId>
        <scope>runtime</scope>
        <version>${postgres.version}</version>
    </dependency>

</dependencies>
</project>

SpringBoot application

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients(basePackages = "com.mycompany.mypackage.clients")
@EnableOAuth2Resource
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class Application {

    public static void main(String[] args) throws Exception {
        SpringApplication.run(Application.class, args);
    }
}

Java Config

@Configuration
@Slf4j
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class AclSecurityConfig extends GlobalMethodSecurityConfiguration {

    @Value("${spring.datasource.url}")
    private String jdbcUrl;
    @Value("${spring.datasource.username}")
    private String username;
    @Value("${spring.datasource.password}")
    private String password;
    @Value("${spring.datasource.driver-class-name}")
    private String driverClass;

    @Autowired
    private ConversionService defaultConversionService;

    @Override
    protected MethodSecurityExpressionHandler createExpressionHandler() {
        log.info("CreateExpressionHandler() got called");

        DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
        try {
            expressionHandler.setPermissionEvaluator(new AclPermissionEvaluator(aclService()));
        } catch (PropertyVetoException e) {
            throw new RuntimeException("Error creating expressionHandler", e);
        }
        return expressionHandler;
    }

    @Bean
    public MutableAclService aclService() throws PropertyVetoException{
        final JdbcMutableAclService mutableAclService = new JdbcMutableAclService(dataSource(), lookupStrategy(), aclCache());
        mutableAclService.setClassIdentityQuery("select currval(pg_get_serial_sequence('acl_class', 'id'))");
        mutableAclService.setSidIdentityQuery("select currval(pg_get_serial_sequence('acl_sid', 'id'))");
        return mutableAclService;
    }

    public DataSource dataSource() throws PropertyVetoException {
        final ComboPooledDataSource cpds = new ComboPooledDataSource(true);
        cpds.setJdbcUrl(jdbcUrl);
        cpds.setUser(username);
        cpds.setPassword(password);
        cpds.setDriverClass(driverClass);
        return cpds;
    }

    @Bean
    public BasicLookupStrategy lookupStrategy() throws PropertyVetoException {
        return new BasicLookupStrategy(dataSource(), aclCache(), aclAuthorizationStrategy(), grantingStrategy(), this.defaultConversionService);
    }

    @Bean
    public AclCache aclCache() {
        return new EhCacheBasedAclCache(ehCacheManagerFactoryBean().getObject().getCache("aclCache"), grantingStrategy(), aclAuthorizationStrategy());
    }

    @Bean
    public EhCacheManagerFactoryBean ehCacheManagerFactoryBean() {
        EhCacheManagerFactoryBean cacheManagerFactoryBean = new EhCacheManagerFactoryBean();

        cacheManagerFactoryBean.setConfigLocation(new ClassPathResource("ehcache.xml"));
        cacheManagerFactoryBean.setShared(true);

        return cacheManagerFactoryBean;
    }

    @Bean
    public AclAuthorizationStrategy aclAuthorizationStrategy() {
        return new AclAuthorizationStrategyImpl(
                new SimpleGrantedAuthority("ROLE_ADMIN"),
                new SimpleGrantedAuthority("ROLE_ADMIN"),
                new SimpleGrantedAuthority("ROLE_ADMIN"));
    }

    @Bean
    public PermissionGrantingStrategy grantingStrategy(){
        return new DefaultPermissionGrantingStrategy(auditLogger());
    }

    @Bean
    public AuditLogger auditLogger(){
        return new ConsoleAuditLogger();
    }   
}

Controller

    @RepositoryRestController
    @ResponseBody
    public class SampleRepositoryController {

    @Autowired
    private MyRepository myRepository;

        @PreAuthorize("hasRole('ROLE_USER') and hasPermission(#id, 'com.mycompany.mypackage.services.MyPojo', 'read')")
        @RequestMapping(value = "/path/{id}/resource", method = RequestMethod.GET)
        public MyObject file(@PathVariable("id") UUID id) {
            return myRepository.findOne(id);
        }

When I debug/step-thru DefaultMethodSecurityExpressionHandler at runtime I can tell that the permission evaluator is always the DenyAllPermissionEvaluator (and not the AclPermissionEvaluator ) as a new MethodSecurityEvaluationContext gets created.

Note that I do not have nor need the @EnableWebSecurity.

Also, I strongly suspect it's because I have @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true).

defined twice but when I take it off Application.java and run the application (mvn spring-boot:run) it fails to start up with the following error:

2016-02-22 16:04:48.747  INFO 26533 --- [lication.main()] o.apache.catalina.core.StandardService   : Stopping service Tomcat
2016-02-22 16:04:48.763  WARN 26533 --- [lication.main()] o.s.boot.SpringApplication               : Error handling failed (Error creating bean with name 'delegatingApplicationListener' defined in class path resource [org/springframework/security/config/annotation/web/configuration/WebSecurityConfiguration.class]: BeanPostProcessor before instantiation of bean failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'aclSecurityConfig': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire method: public void org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration.setApplicationContext(org.springframework.context.ApplicationContext); nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.cache.config.internalCacheAdvisor' defined in class path resource [org/springframework/cache/annotation/ProxyCachingConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.cache.interceptor.BeanFactoryCacheOperationSourceAdvisor]: Factory method 'cacheAdvisor' threw exception; nested exception is java.lang.NullPointerException)
2016-02-22 16:04:48.764 ERROR 26533 --- [lication.main()] o.s.boot.SpringApplication               : Application startup failed

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'defaultServletHandlerMapping' defined in class path resource [org/springframework/boot/autoconfigure/web/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.web.servlet.HandlerMapping]: Factory method 'defaultServletHandlerMapping' threw exception; nested exception is java.lang.IllegalArgumentException: A ServletContext is required to configure default servlet handling
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:599)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1119)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1014)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:504)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:476)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:303)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:299)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:755)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:757)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:480)
    at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:118)
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:686)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:320)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:957)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:946)
    at com.advaita.cloud.services.samples.Application.main(Application.java:20)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at org.springframework.boot.maven.RunMojo$LaunchRunner.run(RunMojo.java:418)
    at java.lang.Thread.run(Thread.java:745)
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.web.servlet.HandlerMapping]: Factory method 'defaultServletHandlerMapping' threw exception; nested exception is java.lang.IllegalArgumentException: A ServletContext is required to configure default servlet handling
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:189)
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:588)
    ... 23 common frames omitted
Caused by: java.lang.IllegalArgumentException: A ServletContext is required to configure default servlet handling
    at org.springframework.util.Assert.notNull(Assert.java:112)

Does anyone know how to get the 2 to play well together help?

0 Answers0