SEC-1953: Spring Security Java Config support
This is the initial migration of Spring Security Java Config from the external project at https://github.com/SpringSource/spring-security-javaconfig
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation;
|
||||
|
||||
/**
|
||||
* Exists for mocking purposes to ensure that the Type information is found.
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
public interface AnyObjectPostProcessor extends ObjectPostProcessor<Object> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation;
|
||||
|
||||
import javax.servlet.Filter
|
||||
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException
|
||||
import org.springframework.context.ConfigurableApplicationContext
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext
|
||||
import org.springframework.mock.web.MockFilterChain
|
||||
import org.springframework.mock.web.MockHttpServletRequest
|
||||
import org.springframework.mock.web.MockHttpServletResponse
|
||||
import org.springframework.security.authentication.AuthenticationManager
|
||||
import org.springframework.security.authentication.AuthenticationProvider
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
|
||||
import org.springframework.security.core.Authentication
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.core.context.SecurityContextHolder
|
||||
import org.springframework.security.core.context.SecurityContextImpl
|
||||
import org.springframework.security.web.FilterChainProxy
|
||||
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor
|
||||
import org.springframework.security.web.context.HttpRequestResponseHolder
|
||||
import org.springframework.security.web.context.HttpSessionSecurityContextRepository
|
||||
|
||||
import spock.lang.AutoCleanup
|
||||
import spock.lang.Specification
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
abstract class BaseSpringSpec extends Specification {
|
||||
@AutoCleanup
|
||||
ConfigurableApplicationContext context
|
||||
|
||||
MockHttpServletRequest request
|
||||
MockHttpServletResponse response
|
||||
MockFilterChain chain
|
||||
|
||||
def setup() {
|
||||
request = new MockHttpServletRequest(method:"GET")
|
||||
response = new MockHttpServletResponse()
|
||||
chain = new MockFilterChain()
|
||||
}
|
||||
|
||||
AuthenticationManagerBuilder authenticationBldr = new AuthenticationManagerBuilder().inMemoryAuthentication().and()
|
||||
|
||||
def cleanup() {
|
||||
SecurityContextHolder.clearContext()
|
||||
}
|
||||
|
||||
def loadConfig(Class<?>... configs) {
|
||||
context = new AnnotationConfigApplicationContext(configs)
|
||||
context
|
||||
}
|
||||
|
||||
def findFilter(Class<?> filter, int index = 0) {
|
||||
filterChain(index).filters.find { filter.isAssignableFrom(it.class)}
|
||||
}
|
||||
|
||||
def filterChain(int index=0) {
|
||||
filterChains()[index]
|
||||
}
|
||||
|
||||
def filterChains() {
|
||||
context.getBean(FilterChainProxy).filterChains
|
||||
}
|
||||
|
||||
Filter getSpringSecurityFilterChain() {
|
||||
context.getBean("springSecurityFilterChain",Filter.class)
|
||||
}
|
||||
|
||||
AuthenticationManager authenticationManager() {
|
||||
context.getBean(AuthenticationManager)
|
||||
}
|
||||
|
||||
AuthenticationManager getAuthenticationManager() {
|
||||
try {
|
||||
authenticationManager().delegateBuilder.getObject()
|
||||
} catch(NoSuchBeanDefinitionException e) {}
|
||||
findFilter(FilterSecurityInterceptor).authenticationManager
|
||||
}
|
||||
|
||||
List<AuthenticationProvider> authenticationProviders() {
|
||||
List<AuthenticationProvider> providers = new ArrayList<AuthenticationProvider>()
|
||||
AuthenticationManager authenticationManager = getAuthenticationManager()
|
||||
while(authenticationManager?.providers) {
|
||||
providers.addAll(authenticationManager.providers)
|
||||
authenticationManager = authenticationManager.parent
|
||||
}
|
||||
providers
|
||||
}
|
||||
|
||||
AuthenticationProvider findAuthenticationProvider(Class<?> provider) {
|
||||
authenticationProviders().find { provider.isAssignableFrom(it.class) }
|
||||
}
|
||||
|
||||
def login(String username="user", String role="ROLE_USER") {
|
||||
login(new UsernamePasswordAuthenticationToken(username, null, AuthorityUtils.createAuthorityList(role)))
|
||||
}
|
||||
|
||||
def login(Authentication auth) {
|
||||
HttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository()
|
||||
HttpRequestResponseHolder requestResponseHolder = new HttpRequestResponseHolder(request, response)
|
||||
repo.loadContext(requestResponseHolder)
|
||||
repo.saveContext(new SecurityContextImpl(authentication:auth), requestResponseHolder.request, requestResponseHolder.response)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation;
|
||||
|
||||
import org.springframework.context.ConfigurableApplicationContext
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.mock.web.MockFilterChain;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.security.core.context.SecurityContextHolder
|
||||
import org.springframework.security.web.FilterChainProxy;
|
||||
|
||||
import spock.lang.AutoCleanup
|
||||
import spock.lang.Specification
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
abstract class BaseWebSpecuritySpec extends BaseSpringSpec {
|
||||
FilterChainProxy springSecurityFilterChain
|
||||
MockHttpServletRequest request
|
||||
MockHttpServletResponse response
|
||||
MockFilterChain chain
|
||||
|
||||
def setup() {
|
||||
request = new MockHttpServletRequest()
|
||||
response = new MockHttpServletResponse()
|
||||
chain = new MockFilterChain()
|
||||
}
|
||||
|
||||
|
||||
def loadConfig(Class<?>... configs) {
|
||||
super.loadConfig(configs)
|
||||
springSecurityFilterChain = context.getBean(FilterChainProxy)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
class ConcereteSecurityConfigurerAdapter extends SecurityConfigurerAdapter<Object, SecurityBuilder<Object>> {
|
||||
private List<Object> list = new ArrayList<Object>();
|
||||
|
||||
@Override
|
||||
public void configure(SecurityBuilder<Object> builder) throws Exception {
|
||||
list = postProcess(list);
|
||||
}
|
||||
|
||||
public ConcereteSecurityConfigurerAdapter list(List<Object> l) {
|
||||
this.list = l;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation;
|
||||
|
||||
import static org.fest.assertions.Assertions.assertThat;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
public class ObjectPostProcessorTests {
|
||||
|
||||
@Test
|
||||
public void convertTypes() {
|
||||
assertThat((Object)PerformConversion.perform(new ArrayList<Object>())).isInstanceOf(LinkedList.class);
|
||||
}
|
||||
}
|
||||
|
||||
class ListToLinkedListObjectPostProcessor implements ObjectPostProcessor<List<?>>{
|
||||
|
||||
@Override
|
||||
public <O extends List<?>> O postProcess(O l) {
|
||||
return (O) new LinkedList(l);
|
||||
}
|
||||
}
|
||||
|
||||
class PerformConversion {
|
||||
public static List<?> perform(ArrayList<?> l) {
|
||||
return new ListToLinkedListObjectPostProcessor().postProcess(l);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation
|
||||
|
||||
import spock.lang.Specification
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
class SecurityConfigurerAdapterTests extends Specification {
|
||||
ConcereteSecurityConfigurerAdapter conf = new ConcereteSecurityConfigurerAdapter()
|
||||
|
||||
def "addPostProcessor closure"() {
|
||||
setup:
|
||||
SecurityBuilder<Object> builder = Mock()
|
||||
conf.addObjectPostProcessor({ List l ->
|
||||
l.add("a")
|
||||
l
|
||||
} as ObjectPostProcessor<List>)
|
||||
when:
|
||||
conf.init(builder)
|
||||
conf.configure(builder)
|
||||
then:
|
||||
conf.list.contains("a")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.authentication
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.authentication.AuthenticationEventPublisher
|
||||
import org.springframework.security.authentication.AuthenticationManager
|
||||
import org.springframework.security.authentication.AuthenticationProvider
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.ObjectPostProcessor
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.core.Authentication;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
class AuthenticationManagerBuilderTests extends BaseSpringSpec {
|
||||
def "add(AuthenticationProvider) does not perform registration"() {
|
||||
setup:
|
||||
ObjectPostProcessor opp = Mock()
|
||||
AuthenticationProvider provider = Mock()
|
||||
AuthenticationManagerBuilder builder = new AuthenticationManagerBuilder().objectPostProcessor(opp)
|
||||
when: "Adding an AuthenticationProvider"
|
||||
builder.authenticationProvider(provider)
|
||||
builder.build()
|
||||
then: "AuthenticationProvider is not passed into LifecycleManager (it should be managed externally)"
|
||||
0 * opp._(_ as AuthenticationProvider)
|
||||
}
|
||||
|
||||
// https://github.com/SpringSource/spring-security-javaconfig/issues/132
|
||||
def "#132 Custom AuthenticationEventPublisher with Web registerAuthentication"() {
|
||||
setup:
|
||||
AuthenticationEventPublisher aep = Mock()
|
||||
when:
|
||||
AuthenticationManager am = new AuthenticationManagerBuilder()
|
||||
.authenticationEventPublisher(aep)
|
||||
.inMemoryAuthentication()
|
||||
.and()
|
||||
.build()
|
||||
then:
|
||||
am.eventPublisher == aep
|
||||
}
|
||||
|
||||
def "authentication-manager support multiple DaoAuthenticationProvider's"() {
|
||||
setup:
|
||||
loadConfig(MultiAuthenticationProvidersConfig)
|
||||
when:
|
||||
Authentication auth = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("user","password"))
|
||||
then:
|
||||
auth.name == "user"
|
||||
auth.authorities*.authority == ['ROLE_USER']
|
||||
when:
|
||||
auth = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("admin","password"))
|
||||
then:
|
||||
auth.name == "admin"
|
||||
auth.authorities*.authority.sort() == ['ROLE_ADMIN','ROLE_USER']
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class MultiAuthenticationProvidersConfig extends WebSecurityConfigurerAdapter {
|
||||
protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.inMemoryAuthentication()
|
||||
.withUser("user").password("password").roles("USER").and()
|
||||
.and()
|
||||
.inMemoryAuthentication()
|
||||
.withUser("admin").password("password").roles("USER","ADMIN")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.authentication
|
||||
|
||||
import java.rmi.registry.Registry;
|
||||
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.security.authentication.AuthenticationManager
|
||||
import org.springframework.security.config.annotation.authentication.AuthenticationManagerBuilder
|
||||
import org.springframework.security.config.annotation.authentication.configurers.userdetails.UserDetailsServiceConfigurer;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.authentication.configurers.provisioning.InMemoryUserDetailsManagerConfigurer;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
@Configuration
|
||||
class BaseAuthenticationConfig {
|
||||
protected void registerAuthentication(
|
||||
AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.inMemoryAuthentication()
|
||||
.withUser("user").password("password").roles("USER").and()
|
||||
.withUser("admin").password("password").roles("USER", "ADMIN").and()
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AuthenticationManager authenticationManager() {
|
||||
AuthenticationManagerBuilder registry = new AuthenticationManagerBuilder();
|
||||
registerAuthentication(registry);
|
||||
return registry.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.authentication
|
||||
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.security.authentication.AuthenticationManager
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.core.Authentication
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
class NamespaceAuthenticationManagerTests extends BaseSpringSpec {
|
||||
def "authentication-manager@erase-credentials=true (default)"() {
|
||||
when:
|
||||
loadConfig(EraseCredentialsTrueDefaultConfig)
|
||||
Authentication auth = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("user","password"))
|
||||
then:
|
||||
auth.principal.password == null
|
||||
auth.credentials == null
|
||||
when: "authenticate the same user"
|
||||
auth = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("user","password"))
|
||||
then: "successfully authenticate again"
|
||||
noExceptionThrown()
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class EraseCredentialsTrueDefaultConfig extends WebSecurityConfigurerAdapter {
|
||||
protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.inMemoryAuthentication()
|
||||
.withUser("user").password("password").roles("USER")
|
||||
}
|
||||
|
||||
// Only necessary to have access to verify the AuthenticationManager
|
||||
@Bean
|
||||
@Override
|
||||
public AuthenticationManager authenticationManagerBean()
|
||||
throws Exception {
|
||||
return super.authenticationManagerBean();
|
||||
}
|
||||
}
|
||||
|
||||
def "authentication-manager@erase-credentials=false"() {
|
||||
when:
|
||||
loadConfig(EraseCredentialsFalseConfig)
|
||||
Authentication auth = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("user","password"))
|
||||
then:
|
||||
auth.credentials == "password"
|
||||
auth.principal.password == "password"
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class EraseCredentialsFalseConfig extends WebSecurityConfigurerAdapter {
|
||||
protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.eraseCredentials(false)
|
||||
.inMemoryAuthentication()
|
||||
.withUser("user").password("password").roles("USER")
|
||||
}
|
||||
|
||||
// Only necessary to have access to verify the AuthenticationManager
|
||||
@Bean
|
||||
@Override
|
||||
public AuthenticationManager authenticationManagerBean()
|
||||
throws Exception {
|
||||
return super.authenticationManagerBean();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.authentication
|
||||
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.security.authentication.AuthenticationManager
|
||||
import org.springframework.security.authentication.dao.DaoAuthenticationProvider
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.provisioning.InMemoryUserDetailsManager
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
class NamespaceAuthenticationProviderTests extends BaseSpringSpec {
|
||||
def "authentication-provider@ref"() {
|
||||
when:
|
||||
loadConfig(AuthenticationProviderRefConfig)
|
||||
then:
|
||||
authenticationProviders()[1] == AuthenticationProviderRefConfig.expected
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class AuthenticationProviderRefConfig extends WebSecurityConfigurerAdapter {
|
||||
static DaoAuthenticationProvider expected = new DaoAuthenticationProvider()
|
||||
protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.authenticationProvider(expected)
|
||||
}
|
||||
|
||||
// Only necessary to have access to verify the AuthenticationManager
|
||||
@Bean
|
||||
@Override
|
||||
public AuthenticationManager authenticationManagerBean()
|
||||
throws Exception {
|
||||
return super.authenticationManagerBean();
|
||||
}
|
||||
}
|
||||
|
||||
def "authentication-provider@user-service-ref"() {
|
||||
when:
|
||||
loadConfig(UserServiceRefConfig)
|
||||
then:
|
||||
findAuthenticationProvider(DaoAuthenticationProvider).userDetailsService == UserServiceRefConfig.expected
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class UserServiceRefConfig extends WebSecurityConfigurerAdapter {
|
||||
static InMemoryUserDetailsManager expected = new InMemoryUserDetailsManager([] as Collection)
|
||||
protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.userDetailsService(expected)
|
||||
}
|
||||
|
||||
// Only necessary to have access to verify the AuthenticationManager
|
||||
@Bean
|
||||
@Override
|
||||
public AuthenticationManager authenticationManagerBean()
|
||||
throws Exception {
|
||||
return super.authenticationManagerBean();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.authentication
|
||||
|
||||
import javax.sql.DataSource
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder
|
||||
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType
|
||||
import org.springframework.security.authentication.AuthenticationManager
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
|
||||
import org.springframework.security.authentication.dao.DaoAuthenticationProvider
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.authentication.NamespaceJdbcUserServiceTests.CustomJdbcUserServiceSampleConfig.CustomUserCache;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.core.Authentication
|
||||
import org.springframework.security.core.userdetails.UserCache
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.provisioning.JdbcUserDetailsManager
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
class NamespaceJdbcUserServiceTests extends BaseSpringSpec {
|
||||
def "jdbc-user-service"() {
|
||||
when:
|
||||
loadConfig(DataSourceConfig,JdbcUserServiceConfig)
|
||||
then:
|
||||
findAuthenticationProvider(DaoAuthenticationProvider).userDetailsService instanceof JdbcUserDetailsManager
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class JdbcUserServiceConfig extends WebSecurityConfigurerAdapter {
|
||||
@Autowired
|
||||
private DataSource dataSource;
|
||||
|
||||
protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.jdbcAuthentication()
|
||||
.dataSource(dataSource) // jdbc-user-service@data-source-ref
|
||||
}
|
||||
|
||||
// Only necessary to have access to verify the AuthenticationManager
|
||||
@Bean
|
||||
@Override
|
||||
public AuthenticationManager authenticationManagerBean()
|
||||
throws Exception {
|
||||
return super.authenticationManagerBean();
|
||||
}
|
||||
}
|
||||
|
||||
def "jdbc-user-service in memory testing sample"() {
|
||||
when:
|
||||
loadConfig(DataSourceConfig,JdbcUserServiceInMemorySampleConfig)
|
||||
then:
|
||||
Authentication auth = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("user", "password"))
|
||||
auth.authorities.collect {it.authority} == ['ROLE_USER']
|
||||
auth.name == "user"
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class JdbcUserServiceInMemorySampleConfig extends WebSecurityConfigurerAdapter {
|
||||
@Autowired
|
||||
private DataSource dataSource;
|
||||
|
||||
protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.jdbcAuthentication()
|
||||
.dataSource(dataSource)
|
||||
// imports the default schema (will fail if already exists)
|
||||
.withDefaultSchema()
|
||||
// adds this user automatically (will fail if already exists)
|
||||
.withUser("user")
|
||||
.password("password")
|
||||
.roles("USER")
|
||||
}
|
||||
|
||||
// Only necessary to have access to verify the AuthenticationManager
|
||||
@Bean
|
||||
@Override
|
||||
public AuthenticationManager authenticationManagerBean()
|
||||
throws Exception {
|
||||
return super.authenticationManagerBean();
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class DataSourceConfig {
|
||||
@Bean
|
||||
public DataSource dataSource() {
|
||||
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder()
|
||||
return builder.setType(EmbeddedDatabaseType.HSQL).build();
|
||||
}
|
||||
}
|
||||
|
||||
def "jdbc-user-service custom"() {
|
||||
when:
|
||||
loadConfig(CustomDataSourceConfig,CustomJdbcUserServiceSampleConfig)
|
||||
then:
|
||||
findAuthenticationProvider(DaoAuthenticationProvider).userDetailsService.userCache instanceof CustomUserCache
|
||||
when:
|
||||
Authentication auth = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("user", "password"))
|
||||
then:
|
||||
auth.authorities.collect {it.authority}.sort() == ['ROLE_DBA','ROLE_USER']
|
||||
auth.name == 'user'
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class CustomJdbcUserServiceSampleConfig extends WebSecurityConfigurerAdapter {
|
||||
@Autowired
|
||||
private DataSource dataSource;
|
||||
|
||||
protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.jdbcAuthentication()
|
||||
// jdbc-user-service@dataSource
|
||||
.dataSource(dataSource)
|
||||
// jdbc-user-service@cache-ref
|
||||
.userCache(new CustomUserCache())
|
||||
// jdbc-user-service@users-byusername-query
|
||||
.usersByUsernameQuery("select principal,credentials,true from users where principal = ?")
|
||||
// jdbc-user-service@authorities-by-username-query
|
||||
.authoritiesByUsernameQuery("select principal,role from roles where principal = ?")
|
||||
// jdbc-user-service@group-authorities-by-username-query
|
||||
.groupAuthoritiesByUsername(JdbcUserDetailsManager.DEF_GROUP_AUTHORITIES_BY_USERNAME_QUERY)
|
||||
// jdbc-user-service@role-prefix
|
||||
.rolePrefix("ROLE_")
|
||||
|
||||
}
|
||||
|
||||
// Only necessary to have access to verify the AuthenticationManager
|
||||
@Bean
|
||||
@Override
|
||||
public AuthenticationManager authenticationManagerBean()
|
||||
throws Exception {
|
||||
return super.authenticationManagerBean();
|
||||
}
|
||||
|
||||
static class CustomUserCache implements UserCache {
|
||||
|
||||
@Override
|
||||
public UserDetails getUserFromCache(String username) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putUserInCache(UserDetails user) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUserFromCache(String username) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class CustomDataSourceConfig {
|
||||
@Bean
|
||||
public DataSource dataSource() {
|
||||
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder()
|
||||
// simulate that the DB already has the schema loaded and users in it
|
||||
.addScript("CustomJdbcUserServiceSampleConfig.sql")
|
||||
return builder.setType(EmbeddedDatabaseType.HSQL).build();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.authentication
|
||||
|
||||
import static org.springframework.security.config.annotation.authentication.PasswordEncoderConfigurerConfigs.*
|
||||
|
||||
import javax.sql.DataSource
|
||||
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder
|
||||
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType
|
||||
import org.springframework.ldap.core.support.BaseLdapPathContextSource;
|
||||
import org.springframework.security.authentication.AuthenticationManager
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.authentication.configurers.ldap.LdapAuthenticationProviderConfigurer;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.core.authority.AuthorityUtils
|
||||
import org.springframework.security.core.userdetails.User
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
|
||||
import org.springframework.security.ldap.authentication.LdapAuthenticationProvider;
|
||||
import org.springframework.security.provisioning.InMemoryUserDetailsManager
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
class NamespacePasswordEncoderTests extends BaseSpringSpec {
|
||||
def "password-encoder@ref with in memory"() {
|
||||
when:
|
||||
loadConfig(PasswordEncoderWithInMemoryConfig)
|
||||
then:
|
||||
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("user", "password"))
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class PasswordEncoderWithInMemoryConfig extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void registerAuthentication(AuthenticationManagerBuilder auth)
|
||||
throws Exception {
|
||||
|
||||
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder()
|
||||
auth
|
||||
.inMemoryAuthentication()
|
||||
.withUser("user").password(encoder.encode("password")).roles("USER").and()
|
||||
.passwordEncoder(encoder)
|
||||
}
|
||||
}
|
||||
|
||||
def "password-encoder@ref with jdbc"() {
|
||||
when:
|
||||
loadConfig(PasswordEncoderWithJdbcConfig)
|
||||
then:
|
||||
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("user", "password"))
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class PasswordEncoderWithJdbcConfig extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void registerAuthentication(AuthenticationManagerBuilder auth)
|
||||
throws Exception {
|
||||
|
||||
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder()
|
||||
auth
|
||||
.jdbcAuthentication()
|
||||
.withDefaultSchema()
|
||||
.dataSource(dataSource())
|
||||
.withUser("user").password(encoder.encode("password")).roles("USER").and()
|
||||
.passwordEncoder(encoder)
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DataSource dataSource() {
|
||||
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder()
|
||||
return builder.setType(EmbeddedDatabaseType.HSQL).build();
|
||||
}
|
||||
}
|
||||
|
||||
def "password-encoder@ref with userdetailsservice"() {
|
||||
when:
|
||||
loadConfig(PasswordEncoderWithUserDetailsServiceConfig)
|
||||
then:
|
||||
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("user", "password"))
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class PasswordEncoderWithUserDetailsServiceConfig extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void registerAuthentication(AuthenticationManagerBuilder auth)
|
||||
throws Exception {
|
||||
|
||||
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder()
|
||||
User user = new User("user",encoder.encode("password"), AuthorityUtils.createAuthorityList("ROLE_USER"))
|
||||
InMemoryUserDetailsManager uds = new InMemoryUserDetailsManager([user])
|
||||
auth
|
||||
.userDetailsService(uds)
|
||||
.passwordEncoder(encoder)
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DataSource dataSource() {
|
||||
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder()
|
||||
return builder.setType(EmbeddedDatabaseType.HSQL).build();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.authentication;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
|
||||
/**
|
||||
* Class Containing the {@link Configuration} for
|
||||
* {@link PasswordEncoderConfigurerTests}. Separate to ensure the configuration
|
||||
* compiles in Java (i.e. we are not using hidden methods).
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
*/
|
||||
public class PasswordEncoderConfigurerConfigs {
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class PasswordEncoderConfig extends WebSecurityConfigurerAdapter {
|
||||
protected void registerAuthentication(
|
||||
AuthenticationManagerBuilder auth) throws Exception {
|
||||
BCryptPasswordEncoder encoder = passwordEncoder();
|
||||
auth
|
||||
.inMemoryAuthentication()
|
||||
.withUser("user").password(encoder.encode("password")).roles("USER").and()
|
||||
.passwordEncoder(encoder);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Override
|
||||
public AuthenticationManager authenticationManagerBean()
|
||||
throws Exception {
|
||||
return super.authenticationManagerBean();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public BCryptPasswordEncoder passwordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class PasswordEncoderNoAuthManagerLoadsConfig extends WebSecurityConfigurerAdapter {
|
||||
protected void registerAuthentication(
|
||||
AuthenticationManagerBuilder auth) throws Exception {
|
||||
BCryptPasswordEncoder encoder = passwordEncoder();
|
||||
auth
|
||||
.inMemoryAuthentication()
|
||||
.withUser("user").password(encoder.encode("password")).roles("USER").and()
|
||||
.passwordEncoder(encoder);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public BCryptPasswordEncoder passwordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.authentication
|
||||
|
||||
import static org.springframework.security.config.annotation.authentication.PasswordEncoderConfigurerConfigs.*
|
||||
|
||||
import org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.ldap.core.ContextSource;
|
||||
import org.springframework.ldap.core.support.BaseLdapPathContextSource;
|
||||
import org.springframework.security.authentication.AuthenticationManager
|
||||
import org.springframework.security.authentication.AuthenticationProvider
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.SecurityBuilder;
|
||||
import org.springframework.security.config.annotation.authentication.AuthenticationManagerBuilder
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.web.FilterChainProxy;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
class PasswordEncoderConfigurerTests extends BaseSpringSpec {
|
||||
def "password-encoder@ref with No AuthenticationManager Bean"() {
|
||||
when:
|
||||
loadConfig(PasswordEncoderNoAuthManagerLoadsConfig)
|
||||
then:
|
||||
noExceptionThrown()
|
||||
}
|
||||
|
||||
def "password-encoder@ref with AuthenticationManagerBuilder"() {
|
||||
when:
|
||||
loadConfig(PasswordEncoderConfig)
|
||||
AuthenticationManager authMgr = authenticationManager()
|
||||
then:
|
||||
authMgr.authenticate(new UsernamePasswordAuthenticationToken("user", "password"))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.configuration
|
||||
|
||||
import javax.servlet.ServletConfig
|
||||
import javax.servlet.ServletContext
|
||||
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware
|
||||
import org.springframework.beans.factory.BeanFactoryAware
|
||||
import org.springframework.beans.factory.BeanNameAware
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
|
||||
import org.springframework.context.ApplicationContextAware
|
||||
import org.springframework.context.ApplicationEventPublisherAware
|
||||
import org.springframework.context.EnvironmentAware
|
||||
import org.springframework.context.MessageSourceAware
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.mock.web.MockServletConfig
|
||||
import org.springframework.mock.web.MockServletContext
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
||||
import org.springframework.security.config.annotation.configuration.AutowireBeanFactoryObjectPostProcessor;
|
||||
import org.springframework.web.context.ServletConfigAware
|
||||
import org.springframework.web.context.ServletContextAware
|
||||
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
class AutowireBeanFactoryObjectPostProcessorTests extends BaseSpringSpec {
|
||||
|
||||
def "Verify All Aware methods are invoked"() {
|
||||
setup:
|
||||
ApplicationContextAware contextAware = Mock(ApplicationContextAware)
|
||||
ApplicationEventPublisherAware publisher = Mock(ApplicationEventPublisherAware)
|
||||
BeanClassLoaderAware classloader = Mock(BeanClassLoaderAware)
|
||||
BeanFactoryAware beanFactory = Mock(BeanFactoryAware)
|
||||
EnvironmentAware environment = Mock(EnvironmentAware)
|
||||
MessageSourceAware messageSource = Mock(MessageSourceAware)
|
||||
ServletConfigAware servletConfig = Mock(ServletConfigAware)
|
||||
ServletContextAware servletContext = Mock(ServletContextAware)
|
||||
DisposableBean disposable = Mock(DisposableBean)
|
||||
|
||||
context = new AnnotationConfigWebApplicationContext([servletConfig:new MockServletConfig(),servletContext:new MockServletContext()])
|
||||
context.register(Config)
|
||||
context.refresh()
|
||||
context.start()
|
||||
|
||||
ObjectPostProcessor opp = context.getBean(ObjectPostProcessor)
|
||||
when:
|
||||
opp.postProcess(contextAware)
|
||||
then:
|
||||
1 * contextAware.setApplicationContext(!null)
|
||||
|
||||
when:
|
||||
opp.postProcess(publisher)
|
||||
then:
|
||||
1 * publisher.setApplicationEventPublisher(!null)
|
||||
|
||||
when:
|
||||
opp.postProcess(classloader)
|
||||
then:
|
||||
1 * classloader.setBeanClassLoader(!null)
|
||||
|
||||
when:
|
||||
opp.postProcess(beanFactory)
|
||||
then:
|
||||
1 * beanFactory.setBeanFactory(!null)
|
||||
|
||||
when:
|
||||
opp.postProcess(environment)
|
||||
then:
|
||||
1 * environment.setEnvironment(!null)
|
||||
|
||||
when:
|
||||
opp.postProcess(messageSource)
|
||||
then:
|
||||
1 * messageSource.setMessageSource(!null)
|
||||
|
||||
when:
|
||||
opp.postProcess(servletConfig)
|
||||
then:
|
||||
1 * servletConfig.setServletConfig(!null)
|
||||
|
||||
when:
|
||||
opp.postProcess(servletContext)
|
||||
then:
|
||||
1 * servletContext.setServletContext(!null)
|
||||
|
||||
when:
|
||||
opp.postProcess(disposable)
|
||||
context.close()
|
||||
context = null
|
||||
then:
|
||||
1 * disposable.destroy()
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class Config {
|
||||
@Bean
|
||||
public ObjectPostProcessor objectPostProcessor(AutowireCapableBeanFactory beanFactory) {
|
||||
return new AutowireBeanFactoryObjectPostProcessor(beanFactory);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.method.configuration
|
||||
|
||||
import static org.fest.assertions.Assertions.assertThat
|
||||
import static org.junit.Assert.fail
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.springframework.context.ApplicationContext
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.access.AccessDecisionManager
|
||||
import org.springframework.security.access.AccessDeniedException
|
||||
import org.springframework.security.access.ConfigAttribute
|
||||
import org.springframework.security.authentication.AuthenticationManager
|
||||
import org.springframework.security.authentication.DefaultAuthenticationEventPublisher
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
|
||||
import org.springframework.security.authentication.event.AuthenticationSuccessEvent;
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||
import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration;
|
||||
import org.springframework.security.config.annotation.web.WebSecurityConfigurerAdapterTests.InMemoryAuthWithWebSecurityConfigurerAdapter
|
||||
import org.springframework.security.core.Authentication
|
||||
import org.springframework.security.core.authority.AuthorityUtils
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
public class GlobalMethodSecurityConfigurationTests extends BaseSpringSpec {
|
||||
def "messages set when using GlobalMethodSecurityConfiguration"() {
|
||||
when:
|
||||
loadConfig(InMemoryAuthWithGlobalMethodSecurityConfig)
|
||||
then:
|
||||
authenticationManager.messages.messageSource instanceof ApplicationContext
|
||||
}
|
||||
|
||||
def "AuthenticationEventPublisher is registered GlobalMethodSecurityConfiguration"() {
|
||||
when:
|
||||
loadConfig(InMemoryAuthWithGlobalMethodSecurityConfig)
|
||||
then:
|
||||
authenticationManager.eventPublisher instanceof DefaultAuthenticationEventPublisher
|
||||
when:
|
||||
Authentication auth = new UsernamePasswordAuthenticationToken("user",null,AuthorityUtils.createAuthorityList("ROLE_USER"))
|
||||
authenticationManager.eventPublisher.publishAuthenticationSuccess(auth)
|
||||
then:
|
||||
InMemoryAuthWithGlobalMethodSecurityConfig.EVENT.authentication == auth
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableGlobalMethodSecurity(prePostEnabled = true)
|
||||
public static class InMemoryAuthWithGlobalMethodSecurityConfig extends GlobalMethodSecurityConfiguration implements ApplicationListener<AuthenticationSuccessEvent> {
|
||||
static AuthenticationSuccessEvent EVENT
|
||||
|
||||
@Override
|
||||
protected void registerAuthentication(AuthenticationManagerBuilder auth)
|
||||
throws Exception {
|
||||
auth
|
||||
.inMemoryAuthentication()
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(AuthenticationSuccessEvent e) {
|
||||
EVENT = e
|
||||
}
|
||||
}
|
||||
|
||||
AuthenticationManager getAuthenticationManager() {
|
||||
context.getBean(MethodInterceptor).authenticationManager
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.method.configuration;
|
||||
|
||||
import javax.annotation.security.DenyAll
|
||||
|
||||
import org.springframework.security.access.annotation.Secured
|
||||
import org.springframework.security.access.prepost.PostAuthorize;
|
||||
import org.springframework.security.access.prepost.PreAuthorize
|
||||
import org.springframework.security.core.Authentication
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
public interface MethodSecurityService {
|
||||
@PreAuthorize("denyAll")
|
||||
public String preAuthorize();
|
||||
|
||||
@Secured("ROLE_ADMIN")
|
||||
public String secured();
|
||||
|
||||
@DenyAll
|
||||
public String jsr250();
|
||||
|
||||
@Secured(["ROLE_USER","RUN_AS_SUPER"])
|
||||
public Authentication runAs();
|
||||
|
||||
@PreAuthorize("permitAll")
|
||||
public String preAuthorizePermitAll();
|
||||
|
||||
@PreAuthorize("hasPermission(#object,'read')")
|
||||
public String hasPermission(String object);
|
||||
|
||||
@PostAuthorize("hasPermission(#object,'read')")
|
||||
public String postHasPermission(String object);
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.method.configuration;
|
||||
|
||||
import org.springframework.security.core.Authentication
|
||||
import org.springframework.security.core.context.SecurityContextHolder
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
public class MethodSecurityServiceImpl implements MethodSecurityService {
|
||||
|
||||
@Override
|
||||
public String preAuthorize() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String secured() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String jsr250() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Authentication runAs() {
|
||||
return SecurityContextHolder.getContext().getAuthentication();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String preAuthorizePermitAll() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String hasPermission(String object) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String postHasPermission(String object) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.method.configuration
|
||||
|
||||
import static org.fest.assertions.Assertions.assertThat
|
||||
import static org.junit.Assert.fail
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.springframework.context.ConfigurableApplicationContext
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.security.access.AccessDeniedException
|
||||
import org.springframework.security.access.PermissionEvaluator;
|
||||
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler
|
||||
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.method.configuration.NamespaceGlobalMethodSecurityTests.BaseMethodConfig;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||
import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
public class NamespaceGlobalMethodSecurityExpressionHandlerTests extends BaseSpringSpec {
|
||||
def setup() {
|
||||
SecurityContextHolder.getContext().setAuthentication(
|
||||
new TestingAuthenticationToken("user", "password","ROLE_USER"))
|
||||
}
|
||||
|
||||
def "global-method-security/expression-handler @PreAuthorize"() {
|
||||
setup:
|
||||
context = new AnnotationConfigApplicationContext(BaseMethodConfig,CustomAccessDecisionManagerConfig)
|
||||
MethodSecurityService service = context.getBean(MethodSecurityService)
|
||||
when:
|
||||
service.hasPermission("granted")
|
||||
then:
|
||||
noExceptionThrown()
|
||||
when:
|
||||
service.hasPermission("denied")
|
||||
then:
|
||||
thrown(AccessDeniedException)
|
||||
}
|
||||
|
||||
def "global-method-security/expression-handler @PostAuthorize"() {
|
||||
setup:
|
||||
context = new AnnotationConfigApplicationContext(BaseMethodConfig,CustomAccessDecisionManagerConfig)
|
||||
MethodSecurityService service = context.getBean(MethodSecurityService)
|
||||
when:
|
||||
service.postHasPermission("granted")
|
||||
then:
|
||||
noExceptionThrown()
|
||||
when:
|
||||
service.postHasPermission("denied")
|
||||
then:
|
||||
thrown(AccessDeniedException)
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableGlobalMethodSecurity(prePostEnabled = true)
|
||||
public static class CustomAccessDecisionManagerConfig extends GlobalMethodSecurityConfiguration {
|
||||
@Override
|
||||
protected MethodSecurityExpressionHandler expressionHandler() {
|
||||
DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler()
|
||||
expressionHandler.permissionEvaluator = new PermissionEvaluator() {
|
||||
boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {
|
||||
"granted" == targetDomainObject
|
||||
}
|
||||
boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) {
|
||||
throw new UnsupportedOperationException()
|
||||
}
|
||||
}
|
||||
return expressionHandler
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,443 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.method.configuration
|
||||
|
||||
import static org.fest.assertions.Assertions.assertThat
|
||||
import static org.junit.Assert.fail
|
||||
|
||||
import java.lang.reflect.Method
|
||||
|
||||
import org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator
|
||||
import org.springframework.beans.factory.BeanCreationException
|
||||
import org.springframework.context.ConfigurableApplicationContext
|
||||
import org.springframework.context.annotation.AdviceMode
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.context.annotation.Import
|
||||
import org.springframework.core.Ordered
|
||||
import org.springframework.security.access.AccessDecisionManager
|
||||
import org.springframework.security.access.AccessDeniedException
|
||||
import org.springframework.security.access.ConfigAttribute
|
||||
import org.springframework.security.access.SecurityConfig
|
||||
import org.springframework.security.access.intercept.AfterInvocationManager
|
||||
import org.springframework.security.access.intercept.RunAsManager
|
||||
import org.springframework.security.access.intercept.RunAsManagerImpl
|
||||
import org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor
|
||||
import org.springframework.security.access.intercept.aopalliance.MethodSecurityMetadataSourceAdvisor
|
||||
import org.springframework.security.access.method.AbstractMethodSecurityMetadataSource
|
||||
import org.springframework.security.access.method.MethodSecurityMetadataSource
|
||||
import org.springframework.security.authentication.AuthenticationManager
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.authentication.BaseAuthenticationConfig;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||
import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration;
|
||||
import org.springframework.security.core.Authentication
|
||||
import org.springframework.security.core.context.SecurityContextHolder
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
public class NamespaceGlobalMethodSecurityTests extends BaseSpringSpec {
|
||||
def setup() {
|
||||
SecurityContextHolder.getContext().setAuthentication(
|
||||
new TestingAuthenticationToken("user", "password","ROLE_USER"))
|
||||
}
|
||||
|
||||
// --- access-decision-manager-ref ---
|
||||
|
||||
def "custom AccessDecisionManager can be used"() {
|
||||
setup: "Create an instance with an AccessDecisionManager that always denies access"
|
||||
context = new AnnotationConfigApplicationContext(BaseMethodConfig,CustomAccessDecisionManagerConfig)
|
||||
MethodSecurityService service = context.getBean(MethodSecurityService)
|
||||
when:
|
||||
service.preAuthorize()
|
||||
then:
|
||||
thrown(AccessDeniedException)
|
||||
when:
|
||||
service.secured()
|
||||
then:
|
||||
thrown(AccessDeniedException)
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
|
||||
public static class CustomAccessDecisionManagerConfig extends GlobalMethodSecurityConfiguration {
|
||||
@Override
|
||||
protected AccessDecisionManager accessDecisionManager() {
|
||||
return new DenyAllAccessDecisionManager()
|
||||
}
|
||||
|
||||
public static class DenyAllAccessDecisionManager implements AccessDecisionManager {
|
||||
public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) {
|
||||
throw new AccessDeniedException("Always Denied")
|
||||
}
|
||||
public boolean supports(ConfigAttribute attribute) {
|
||||
return true
|
||||
}
|
||||
public boolean supports(Class<?> clazz) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- authentication-manager-ref ---
|
||||
|
||||
def "custom AuthenticationManager can be used"() {
|
||||
when:
|
||||
context = new AnnotationConfigApplicationContext(CustomAuthenticationConfig)
|
||||
MethodSecurityInterceptor interceptor = context.getBean(MethodSecurityInterceptor)
|
||||
interceptor.authenticationManager.authenticate(SecurityContextHolder.context.authentication)
|
||||
then:
|
||||
thrown(UnsupportedOperationException)
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableGlobalMethodSecurity
|
||||
public static class CustomAuthenticationConfig extends GlobalMethodSecurityConfiguration {
|
||||
@Override
|
||||
protected AuthenticationManager authenticationManager() {
|
||||
return new AuthenticationManager() {
|
||||
Authentication authenticate(Authentication authentication) {
|
||||
throw new UnsupportedOperationException()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- jsr250-annotations ---
|
||||
|
||||
def "enable jsr250"() {
|
||||
when:
|
||||
context = new AnnotationConfigApplicationContext(Jsr250Config)
|
||||
MethodSecurityService service = context.getBean(MethodSecurityService)
|
||||
then: "@Secured and @PreAuthorize are ignored"
|
||||
service.secured() == null
|
||||
service.preAuthorize() == null
|
||||
|
||||
when: "@DenyAll method invoked"
|
||||
service.jsr250()
|
||||
then: "access is denied"
|
||||
thrown(AccessDeniedException)
|
||||
}
|
||||
|
||||
@EnableGlobalMethodSecurity(jsr250Enabled = true)
|
||||
@Configuration
|
||||
public static class Jsr250Config extends BaseMethodConfig {
|
||||
}
|
||||
|
||||
// --- metadata-source-ref ---
|
||||
|
||||
def "custom MethodSecurityMetadataSource can be used with higher priority than other sources"() {
|
||||
setup:
|
||||
context = new AnnotationConfigApplicationContext(BaseMethodConfig,CustomMethodSecurityMetadataSourceConfig)
|
||||
MethodSecurityService service = context.getBean(MethodSecurityService)
|
||||
when:
|
||||
service.preAuthorize()
|
||||
then:
|
||||
thrown(AccessDeniedException)
|
||||
when:
|
||||
service.secured()
|
||||
then:
|
||||
thrown(AccessDeniedException)
|
||||
when:
|
||||
service.jsr250()
|
||||
then:
|
||||
thrown(AccessDeniedException)
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableGlobalMethodSecurity
|
||||
public static class CustomMethodSecurityMetadataSourceConfig extends GlobalMethodSecurityConfiguration {
|
||||
@Override
|
||||
protected MethodSecurityMetadataSource customMethodSecurityMetadataSource() {
|
||||
return new AbstractMethodSecurityMetadataSource() {
|
||||
public Collection<ConfigAttribute> getAttributes(Method method, Class<?> targetClass) {
|
||||
// require ROLE_NOBODY for any method on MethodSecurityService class
|
||||
return MethodSecurityService.isAssignableFrom(targetClass) ? [new SecurityConfig("ROLE_NOBODY")] : []
|
||||
}
|
||||
public Collection<ConfigAttribute> getAllConfigAttributes() {
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- mode ---
|
||||
|
||||
def "aspectj mode works"() {
|
||||
when:
|
||||
context = new AnnotationConfigApplicationContext(AspectJModeConfig)
|
||||
then:
|
||||
AnnotationAwareAspectJAutoProxyCreator autoProxyCreator = context.getBean(AnnotationAwareAspectJAutoProxyCreator)
|
||||
autoProxyCreator.proxyTargetClass == true
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableGlobalMethodSecurity(mode = AdviceMode.ASPECTJ, proxyTargetClass = true)
|
||||
public static class AspectJModeConfig extends BaseMethodConfig {
|
||||
}
|
||||
|
||||
def "aspectj mode works extending GlobalMethodSecurityConfiguration"() {
|
||||
when:
|
||||
context = new AnnotationConfigApplicationContext(BaseMethodConfig,AspectJModeExtendsGMSCConfig)
|
||||
then:
|
||||
AnnotationAwareAspectJAutoProxyCreator autoProxyCreator = context.getBean(AnnotationAwareAspectJAutoProxyCreator)
|
||||
autoProxyCreator.proxyTargetClass == false
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableGlobalMethodSecurity(mode = AdviceMode.ASPECTJ)
|
||||
public static class AspectJModeExtendsGMSCConfig extends GlobalMethodSecurityConfiguration {
|
||||
}
|
||||
|
||||
// --- order ---
|
||||
|
||||
def order() {
|
||||
when:
|
||||
context = new AnnotationConfigApplicationContext(CustomOrderConfig)
|
||||
MethodSecurityMetadataSourceAdvisor advisor = context.getBean(MethodSecurityMetadataSourceAdvisor)
|
||||
then:
|
||||
advisor.order == 135
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableGlobalMethodSecurity(order = 135)
|
||||
public static class CustomOrderConfig extends BaseMethodConfig {
|
||||
}
|
||||
|
||||
def "order is defaulted to Ordered.LOWEST_PRECEDENCE when using @EnableGlobalMethodSecurity"() {
|
||||
when:
|
||||
context = new AnnotationConfigApplicationContext(DefaultOrderConfig)
|
||||
MethodSecurityMetadataSourceAdvisor advisor = context.getBean(MethodSecurityMetadataSourceAdvisor)
|
||||
then:
|
||||
advisor.order == Ordered.LOWEST_PRECEDENCE
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableGlobalMethodSecurity
|
||||
public static class DefaultOrderConfig extends BaseMethodConfig {
|
||||
}
|
||||
|
||||
def "order is defaulted to Ordered.LOWEST_PRECEDENCE when extending GlobalMethodSecurityConfiguration"() {
|
||||
when:
|
||||
context = new AnnotationConfigApplicationContext(BaseMethodConfig,DefaultOrderExtendsMethodSecurityConfig)
|
||||
MethodSecurityMetadataSourceAdvisor advisor = context.getBean(MethodSecurityMetadataSourceAdvisor)
|
||||
then:
|
||||
advisor.order == Ordered.LOWEST_PRECEDENCE
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableGlobalMethodSecurity
|
||||
public static class DefaultOrderExtendsMethodSecurityConfig extends GlobalMethodSecurityConfiguration {
|
||||
}
|
||||
|
||||
// --- pre-post-annotations ---
|
||||
|
||||
def preAuthorize() {
|
||||
when:
|
||||
context = new AnnotationConfigApplicationContext(PreAuthorizeConfig)
|
||||
MethodSecurityService service = context.getBean(MethodSecurityService)
|
||||
then:
|
||||
service.secured() == null
|
||||
service.jsr250() == null
|
||||
|
||||
when:
|
||||
service.preAuthorize()
|
||||
then:
|
||||
thrown(AccessDeniedException)
|
||||
}
|
||||
|
||||
@EnableGlobalMethodSecurity(prePostEnabled = true)
|
||||
@Configuration
|
||||
public static class PreAuthorizeConfig extends BaseMethodConfig {
|
||||
}
|
||||
|
||||
def "prePostEnabled extends GlobalMethodSecurityConfiguration"() {
|
||||
when:
|
||||
context = new AnnotationConfigApplicationContext(BaseMethodConfig,PreAuthorizeExtendsGMSCConfig)
|
||||
MethodSecurityService service = context.getBean(MethodSecurityService)
|
||||
then:
|
||||
service.secured() == null
|
||||
service.jsr250() == null
|
||||
|
||||
when:
|
||||
service.preAuthorize()
|
||||
then:
|
||||
thrown(AccessDeniedException)
|
||||
}
|
||||
|
||||
@EnableGlobalMethodSecurity(prePostEnabled = true)
|
||||
@Configuration
|
||||
public static class PreAuthorizeExtendsGMSCConfig extends GlobalMethodSecurityConfiguration {
|
||||
}
|
||||
|
||||
// --- proxy-target-class ---
|
||||
|
||||
def "proxying classes works"() {
|
||||
when:
|
||||
context = new AnnotationConfigApplicationContext(ProxyTargetClass)
|
||||
MethodSecurityServiceImpl service = context.getBean(MethodSecurityServiceImpl)
|
||||
then:
|
||||
noExceptionThrown()
|
||||
}
|
||||
|
||||
@EnableGlobalMethodSecurity(proxyTargetClass = true)
|
||||
@Configuration
|
||||
public static class ProxyTargetClass extends BaseMethodConfig {
|
||||
}
|
||||
|
||||
def "proxying interfaces works"() {
|
||||
when:
|
||||
context = new AnnotationConfigApplicationContext(PreAuthorizeConfig)
|
||||
MethodSecurityService service = context.getBean(MethodSecurityService)
|
||||
then: "we get an instance of the interface"
|
||||
noExceptionThrown()
|
||||
when: "try to cast to the class"
|
||||
MethodSecurityServiceImpl serviceImpl = service
|
||||
then: "we get a class cast exception"
|
||||
thrown(ClassCastException)
|
||||
}
|
||||
|
||||
// --- run-as-manager-ref ---
|
||||
|
||||
def "custom RunAsManager"() {
|
||||
when:
|
||||
context = new AnnotationConfigApplicationContext(BaseMethodConfig,CustomRunAsManagerConfig)
|
||||
MethodSecurityService service = context.getBean(MethodSecurityService)
|
||||
then:
|
||||
service.runAs().authorities.find { it.authority == "ROLE_RUN_AS_SUPER"}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableGlobalMethodSecurity(securedEnabled = true)
|
||||
public static class CustomRunAsManagerConfig extends GlobalMethodSecurityConfiguration {
|
||||
@Override
|
||||
protected RunAsManager runAsManager() {
|
||||
RunAsManagerImpl runAsManager = new RunAsManagerImpl()
|
||||
runAsManager.setKey("some key")
|
||||
return runAsManager
|
||||
}
|
||||
}
|
||||
|
||||
// --- secured-annotation ---
|
||||
|
||||
def "secured enabled"() {
|
||||
setup:
|
||||
context = new AnnotationConfigApplicationContext(SecuredConfig)
|
||||
MethodSecurityService service = context.getBean(MethodSecurityService)
|
||||
when:
|
||||
service.secured()
|
||||
then:
|
||||
thrown(AccessDeniedException)
|
||||
service.preAuthorize() == null
|
||||
service.jsr250() == null
|
||||
}
|
||||
|
||||
@EnableGlobalMethodSecurity(securedEnabled = true)
|
||||
@Configuration
|
||||
public static class SecuredConfig extends BaseMethodConfig {
|
||||
}
|
||||
|
||||
// --- after-invocation-provider
|
||||
|
||||
def "custom AfterInvocationManager"() {
|
||||
setup:
|
||||
context = new AnnotationConfigApplicationContext(BaseMethodConfig,CustomAfterInvocationManagerConfig)
|
||||
MethodSecurityService service = context.getBean(MethodSecurityService)
|
||||
when:
|
||||
service.preAuthorizePermitAll()
|
||||
then:
|
||||
AccessDeniedException e = thrown()
|
||||
e.message == "custom AfterInvocationManager"
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableGlobalMethodSecurity(prePostEnabled = true)
|
||||
public static class CustomAfterInvocationManagerConfig extends GlobalMethodSecurityConfiguration {
|
||||
@Override
|
||||
protected AfterInvocationManager afterInvocationManager() {
|
||||
return new AfterInvocationManagerStub()
|
||||
}
|
||||
|
||||
public static class AfterInvocationManagerStub implements AfterInvocationManager {
|
||||
Object decide(Authentication authentication, Object object, Collection<ConfigAttribute> attributes,
|
||||
Object returnedObject) throws AccessDeniedException {
|
||||
throw new AccessDeniedException("custom AfterInvocationManager")
|
||||
}
|
||||
|
||||
boolean supports(ConfigAttribute attribute) {
|
||||
return true
|
||||
}
|
||||
boolean supports(Class<?> clazz) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- misc ---
|
||||
|
||||
def "good error message when no Enable annotation"() {
|
||||
when:
|
||||
context = new AnnotationConfigApplicationContext(ExtendsNoEnableAnntotationConfig)
|
||||
MethodSecurityInterceptor interceptor = context.getBean(MethodSecurityInterceptor)
|
||||
interceptor.authenticationManager.authenticate(SecurityContextHolder.context.authentication)
|
||||
then:
|
||||
BeanCreationException e = thrown()
|
||||
e.message.contains(EnableGlobalMethodSecurity.class.getName() + " is required")
|
||||
}
|
||||
|
||||
@Configuration
|
||||
public static class ExtendsNoEnableAnntotationConfig extends GlobalMethodSecurityConfiguration {
|
||||
@Override
|
||||
protected AuthenticationManager authenticationManager() {
|
||||
return new AuthenticationManager() {
|
||||
Authentication authenticate(Authentication authentication) {
|
||||
throw new UnsupportedOperationException()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def "import subclass of GlobalMethodSecurityConfiguration"() {
|
||||
when:
|
||||
context = new AnnotationConfigApplicationContext(ImportSubclassGMSCConfig)
|
||||
MethodSecurityService service = context.getBean(MethodSecurityService)
|
||||
then:
|
||||
service.secured() == null
|
||||
service.jsr250() == null
|
||||
|
||||
when:
|
||||
service.preAuthorize()
|
||||
then:
|
||||
thrown(AccessDeniedException)
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import(PreAuthorizeExtendsGMSCConfig)
|
||||
public static class ImportSubclassGMSCConfig extends BaseMethodConfig {
|
||||
}
|
||||
|
||||
@Configuration
|
||||
public static class BaseMethodConfig extends BaseAuthenticationConfig {
|
||||
@Bean
|
||||
public MethodSecurityService methodSecurityService() {
|
||||
return new MethodSecurityServiceImpl()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.method.configuration
|
||||
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.security.access.AccessDeniedException
|
||||
import org.springframework.security.access.PermissionEvaluator;
|
||||
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
|
||||
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler
|
||||
import org.springframework.security.authentication.AuthenticationManager
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.authentication.AuthenticationManagerBuilder
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||
import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder
|
||||
|
||||
/**
|
||||
* Demonstrate the samples
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
public class SampleEnableGlobalMethodSecurityTests extends BaseSpringSpec {
|
||||
def setup() {
|
||||
SecurityContextHolder.getContext().setAuthentication(
|
||||
new TestingAuthenticationToken("user", "password","ROLE_USER"))
|
||||
}
|
||||
|
||||
def preAuthorize() {
|
||||
when:
|
||||
loadConfig(SampleWebSecurityConfig)
|
||||
MethodSecurityService service = context.getBean(MethodSecurityService)
|
||||
then:
|
||||
service.secured() == null
|
||||
service.jsr250() == null
|
||||
|
||||
when:
|
||||
service.preAuthorize()
|
||||
then:
|
||||
thrown(AccessDeniedException)
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableGlobalMethodSecurity(prePostEnabled=true)
|
||||
public static class SampleWebSecurityConfig {
|
||||
@Bean
|
||||
public MethodSecurityService methodSecurityService() {
|
||||
return new MethodSecurityServiceImpl()
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AuthenticationManager authenticationManager() throws Exception {
|
||||
return new AuthenticationManagerBuilder()
|
||||
.inMemoryAuthentication()
|
||||
.withUser("user").password("password").roles("USER").and()
|
||||
.withUser("admin").password("password").roles("USER", "ADMIN").and()
|
||||
.and()
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
def 'custom permission handler'() {
|
||||
when:
|
||||
loadConfig(CustomPermissionEvaluatorWebSecurityConfig)
|
||||
MethodSecurityService service = context.getBean(MethodSecurityService)
|
||||
then:
|
||||
service.hasPermission("allowed") == null
|
||||
|
||||
when:
|
||||
service.hasPermission("denied") == null
|
||||
then:
|
||||
thrown(AccessDeniedException)
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableGlobalMethodSecurity(prePostEnabled=true)
|
||||
public static class CustomPermissionEvaluatorWebSecurityConfig extends GlobalMethodSecurityConfiguration {
|
||||
@Bean
|
||||
public MethodSecurityService methodSecurityService() {
|
||||
return new MethodSecurityServiceImpl()
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MethodSecurityExpressionHandler expressionHandler() {
|
||||
DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
|
||||
expressionHandler.setPermissionEvaluator(new CustomPermissionEvaluator());
|
||||
return expressionHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerAuthentication(AuthenticationManagerBuilder auth)
|
||||
throws Exception {
|
||||
auth
|
||||
.inMemoryAuthentication()
|
||||
.withUser("user").password("password").roles("USER").and()
|
||||
.withUser("admin").password("password").roles("USER", "ADMIN");
|
||||
}
|
||||
}
|
||||
|
||||
static class CustomPermissionEvaluator implements PermissionEvaluator {
|
||||
public boolean hasPermission(Authentication authentication,
|
||||
Object targetDomainObject, Object permission) {
|
||||
return !"denied".equals(targetDomainObject);
|
||||
}
|
||||
|
||||
public boolean hasPermission(Authentication authentication,
|
||||
Serializable targetId, String targetType, Object permission) {
|
||||
return !"denied".equals(targetId);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.provisioning
|
||||
|
||||
import org.springframework.security.config.annotation.authentication.configurers.provisioning.UserDetailsManagerConfigurer;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority
|
||||
import org.springframework.security.core.userdetails.UserDetails
|
||||
import org.springframework.security.provisioning.InMemoryUserDetailsManager
|
||||
|
||||
import spock.lang.Specification
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
class UserDetailsManagerConfigurerTests extends Specification {
|
||||
|
||||
def "all attributes supported"() {
|
||||
setup:
|
||||
InMemoryUserDetailsManager userDetailsManager = new InMemoryUserDetailsManager([])
|
||||
when:
|
||||
UserDetails userDetails = new UserDetailsManagerConfigurer<UserDetailsManagerConfigurer<InMemoryUserDetailsManager>>(userDetailsManager)
|
||||
.withUser("user")
|
||||
.password("password")
|
||||
.roles("USER")
|
||||
.disabled(true)
|
||||
.accountExpired(true)
|
||||
.accountLocked(true)
|
||||
.credentialsExpired(true)
|
||||
.build()
|
||||
then:
|
||||
userDetails.username == 'user'
|
||||
userDetails.password == 'password'
|
||||
userDetails.authorities.collect { it.authority } == ["ROLE_USER"]
|
||||
!userDetails.accountNonExpired
|
||||
!userDetails.accountNonLocked
|
||||
!userDetails.credentialsNonExpired
|
||||
!userDetails.enabled
|
||||
}
|
||||
|
||||
def "authorities(GrantedAuthorities...) works"() {
|
||||
setup:
|
||||
InMemoryUserDetailsManager userDetailsManager = new InMemoryUserDetailsManager([])
|
||||
SimpleGrantedAuthority authority = new SimpleGrantedAuthority("ROLE_USER")
|
||||
when:
|
||||
UserDetails userDetails = new UserDetailsManagerConfigurer<UserDetailsManagerConfigurer<InMemoryUserDetailsManager>>(userDetailsManager)
|
||||
.withUser("user")
|
||||
.password("password")
|
||||
.authorities(authority)
|
||||
.build()
|
||||
then:
|
||||
userDetails.authorities == [authority] as Set
|
||||
}
|
||||
|
||||
def "authorities(String...) works"() {
|
||||
setup:
|
||||
InMemoryUserDetailsManager userDetailsManager = new InMemoryUserDetailsManager([])
|
||||
String authority = "ROLE_USER"
|
||||
when:
|
||||
UserDetails userDetails = new UserDetailsManagerConfigurer<UserDetailsManagerConfigurer<InMemoryUserDetailsManager>>(userDetailsManager)
|
||||
.withUser("user")
|
||||
.password("password")
|
||||
.authorities(authority)
|
||||
.build()
|
||||
then:
|
||||
userDetails.authorities.collect { it.authority } == [authority]
|
||||
}
|
||||
|
||||
|
||||
def "authorities(List) works"() {
|
||||
setup:
|
||||
InMemoryUserDetailsManager userDetailsManager = new InMemoryUserDetailsManager([])
|
||||
SimpleGrantedAuthority authority = new SimpleGrantedAuthority("ROLE_USER")
|
||||
when:
|
||||
UserDetails userDetails = new UserDetailsManagerConfigurer<UserDetailsManagerConfigurer<InMemoryUserDetailsManager>>(userDetailsManager)
|
||||
.withUser("user")
|
||||
.password("password")
|
||||
.authorities([authority])
|
||||
.build()
|
||||
then:
|
||||
userDetails.authorities == [authority] as Set
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web
|
||||
|
||||
import org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder
|
||||
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
||||
import org.springframework.security.config.annotation.SecurityConfigurer
|
||||
import org.springframework.security.config.annotation.SecurityConfigurerAdapter
|
||||
import org.springframework.test.util.ReflectionTestUtils
|
||||
|
||||
import spock.lang.Specification
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
class AbstractConfiguredSecurityBuilderTests extends Specification {
|
||||
|
||||
ConcreteAbstractConfiguredBuilder builder = new ConcreteAbstractConfiguredBuilder()
|
||||
|
||||
def "Null ObjectPostProcessor rejected"() {
|
||||
when:
|
||||
new ConcreteAbstractConfiguredBuilder(null)
|
||||
then:
|
||||
thrown(IllegalArgumentException)
|
||||
when:
|
||||
builder.objectPostProcessor(null);
|
||||
then:
|
||||
thrown(IllegalArgumentException)
|
||||
}
|
||||
|
||||
def "apply null is rejected"() {
|
||||
when:
|
||||
builder.apply(null)
|
||||
then:
|
||||
thrown(IllegalArgumentException)
|
||||
}
|
||||
|
||||
def "Duplicate configurer is removed"() {
|
||||
when:
|
||||
builder.apply(new ConcreteConfigurer())
|
||||
builder.apply(new ConcreteConfigurer())
|
||||
then:
|
||||
ReflectionTestUtils.getField(builder,"configurers").size() == 1
|
||||
}
|
||||
|
||||
def "build twice fails"() {
|
||||
setup:
|
||||
builder.build()
|
||||
when:
|
||||
builder.build()
|
||||
then:
|
||||
thrown(IllegalStateException)
|
||||
}
|
||||
|
||||
def "getObject before build fails"() {
|
||||
when:
|
||||
builder.getObject()
|
||||
then:
|
||||
thrown(IllegalStateException)
|
||||
}
|
||||
|
||||
def "Configurer.init can apply another configurer"() {
|
||||
setup:
|
||||
DelegateConfigurer.CONF = Mock(SecurityConfigurerAdapter)
|
||||
when:
|
||||
builder.apply(new DelegateConfigurer())
|
||||
builder.build()
|
||||
then:
|
||||
1 * DelegateConfigurer.CONF.init(builder)
|
||||
1 * DelegateConfigurer.CONF.configure(builder)
|
||||
}
|
||||
|
||||
def "getConfigurer with multi fails"() {
|
||||
setup:
|
||||
ConcreteAbstractConfiguredBuilder builder = new ConcreteAbstractConfiguredBuilder(ObjectPostProcessor.QUIESCENT_POSTPROCESSOR, true)
|
||||
builder.apply(new DelegateConfigurer())
|
||||
builder.apply(new DelegateConfigurer())
|
||||
when:
|
||||
builder.getConfigurer(DelegateConfigurer)
|
||||
then: "Fail due to trying to obtain a single DelegateConfigurer and multiple are provided"
|
||||
thrown(IllegalStateException)
|
||||
}
|
||||
|
||||
def "removeConfigurer with multi fails"() {
|
||||
setup:
|
||||
ConcreteAbstractConfiguredBuilder builder = new ConcreteAbstractConfiguredBuilder(ObjectPostProcessor.QUIESCENT_POSTPROCESSOR, true)
|
||||
builder.apply(new DelegateConfigurer())
|
||||
builder.apply(new DelegateConfigurer())
|
||||
when:
|
||||
builder.removeConfigurer(DelegateConfigurer)
|
||||
then: "Fail due to trying to remove and obtain a single DelegateConfigurer and multiple are provided"
|
||||
thrown(IllegalStateException)
|
||||
}
|
||||
|
||||
def "removeConfigurers with multi"() {
|
||||
setup:
|
||||
DelegateConfigurer c1 = new DelegateConfigurer()
|
||||
DelegateConfigurer c2 = new DelegateConfigurer()
|
||||
ConcreteAbstractConfiguredBuilder builder = new ConcreteAbstractConfiguredBuilder(ObjectPostProcessor.QUIESCENT_POSTPROCESSOR, true)
|
||||
builder.apply(c1)
|
||||
builder.apply(c2)
|
||||
when:
|
||||
def result = builder.removeConfigurers(DelegateConfigurer)
|
||||
then:
|
||||
result.size() == 2
|
||||
result.contains(c1)
|
||||
result.contains(c2)
|
||||
builder.getConfigurers(DelegateConfigurer).empty
|
||||
}
|
||||
|
||||
def "getConfigurers with multi"() {
|
||||
setup:
|
||||
DelegateConfigurer c1 = new DelegateConfigurer()
|
||||
DelegateConfigurer c2 = new DelegateConfigurer()
|
||||
ConcreteAbstractConfiguredBuilder builder = new ConcreteAbstractConfiguredBuilder(ObjectPostProcessor.QUIESCENT_POSTPROCESSOR, true)
|
||||
builder.apply(c1)
|
||||
builder.apply(c2)
|
||||
when:
|
||||
def result = builder.getConfigurers(DelegateConfigurer)
|
||||
then:
|
||||
result.size() == 2
|
||||
result.contains(c1)
|
||||
result.contains(c2)
|
||||
builder.getConfigurers(DelegateConfigurer).size() == 2
|
||||
}
|
||||
|
||||
private static class DelegateConfigurer extends SecurityConfigurerAdapter<Object, ConcreteAbstractConfiguredBuilder> {
|
||||
private static SecurityConfigurer<Object, ConcreteAbstractConfiguredBuilder> CONF;
|
||||
|
||||
@Override
|
||||
public void init(ConcreteAbstractConfiguredBuilder builder)
|
||||
throws Exception {
|
||||
builder.apply(CONF);
|
||||
}
|
||||
}
|
||||
|
||||
private static class ConcreteConfigurer extends SecurityConfigurerAdapter<Object, ConcreteAbstractConfiguredBuilder> { }
|
||||
|
||||
private static class ConcreteAbstractConfiguredBuilder extends AbstractConfiguredSecurityBuilder<Object, ConcreteAbstractConfiguredBuilder> {
|
||||
|
||||
public ConcreteAbstractConfiguredBuilder() {
|
||||
}
|
||||
|
||||
public ConcreteAbstractConfiguredBuilder(ObjectPostProcessor<Object> objectPostProcessor) {
|
||||
super(objectPostProcessor);
|
||||
}
|
||||
|
||||
public ConcreteAbstractConfiguredBuilder(ObjectPostProcessor<Object> objectPostProcessor, boolean allowMulti) {
|
||||
super(objectPostProcessor,allowMulti);
|
||||
}
|
||||
|
||||
public Object performBuild() throws Exception {
|
||||
return "success";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web;
|
||||
|
||||
import static org.springframework.security.config.annotation.web.AbstractRequestMatcherConfigurer.RequestMatchers.*
|
||||
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.security.web.util.AntPathRequestMatcher;
|
||||
import org.springframework.security.web.util.RegexRequestMatcher;
|
||||
|
||||
import spock.lang.Specification;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
class RequestMatchersTests extends Specification {
|
||||
|
||||
def "regexMatchers(GET,'/a.*') uses RegexRequestMatcher"() {
|
||||
when:
|
||||
def matchers = regexMatchers(HttpMethod.GET, "/a.*")
|
||||
then: 'matcher is a RegexRequestMatcher'
|
||||
matchers.collect {it.class } == [RegexRequestMatcher]
|
||||
}
|
||||
|
||||
def "regexMatchers('/a.*') uses RegexRequestMatcher"() {
|
||||
when:
|
||||
def matchers = regexMatchers("/a.*")
|
||||
then: 'matcher is a RegexRequestMatcher'
|
||||
matchers.collect {it.class } == [RegexRequestMatcher]
|
||||
}
|
||||
|
||||
def "antMatchers(GET,'/a.*') uses AntPathRequestMatcher"() {
|
||||
when:
|
||||
def matchers = antMatchers(HttpMethod.GET, "/a.*")
|
||||
then: 'matcher is a RegexRequestMatcher'
|
||||
matchers.collect {it.class } == [AntPathRequestMatcher]
|
||||
}
|
||||
|
||||
def "antMatchers('/a.*') uses AntPathRequestMatcher"() {
|
||||
when:
|
||||
def matchers = antMatchers("/a.*")
|
||||
then: 'matcher is a AntPathRequestMatcher'
|
||||
matchers.collect {it.class } == [AntPathRequestMatcher]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,321 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web
|
||||
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.core.annotation.Order
|
||||
import org.springframework.security.authentication.AuthenticationManager
|
||||
import org.springframework.security.config.annotation.BaseWebSpecuritySpec
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.annotation.web.builders.WebSecurity
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
|
||||
|
||||
/**
|
||||
* Demonstrate the samples
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
public class SampleWebSecurityConfigurerAdapterTests extends BaseWebSpecuritySpec {
|
||||
def "README HelloWorld Sample works"() {
|
||||
setup: "Sample Config is loaded"
|
||||
loadConfig(HelloWorldWebSecurityConfigurerAdapter)
|
||||
when:
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.getRedirectedUrl() == "http://localhost/login"
|
||||
when: "fail to log in"
|
||||
super.setup()
|
||||
request.requestURI = "/login"
|
||||
request.method = "POST"
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then: "sent to login error page"
|
||||
response.getRedirectedUrl() == "/login?error"
|
||||
when: "login success"
|
||||
super.setup()
|
||||
request.requestURI = "/login"
|
||||
request.method = "POST"
|
||||
request.parameters.username = ["user"] as String[]
|
||||
request.parameters.password = ["password"] as String[]
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then: "sent to default succes page"
|
||||
response.getRedirectedUrl() == "/"
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>
|
||||
* <http use-expressions="true">
|
||||
* <intercept-url pattern="/resources/**" access="permitAll"/>
|
||||
* <intercept-url pattern="/**" access="authenticated"/>
|
||||
* <logout
|
||||
* logout-success-url="/login?logout"
|
||||
* logout-url="/logout"
|
||||
* <form-login
|
||||
* authentication-failure-url="/login?error"
|
||||
* login-page="/login" <!-- Except Spring Security renders the login page -->
|
||||
* login-processing-url="/login" <!-- but only POST -->
|
||||
* password-parameter="password"
|
||||
* username-parameter="username"
|
||||
* />
|
||||
* </http>
|
||||
* <authentication-manager>
|
||||
* <authentication-provider>
|
||||
* <user-service>
|
||||
* <user username="user" password="password" authorities="ROLE_USER"/>
|
||||
* </user-service>
|
||||
* </authentication-provider>
|
||||
* </authentication-manager>
|
||||
* </code>
|
||||
* @author Rob Winch
|
||||
*/
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public static class HelloWorldWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void registerAuthentication(AuthenticationManagerBuilder auth) {
|
||||
auth
|
||||
.inMemoryAuthentication()
|
||||
.withUser("user").password("password").roles("USER");
|
||||
}
|
||||
}
|
||||
|
||||
def "README Sample works"() {
|
||||
setup: "Sample Config is loaded"
|
||||
loadConfig(SampleWebSecurityConfigurerAdapter)
|
||||
when:
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.getRedirectedUrl() == "http://localhost/login"
|
||||
when: "fail to log in"
|
||||
super.setup()
|
||||
request.requestURI = "/login"
|
||||
request.method = "POST"
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then: "sent to login error page"
|
||||
response.getRedirectedUrl() == "/login?error"
|
||||
when: "login success"
|
||||
super.setup()
|
||||
request.requestURI = "/login"
|
||||
request.method = "POST"
|
||||
request.parameters.username = ["user"] as String[]
|
||||
request.parameters.password = ["password"] as String[]
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then: "sent to default succes page"
|
||||
response.getRedirectedUrl() == "/"
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>
|
||||
* <http security="none" pattern="/resources/**"/>
|
||||
* <http use-expressions="true">
|
||||
* <intercept-url pattern="/logout" access="permitAll"/>
|
||||
* <intercept-url pattern="/login" access="permitAll"/>
|
||||
* <intercept-url pattern="/signup" access="permitAll"/>
|
||||
* <intercept-url pattern="/about" access="permitAll"/>
|
||||
* <intercept-url pattern="/**" access="hasRole('ROLE_USER')"/>
|
||||
* <logout
|
||||
* logout-success-url="/login?logout"
|
||||
* logout-url="/logout"
|
||||
* <form-login
|
||||
* authentication-failure-url="/login?error"
|
||||
* login-page="/login"
|
||||
* login-processing-url="/login" <!-- but only POST -->
|
||||
* password-parameter="password"
|
||||
* username-parameter="username"
|
||||
* />
|
||||
* </http>
|
||||
* <authentication-manager>
|
||||
* <authentication-provider>
|
||||
* <user-service>
|
||||
* <user username="user" password="password" authorities="ROLE_USER"/>
|
||||
* <user username="admin" password="password" authorities="ROLE_USER,ROLE_ADMIN"/>
|
||||
* </user-service>
|
||||
* </authentication-provider>
|
||||
* </authentication-manager>
|
||||
* </code>
|
||||
* @author Rob Winch
|
||||
*/
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public static class SampleWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Override
|
||||
public void configure(WebSecurity web) throws Exception {
|
||||
web
|
||||
.ignoring()
|
||||
.antMatchers("/resources/**");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.antMatchers("/signup","/about").permitAll()
|
||||
.anyRequest().hasRole("USER")
|
||||
.and()
|
||||
.formLogin()
|
||||
.loginUrl("/login")
|
||||
// set permitAll for all URLs associated with Form Login
|
||||
.permitAll();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerAuthentication(AuthenticationManagerBuilder auth) {
|
||||
auth
|
||||
.inMemoryAuthentication()
|
||||
.withUser("user").password("password").roles("USER").and()
|
||||
.withUser("admin").password("password").roles("USER", "ADMIN");
|
||||
}
|
||||
}
|
||||
|
||||
def "README Multi http Sample works"() {
|
||||
setup:
|
||||
loadConfig(SampleMultiHttpSecurityConfig)
|
||||
when:
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.getRedirectedUrl() == "http://localhost/login"
|
||||
when: "fail to log in"
|
||||
super.setup()
|
||||
request.requestURI = "/login"
|
||||
request.method = "POST"
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then: "sent to login error page"
|
||||
response.getRedirectedUrl() == "/login?error"
|
||||
when: "login success"
|
||||
super.setup()
|
||||
request.requestURI = "/login"
|
||||
request.method = "POST"
|
||||
request.parameters.username = ["user"] as String[]
|
||||
request.parameters.password = ["password"] as String[]
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then: "sent to default succes page"
|
||||
response.getRedirectedUrl() == "/"
|
||||
|
||||
when: "request protected API URL"
|
||||
super.setup()
|
||||
request.servletPath = "/api/admin/test"
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then: "get 403"
|
||||
response.getStatus() == 403
|
||||
|
||||
when: "request API for admins with user"
|
||||
super.setup()
|
||||
request.servletPath = "/api/admin/test"
|
||||
request.addHeader("Authorization", "Basic " + "user:password".bytes.encodeBase64().toString())
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then: "get 403"
|
||||
response.getStatus() == 403
|
||||
|
||||
when: "request API for admins with admin"
|
||||
super.setup()
|
||||
request.servletPath = "/api/admin/test"
|
||||
request.addHeader("Authorization", "Basic " + "admin:password".bytes.encodeBase64().toString())
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then: "get 200"
|
||||
response.getStatus() == 200
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* <code>
|
||||
* <http security="none" pattern="/resources/**"/>
|
||||
* <http use-expressions="true" pattern="/api/**">
|
||||
* <intercept-url pattern="/api/admin/**" access="hasRole('ROLE_ADMIN')"/>
|
||||
* <intercept-url pattern="/api/**" access="hasRole('ROLE_USER')"/>
|
||||
* <http-basic />
|
||||
* </http>
|
||||
* <http use-expressions="true">
|
||||
* <intercept-url pattern="/logout" access="permitAll"/>
|
||||
* <intercept-url pattern="/login" access="permitAll"/>
|
||||
* <intercept-url pattern="/signup" access="permitAll"/>
|
||||
* <intercept-url pattern="/about" access="permitAll"/>
|
||||
* <intercept-url pattern="/**" access="hasRole('ROLE_USER')"/>
|
||||
* <logout
|
||||
* logout-success-url="/login?logout"
|
||||
* logout-url="/logout"
|
||||
* <form-login
|
||||
* authentication-failure-url="/login?error"
|
||||
* login-page="/login"
|
||||
* login-processing-url="/login" <!-- but only POST -->
|
||||
* password-parameter="password"
|
||||
* username-parameter="username"
|
||||
* />
|
||||
* </http>
|
||||
* <authentication-manager>
|
||||
* <authentication-provider>
|
||||
* <user-service>
|
||||
* <user username="user" password="password" authorities="ROLE_USER"/>
|
||||
* <user username="admin" password="password" authorities="ROLE_USER,ROLE_ADMIN"/>
|
||||
* </user-service>
|
||||
* </authentication-provider>
|
||||
* </authentication-manager>
|
||||
* </code>
|
||||
* @author Rob Winch
|
||||
*/
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public static class SampleMultiHttpSecurityConfig {
|
||||
@Bean
|
||||
public AuthenticationManager authenticationManager() {
|
||||
return new AuthenticationManagerBuilder()
|
||||
.inMemoryAuthentication()
|
||||
.withUser("user").password("password").roles("USER").and()
|
||||
.withUser("admin").password("password").roles("USER", "ADMIN").and()
|
||||
.and()
|
||||
.build();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Order(1)
|
||||
public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.antMatcher("/api/**")
|
||||
.authorizeUrls()
|
||||
.antMatchers("/api/admin/**").hasRole("ADMIN")
|
||||
.antMatchers("/api/**").hasRole("USER")
|
||||
.and()
|
||||
.httpBasic();
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
public void configure(WebSecurity web) throws Exception {
|
||||
web
|
||||
.ignoring()
|
||||
.antMatchers("/resources/**");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.antMatchers("/signup","/about").permitAll()
|
||||
.anyRequest().hasRole("USER")
|
||||
.and()
|
||||
.formLogin()
|
||||
.loginUrl("/login")
|
||||
.permitAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web;
|
||||
|
||||
import static org.springframework.security.config.annotation.web.WebSecurityConfigurerAdapterTestsConfigs.*
|
||||
import static org.junit.Assert.*
|
||||
|
||||
import javax.sql.DataSource
|
||||
|
||||
import org.springframework.context.ApplicationContext
|
||||
import org.springframework.context.ApplicationListener
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder
|
||||
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType
|
||||
import org.springframework.ldap.core.support.BaseLdapPathContextSource
|
||||
import org.springframework.security.authentication.AuthenticationManager
|
||||
import org.springframework.security.authentication.AuthenticationProvider
|
||||
import org.springframework.security.authentication.DefaultAuthenticationEventPublisher
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
|
||||
import org.springframework.security.authentication.event.AuthenticationSuccessEvent
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.authentication.configurers.ldap.LdapAuthenticationProviderConfigurer;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.core.Authentication
|
||||
import org.springframework.security.core.authority.AuthorityUtils
|
||||
import org.springframework.security.ldap.DefaultSpringSecurityContextSource
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
class WebSecurityConfigurerAdapterTests extends BaseSpringSpec {
|
||||
|
||||
def "MessageSources populated on AuthenticationProviders"() {
|
||||
when:
|
||||
loadConfig(MessageSourcesPopulatedConfig)
|
||||
List<AuthenticationProvider> providers = authenticationProviders()
|
||||
then:
|
||||
providers*.messages*.messageSource == [context,context,context,context]
|
||||
}
|
||||
|
||||
def "messages set when using WebSecurityConfigurerAdapter"() {
|
||||
when:
|
||||
loadConfig(InMemoryAuthWithWebSecurityConfigurerAdapter)
|
||||
then:
|
||||
authenticationManager.messages.messageSource instanceof ApplicationContext
|
||||
}
|
||||
|
||||
def "AuthenticationEventPublisher is registered for Web registerAuthentication"() {
|
||||
when:
|
||||
loadConfig(InMemoryAuthWithWebSecurityConfigurerAdapter)
|
||||
then:
|
||||
authenticationManager.parent.eventPublisher instanceof DefaultAuthenticationEventPublisher
|
||||
when:
|
||||
Authentication token = new UsernamePasswordAuthenticationToken("user","password")
|
||||
authenticationManager.authenticate(token)
|
||||
then: "We only receive the AuthenticationSuccessEvent once"
|
||||
InMemoryAuthWithWebSecurityConfigurerAdapter.EVENTS.size() == 1
|
||||
InMemoryAuthWithWebSecurityConfigurerAdapter.EVENTS[0].authentication.name == token.principal
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class InMemoryAuthWithWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter implements ApplicationListener<AuthenticationSuccessEvent> {
|
||||
static List<AuthenticationSuccessEvent> EVENTS = []
|
||||
@Bean
|
||||
@Override
|
||||
public AuthenticationManager authenticationManagerBean()
|
||||
throws Exception {
|
||||
return super.authenticationManagerBean();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerAuthentication(AuthenticationManagerBuilder auth)
|
||||
throws Exception {
|
||||
auth
|
||||
.inMemoryAuthentication()
|
||||
.withUser("user").password("password").roles("USER")
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(AuthenticationSuccessEvent e) {
|
||||
EVENTS.add(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
|
||||
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
|
||||
import org.springframework.ldap.core.support.BaseLdapPathContextSource;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
public class WebSecurityConfigurerAdapterTestsConfigs {
|
||||
|
||||
// necessary because groovy resolves incorrect method when using generics
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
static class MessageSourcesPopulatedConfig extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.antMatcher("/role1/**")
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("1");
|
||||
}
|
||||
|
||||
@Bean
|
||||
public BaseLdapPathContextSource contextSource() throws Exception {
|
||||
DefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource("ldap://127.0.0.1:33389/dc=springframework,dc=org");
|
||||
contextSource.setUserDn("uid=admin,ou=system");
|
||||
contextSource.setPassword("secret");
|
||||
return contextSource;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DataSource dataSource() {
|
||||
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
|
||||
return builder.setType(EmbeddedDatabaseType.HSQL).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerAuthentication(AuthenticationManagerBuilder auth)
|
||||
throws Exception {
|
||||
auth
|
||||
.inMemoryAuthentication().and()
|
||||
.jdbcAuthentication()
|
||||
.dataSource(dataSource())
|
||||
.and()
|
||||
.ldapAuthentication()
|
||||
.userDnPatterns("uid={0},ou=people")
|
||||
.contextSource(contextSource());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web.builders
|
||||
|
||||
import javax.servlet.Filter
|
||||
import javax.servlet.FilterChain
|
||||
import javax.servlet.ServletException
|
||||
import javax.servlet.http.HttpServletRequest
|
||||
import javax.servlet.http.HttpServletResponse
|
||||
|
||||
import org.springframework.beans.factory.BeanCreationException
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.mock.web.MockFilterChain
|
||||
import org.springframework.mock.web.MockHttpServletRequest
|
||||
import org.springframework.mock.web.MockHttpServletResponse
|
||||
import org.springframework.security.cas.web.CasAuthenticationFilter
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.web.configuration.BaseWebConfig
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
|
||||
import org.springframework.web.filter.OncePerRequestFilter
|
||||
|
||||
/**
|
||||
* HttpSecurity tests
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
public class HttpSecurityTests extends BaseSpringSpec {
|
||||
def "addFilter with unregistered Filter"() {
|
||||
when:
|
||||
loadConfig(UnregisteredFilterConfig)
|
||||
then:
|
||||
BeanCreationException success = thrown()
|
||||
success.message.contains "The Filter class ${UnregisteredFilter.name} does not have a registered order and cannot be added without a specified order. Consider using addFilterBefore or addFilterAfter instead."
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class UnregisteredFilterConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.addFilter(new UnregisteredFilter())
|
||||
}
|
||||
}
|
||||
|
||||
static class UnregisteredFilter extends OncePerRequestFilter {
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request,
|
||||
HttpServletResponse response, FilterChain filterChain)
|
||||
throws ServletException, IOException {
|
||||
filterChain.doFilter(request, response);
|
||||
}
|
||||
}
|
||||
|
||||
// https://github.com/SpringSource/spring-security-javaconfig/issues/104
|
||||
def "#104 addFilter CasAuthenticationFilter"() {
|
||||
when:
|
||||
loadConfig(CasAuthenticationFilterConfig)
|
||||
then:
|
||||
findFilter(CasAuthenticationFilter)
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class CasAuthenticationFilterConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.addFilter(new CasAuthenticationFilter())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def "requestMatchers() javadoc"() {
|
||||
setup: "load configuration like the config on the requestMatchers() javadoc"
|
||||
loadConfig(RequestMatcherRegistryConfigs)
|
||||
when:
|
||||
super.setup()
|
||||
request.servletPath = "/oauth/a"
|
||||
springSecurityFilterChain.doFilter(request, response, chain)
|
||||
then:
|
||||
response.status == 403
|
||||
where:
|
||||
servletPath | status
|
||||
"/oauth/a" | 403
|
||||
"/oauth/b" | 403
|
||||
"/api/a" | 403
|
||||
"/api/b" | 403
|
||||
"/oauth2/b" | 200
|
||||
"/api2/b" | 200
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class RequestMatcherRegistryConfigs extends BaseWebConfig {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.requestMatchers()
|
||||
.antMatchers("/api/**")
|
||||
.antMatchers("/oauth/**")
|
||||
.and()
|
||||
.authorizeUrls()
|
||||
.antMatchers("/**").hasRole("USER")
|
||||
.and()
|
||||
.httpBasic()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,511 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web.builders
|
||||
|
||||
import javax.servlet.http.HttpServletRequest
|
||||
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.security.access.AccessDecisionManager
|
||||
import org.springframework.security.access.ConfigAttribute
|
||||
import org.springframework.security.access.vote.AuthenticatedVoter
|
||||
import org.springframework.security.access.vote.RoleVoter
|
||||
import org.springframework.security.authentication.AuthenticationManager
|
||||
import org.springframework.security.authentication.BadCredentialsException
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.web.builders.NamespaceHttpTests.AuthenticationManagerRefConfig.CustomAuthenticationManager
|
||||
import org.springframework.security.config.annotation.web.builders.NamespaceHttpTests.RequestMatcherRefConfig.MyRequestMatcher
|
||||
import org.springframework.security.config.annotation.web.configuration.BaseWebConfig
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
|
||||
import org.springframework.security.config.annotation.web.configurers.SessionCreationPolicy
|
||||
import org.springframework.security.config.annotation.web.configurers.UrlAuthorizationConfigurer
|
||||
import org.springframework.security.core.Authentication
|
||||
import org.springframework.security.core.AuthenticationException
|
||||
import org.springframework.security.web.FilterInvocation
|
||||
import org.springframework.security.web.access.ExceptionTranslationFilter
|
||||
import org.springframework.security.web.access.expression.ExpressionBasedFilterInvocationSecurityMetadataSource
|
||||
import org.springframework.security.web.access.expression.WebExpressionVoter
|
||||
import org.springframework.security.web.access.intercept.DefaultFilterInvocationSecurityMetadataSource
|
||||
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor
|
||||
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint
|
||||
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter
|
||||
import org.springframework.security.web.context.HttpSessionSecurityContextRepository
|
||||
import org.springframework.security.web.context.NullSecurityContextRepository
|
||||
import org.springframework.security.web.context.SecurityContextPersistenceFilter
|
||||
import org.springframework.security.web.jaasapi.JaasApiIntegrationFilter
|
||||
import org.springframework.security.web.savedrequest.HttpSessionRequestCache
|
||||
import org.springframework.security.web.savedrequest.NullRequestCache
|
||||
import org.springframework.security.web.savedrequest.RequestCacheAwareFilter
|
||||
import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter
|
||||
import org.springframework.security.web.session.SessionManagementFilter
|
||||
import org.springframework.security.web.util.RegexRequestMatcher
|
||||
import org.springframework.security.web.util.RequestMatcher
|
||||
|
||||
/**
|
||||
* Tests to verify that all the functionality of <http> attributes is present
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
public class NamespaceHttpTests extends BaseSpringSpec {
|
||||
def "http@access-decision-manager-ref"() {
|
||||
setup:
|
||||
AccessDecisionManagerRefConfig.ACCESS_DECISION_MGR = Mock(AccessDecisionManager)
|
||||
AccessDecisionManagerRefConfig.ACCESS_DECISION_MGR.supports(FilterInvocation) >> true
|
||||
AccessDecisionManagerRefConfig.ACCESS_DECISION_MGR.supports(_ as ConfigAttribute) >> true
|
||||
when:
|
||||
loadConfig(AccessDecisionManagerRefConfig)
|
||||
then:
|
||||
findFilter(FilterSecurityInterceptor).accessDecisionManager == AccessDecisionManagerRefConfig.ACCESS_DECISION_MGR
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class AccessDecisionManagerRefConfig extends BaseWebConfig {
|
||||
static AccessDecisionManager ACCESS_DECISION_MGR
|
||||
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.anyRequest().permitAll()
|
||||
.accessDecisionManager(ACCESS_DECISION_MGR)
|
||||
}
|
||||
}
|
||||
|
||||
def "http@access-denied-page"() {
|
||||
when:
|
||||
loadConfig(AccessDeniedPageConfig)
|
||||
then:
|
||||
findFilter(ExceptionTranslationFilter).accessDeniedHandler.errorPage == "/AccessDeniedPageConfig"
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class AccessDeniedPageConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.exceptionHandling()
|
||||
.accessDeniedPage("/AccessDeniedPageConfig")
|
||||
}
|
||||
}
|
||||
|
||||
def "http@authentication-manager-ref"() {
|
||||
when: "Specify AuthenticationManager"
|
||||
loadConfig(AuthenticationManagerRefConfig)
|
||||
then: "Populates the AuthenticationManager"
|
||||
findFilter(FilterSecurityInterceptor).authenticationManager.parent.class == CustomAuthenticationManager
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class AuthenticationManagerRefConfig extends BaseWebConfig {
|
||||
// demo authentication-manager-ref (could be any value)
|
||||
|
||||
@Override
|
||||
protected AuthenticationManager authenticationManager() throws Exception {
|
||||
return new CustomAuthenticationManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("USER");
|
||||
}
|
||||
|
||||
static class CustomAuthenticationManager implements AuthenticationManager {
|
||||
public Authentication authenticate(Authentication authentication)
|
||||
throws AuthenticationException {
|
||||
throw new BadCredentialsException("This always fails");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Note: There is no http@auto-config equivalent in Java Config
|
||||
|
||||
def "http@create-session=always"() {
|
||||
when:
|
||||
loadConfig(IfRequiredConfig)
|
||||
then:
|
||||
findFilter(SecurityContextPersistenceFilter).forceEagerSessionCreation == false
|
||||
findFilter(SecurityContextPersistenceFilter).repo.allowSessionCreation == true
|
||||
findFilter(SessionManagementFilter).securityContextRepository.allowSessionCreation == true
|
||||
findFilter(ExceptionTranslationFilter).requestCache.class == HttpSessionRequestCache
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class CreateSessionAlwaysConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.sessionManagement()
|
||||
.sessionCreationPolicy(SessionCreationPolicy.always);
|
||||
}
|
||||
}
|
||||
|
||||
def "http@create-session=stateless"() {
|
||||
when:
|
||||
loadConfig(CreateSessionStatelessConfig)
|
||||
then:
|
||||
findFilter(SecurityContextPersistenceFilter).forceEagerSessionCreation == false
|
||||
findFilter(SecurityContextPersistenceFilter).repo.class == NullSecurityContextRepository
|
||||
findFilter(SessionManagementFilter).securityContextRepository.class == NullSecurityContextRepository
|
||||
findFilter(ExceptionTranslationFilter).requestCache.class == NullRequestCache
|
||||
findFilter(RequestCacheAwareFilter).requestCache.class == NullRequestCache
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class CreateSessionStatelessConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.sessionManagement()
|
||||
.sessionCreationPolicy(SessionCreationPolicy.stateless);
|
||||
}
|
||||
}
|
||||
|
||||
def "http@create-session=ifRequired"() {
|
||||
when:
|
||||
loadConfig(IfRequiredConfig)
|
||||
then:
|
||||
findFilter(SecurityContextPersistenceFilter).forceEagerSessionCreation == false
|
||||
findFilter(SecurityContextPersistenceFilter).repo.allowSessionCreation == true
|
||||
findFilter(SessionManagementFilter).securityContextRepository.allowSessionCreation == true
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class IfRequiredConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.sessionManagement()
|
||||
.sessionCreationPolicy(SessionCreationPolicy.ifRequired);
|
||||
}
|
||||
}
|
||||
|
||||
def "http@create-session defaults to ifRequired"() {
|
||||
when:
|
||||
loadConfig(IfRequiredConfig)
|
||||
then:
|
||||
findFilter(SecurityContextPersistenceFilter).forceEagerSessionCreation == false
|
||||
findFilter(SecurityContextPersistenceFilter).repo.allowSessionCreation == true
|
||||
findFilter(SessionManagementFilter).securityContextRepository.allowSessionCreation == true
|
||||
}
|
||||
|
||||
def "http@create-session=never"() {
|
||||
when:
|
||||
loadConfig(CreateSessionNeverConfig)
|
||||
then:
|
||||
findFilter(SecurityContextPersistenceFilter).forceEagerSessionCreation == false
|
||||
findFilter(SecurityContextPersistenceFilter).repo.allowSessionCreation == false
|
||||
findFilter(SessionManagementFilter).securityContextRepository.allowSessionCreation == false
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class CreateSessionNeverConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.sessionManagement()
|
||||
.sessionCreationPolicy(SessionCreationPolicy.never);
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class DefaultCreateSessionConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
}
|
||||
}
|
||||
|
||||
def "http@disable-url-rewriting = true (default for Java Config)"() {
|
||||
when:
|
||||
loadConfig(DefaultUrlRewritingConfig)
|
||||
then:
|
||||
findFilter(SecurityContextPersistenceFilter).repo.disableUrlRewriting
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class DefaultUrlRewritingConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
}
|
||||
}
|
||||
|
||||
// http@disable-url-rewriting is on by default to disable it create a custom HttpSecurityContextRepository and use security-context-repository-ref
|
||||
|
||||
def "http@disable-url-rewriting = false"() {
|
||||
when:
|
||||
loadConfig(EnableUrlRewritingConfig)
|
||||
then:
|
||||
findFilter(SecurityContextPersistenceFilter).repo.disableUrlRewriting == false
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class EnableUrlRewritingConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
HttpSessionSecurityContextRepository repository = new HttpSessionSecurityContextRepository()
|
||||
repository.disableUrlRewriting = false // explicitly configured (not necessary due to default values)
|
||||
|
||||
http.
|
||||
securityContext()
|
||||
.securityContextRepository(repository)
|
||||
}
|
||||
}
|
||||
|
||||
def "http@entry-point-ref"() {
|
||||
when:
|
||||
loadConfig(EntryPointRefConfig)
|
||||
then:
|
||||
findFilter(ExceptionTranslationFilter).authenticationEntryPoint.loginFormUrl == "/EntryPointRefConfig"
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class EntryPointRefConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.exceptionHandling()
|
||||
.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/EntryPointRefConfig"))
|
||||
}
|
||||
}
|
||||
|
||||
def "http@jaas-api-provision"() {
|
||||
when:
|
||||
loadConfig(JaasApiProvisionConfig)
|
||||
then:
|
||||
findFilter(JaasApiIntegrationFilter)
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class JaasApiProvisionConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.addFilter(new JaasApiIntegrationFilter())
|
||||
}
|
||||
}
|
||||
|
||||
// http@name is not available since it can be done w/ standard bean configuration easily
|
||||
|
||||
def "http@once-per-request=true"() {
|
||||
when:
|
||||
loadConfig(OncePerRequestConfig)
|
||||
then:
|
||||
findFilter(FilterSecurityInterceptor).observeOncePerRequest
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class OncePerRequestConfig extends BaseWebConfig {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("USER");
|
||||
}
|
||||
}
|
||||
|
||||
def "http@once-per-request=false"() {
|
||||
when:
|
||||
loadConfig(OncePerRequestFalseConfig)
|
||||
then:
|
||||
!findFilter(FilterSecurityInterceptor).observeOncePerRequest
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class OncePerRequestFalseConfig extends BaseWebConfig {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http.
|
||||
authorizeUrls()
|
||||
.filterSecurityInterceptorOncePerRequest(false)
|
||||
.antMatchers("/users**","/sessions/**").hasRole("ADMIN")
|
||||
.antMatchers("/signup").permitAll()
|
||||
.anyRequest().hasRole("USER");
|
||||
}
|
||||
}
|
||||
|
||||
// http@path-type is not available (instead request matcher instances are used)
|
||||
|
||||
// http@pattern is not available (instead see the tests http@request-matcher-ref ant or http@request-matcher-ref regex)
|
||||
|
||||
def "http@realm"() {
|
||||
when:
|
||||
loadConfig(RealmConfig)
|
||||
then:
|
||||
findFilter(BasicAuthenticationFilter).authenticationEntryPoint.realmName == "RealmConfig"
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class RealmConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.httpBasic().realmName("RealmConfig")
|
||||
}
|
||||
}
|
||||
|
||||
// http@request-matcher is not available (instead request matcher instances are used)
|
||||
|
||||
def "http@request-matcher-ref ant"() {
|
||||
when:
|
||||
loadConfig(RequestMatcherAntConfig)
|
||||
then:
|
||||
filterChain(0).requestMatcher.pattern == "/api/**"
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class RequestMatcherAntConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.antMatcher("/api/**")
|
||||
}
|
||||
}
|
||||
|
||||
def "http@request-matcher-ref regex"() {
|
||||
when:
|
||||
loadConfig(RequestMatcherRegexConfig)
|
||||
then:
|
||||
filterChain(0).requestMatcher.class == RegexRequestMatcher
|
||||
filterChain(0).requestMatcher.pattern.matcher("/regex/a")
|
||||
filterChain(0).requestMatcher.pattern.matcher("/regex/b")
|
||||
!filterChain(0).requestMatcher.pattern.matcher("/regex1/b")
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class RequestMatcherRegexConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.regexMatcher("/regex/.*")
|
||||
}
|
||||
}
|
||||
|
||||
def "http@request-matcher-ref"() {
|
||||
when:
|
||||
loadConfig(RequestMatcherRefConfig)
|
||||
then:
|
||||
filterChain(0).requestMatcher.class == MyRequestMatcher
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class RequestMatcherRefConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.requestMatcher(new MyRequestMatcher());
|
||||
}
|
||||
static class MyRequestMatcher implements RequestMatcher {
|
||||
public boolean matches(HttpServletRequest request) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def "http@security=none"() {
|
||||
when:
|
||||
loadConfig(SecurityNoneConfig)
|
||||
then:
|
||||
filterChain(0).requestMatcher.pattern == "/resources/**"
|
||||
filterChain(0).filters.empty
|
||||
filterChain(1).requestMatcher.pattern == "/public/**"
|
||||
filterChain(1).filters.empty
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class SecurityNoneConfig extends BaseWebConfig {
|
||||
|
||||
@Override
|
||||
public void configure(WebSecurity web)
|
||||
throws Exception {
|
||||
web
|
||||
.ignoring()
|
||||
.antMatchers("/resources/**","/public/**")
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {}
|
||||
|
||||
}
|
||||
|
||||
def "http@security-context-repository-ref"() {
|
||||
when:
|
||||
loadConfig(SecurityContextRepoConfig)
|
||||
then:
|
||||
findFilter(SecurityContextPersistenceFilter).repo.class == NullSecurityContextRepository
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class SecurityContextRepoConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.securityContext()
|
||||
.securityContextRepository(new NullSecurityContextRepository()) // security-context-repository-ref
|
||||
}
|
||||
}
|
||||
|
||||
def "http@servlet-api-provision=false"() {
|
||||
when:
|
||||
loadConfig(ServletApiProvisionConfig)
|
||||
then:
|
||||
findFilter(SecurityContextHolderAwareRequestFilter) == null
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class ServletApiProvisionConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http.servletApi().disable()
|
||||
}
|
||||
}
|
||||
|
||||
def "http@servlet-api-provision defaults to true"() {
|
||||
when:
|
||||
loadConfig(ServletApiProvisionDefaultsConfig)
|
||||
then:
|
||||
findFilter(SecurityContextHolderAwareRequestFilter) != null
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class ServletApiProvisionDefaultsConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
}
|
||||
}
|
||||
|
||||
def "http@use-expressions=true"() {
|
||||
when:
|
||||
loadConfig(UseExpressionsConfig)
|
||||
then:
|
||||
findFilter(FilterSecurityInterceptor).securityMetadataSource.class == ExpressionBasedFilterInvocationSecurityMetadataSource
|
||||
findFilter(FilterSecurityInterceptor).accessDecisionManager.decisionVoters.collect { it.class } == [WebExpressionVoter]
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
static class UseExpressionsConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.antMatchers("/users**","/sessions/**").hasRole("USER")
|
||||
.antMatchers("/signup").permitAll()
|
||||
.anyRequest().hasRole("USER")
|
||||
}
|
||||
}
|
||||
|
||||
def "http@use-expressions=false"() {
|
||||
when:
|
||||
loadConfig(DisableUseExpressionsConfig)
|
||||
then:
|
||||
findFilter(FilterSecurityInterceptor).securityMetadataSource.class == DefaultFilterInvocationSecurityMetadataSource
|
||||
findFilter(FilterSecurityInterceptor).accessDecisionManager.decisionVoters.collect { it.class } == [RoleVoter, AuthenticatedVoter]
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
static class DisableUseExpressionsConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.apply(new UrlAuthorizationConfigurer())
|
||||
.antMatchers("/users**","/sessions/**").hasRole("USER")
|
||||
.antMatchers("/signup").hasRole("ANONYMOUS")
|
||||
.anyRequest().hasRole("USER")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web.configuration;
|
||||
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public abstract class BaseWebConfig extends WebSecurityConfigurerAdapter {
|
||||
BaseWebConfig(boolean disableDefaults) {
|
||||
super(disableDefaults)
|
||||
}
|
||||
|
||||
BaseWebConfig() {
|
||||
}
|
||||
|
||||
protected void registerAuthentication(
|
||||
AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.inMemoryAuthentication()
|
||||
.withUser("user").password("password").roles("USER").and()
|
||||
.withUser("admin").password("password").roles("USER", "ADMIN");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web.configuration;
|
||||
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.mock.web.MockHttpServletRequest
|
||||
import org.springframework.security.authentication.AnonymousAuthenticationToken
|
||||
import org.springframework.security.authentication.AuthenticationManager
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
|
||||
import org.springframework.security.config.annotation.web.builders.DebugFilter;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter
|
||||
|
||||
class EnableWebSecurityTests extends BaseSpringSpec {
|
||||
|
||||
def "@Bean(BeanIds.AUTHENTICATION_MANAGER) includes HttpSecurity's AuthenticationManagerBuilder"() {
|
||||
when:
|
||||
loadConfig(SecurityConfig)
|
||||
AuthenticationManager authenticationManager = context.getBean(AuthenticationManager)
|
||||
AnonymousAuthenticationToken anonymousAuthToken = findFilter(AnonymousAuthenticationFilter).createAuthentication(new MockHttpServletRequest())
|
||||
then:
|
||||
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("user", "password"))
|
||||
authenticationManager.authenticate(anonymousAuthToken)
|
||||
|
||||
}
|
||||
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class SecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void registerAuthentication(AuthenticationManagerBuilder auth)
|
||||
throws Exception {
|
||||
auth
|
||||
.inMemoryAuthentication()
|
||||
.withUser("user").password("password").roles("USER");
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Override
|
||||
public AuthenticationManager authenticationManagerBean()
|
||||
throws Exception {
|
||||
return super.authenticationManagerBean();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.antMatchers("/*").hasRole("USER")
|
||||
.and()
|
||||
.formLogin();
|
||||
}
|
||||
}
|
||||
|
||||
def "@EnableWebSecurity on superclass"() {
|
||||
when:
|
||||
loadConfig(ChildSecurityConfig)
|
||||
then:
|
||||
context.getBean("springSecurityFilterChain", DebugFilter)
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class ChildSecurityConfig extends DebugSecurityConfig {
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity(debug=true)
|
||||
static class DebugSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web.configuration;
|
||||
|
||||
import static org.junit.Assert.*
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.BeanCreationException
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.core.annotation.Order
|
||||
import org.springframework.security.authentication.AuthenticationManager
|
||||
import org.springframework.security.authentication.event.AuthenticationSuccessEvent;
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.annotation.web.builders.WebSecurity
|
||||
import org.springframework.security.web.FilterChainProxy
|
||||
import org.springframework.security.web.SecurityFilterChain
|
||||
import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator;
|
||||
import org.springframework.security.web.access.expression.WebSecurityExpressionHandler;
|
||||
import org.springframework.security.web.util.AnyRequestMatcher
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
class WebSecurityConfigurationTests extends BaseSpringSpec {
|
||||
|
||||
def "WebSecurityConfigurers are sorted"() {
|
||||
when:
|
||||
loadConfig(SortedWebSecurityConfigurerAdaptersConfig);
|
||||
List<SecurityFilterChain> filterChains = context.getBean(FilterChainProxy).filterChains
|
||||
then:
|
||||
filterChains[0].requestMatcher.pattern == "/ignore1"
|
||||
filterChains[0].filters.empty
|
||||
filterChains[1].requestMatcher.pattern == "/ignore2"
|
||||
filterChains[1].filters.empty
|
||||
|
||||
filterChains[2].requestMatcher.pattern == "/role1/**"
|
||||
filterChains[3].requestMatcher.pattern == "/role2/**"
|
||||
filterChains[4].requestMatcher.pattern == "/role3/**"
|
||||
filterChains[5].requestMatcher.class == AnyRequestMatcher
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
static class SortedWebSecurityConfigurerAdaptersConfig {
|
||||
public AuthenticationManager authenticationManager() throws Exception {
|
||||
return new AuthenticationManagerBuilder()
|
||||
.inMemoryAuthentication()
|
||||
.withUser("marissa").password("koala").roles("USER").and()
|
||||
.withUser("paul").password("emu").roles("USER").and()
|
||||
.and()
|
||||
.build();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Order(1)
|
||||
public static class WebConfigurer1 extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
public void configure(WebSecurity web) throws Exception {
|
||||
web
|
||||
.ignoring()
|
||||
.antMatchers("/ignore1","/ignore2");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.antMatcher("/role1/**")
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("1");
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Order(2)
|
||||
public static class WebConfigurer2 extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.antMatcher("/role2/**")
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("2");
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Order(3)
|
||||
public static class WebConfigurer3 extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.antMatcher("/role3/**")
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("3");
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
public static class WebConfigurer4 extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("4");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def "WebSecurityConfigurers fails with duplicate order"() {
|
||||
when:
|
||||
loadConfig(DuplicateOrderConfig);
|
||||
then:
|
||||
BeanCreationException e = thrown()
|
||||
e.message.contains "@Order on WebSecurityConfigurers must be unique"
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
static class DuplicateOrderConfig {
|
||||
public AuthenticationManager authenticationManager() throws Exception {
|
||||
return new AuthenticationManagerBuilder()
|
||||
.inMemoryAuthentication()
|
||||
.withUser("user").password("password").roles("USER").and()
|
||||
.and()
|
||||
.build();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
public static class WebConfigurer1 extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.antMatcher("/role1/**")
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("1");
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
public static class WebConfigurer2 extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.antMatcher("/role2/**")
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("2");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def "Override privilegeEvaluator"() {
|
||||
setup:
|
||||
WebInvocationPrivilegeEvaluator privilegeEvaluator = Mock()
|
||||
PrivilegeEvaluatorConfigurerAdapterConfig.PE = privilegeEvaluator
|
||||
when:
|
||||
loadConfig(PrivilegeEvaluatorConfigurerAdapterConfig)
|
||||
then:
|
||||
context.getBean(WebInvocationPrivilegeEvaluator) == privilegeEvaluator
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class PrivilegeEvaluatorConfigurerAdapterConfig extends WebSecurityConfigurerAdapter {
|
||||
static WebInvocationPrivilegeEvaluator PE
|
||||
|
||||
@Override
|
||||
public void configure(WebSecurity web) throws Exception {
|
||||
web
|
||||
.privilegeEvaluator(PE)
|
||||
}
|
||||
}
|
||||
|
||||
def "Override webSecurityExpressionHandler"() {
|
||||
setup:
|
||||
WebSecurityExpressionHandler expressionHandler = Mock()
|
||||
WebSecurityExpressionHandlerConfig.EH = expressionHandler
|
||||
when:
|
||||
loadConfig(WebSecurityExpressionHandlerConfig)
|
||||
then:
|
||||
context.getBean(WebSecurityExpressionHandler) == expressionHandler
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class WebSecurityExpressionHandlerConfig extends WebSecurityConfigurerAdapter {
|
||||
@SuppressWarnings("deprecation")
|
||||
static WebSecurityExpressionHandler EH
|
||||
|
||||
@Override
|
||||
public void configure(WebSecurity web) throws Exception {
|
||||
web
|
||||
.expressionHandler(EH)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web.configurers;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.security.access.AccessDecisionVoter;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractRequestMatcherMappingConfigurer;
|
||||
import org.springframework.security.web.DefaultSecurityFilterChain;
|
||||
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
|
||||
import org.springframework.security.web.util.AntPathRequestMatcher;
|
||||
import org.springframework.security.web.util.RegexRequestMatcher;
|
||||
import org.springframework.security.web.util.RequestMatcher;
|
||||
|
||||
import spock.lang.Specification;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
class AbstractRequestMatcherMappingConfigurerTests extends Specification {
|
||||
ConcreteAbstractRequestMatcherMappingConfigurer registry = new ConcreteAbstractRequestMatcherMappingConfigurer()
|
||||
|
||||
def "regexMatchers(GET,'/a.*') uses RegexRequestMatcher"() {
|
||||
when:
|
||||
def matchers = registry.regexMatchers(HttpMethod.GET,"/a.*")
|
||||
then: 'matcher is a RegexRequestMatcher'
|
||||
matchers.collect {it.class } == [RegexRequestMatcher]
|
||||
}
|
||||
|
||||
def "regexMatchers('/a.*') uses RegexRequestMatcher"() {
|
||||
when:
|
||||
def matchers = registry.regexMatchers("/a.*")
|
||||
then: 'matcher is a RegexRequestMatcher'
|
||||
matchers.collect {it.class } == [RegexRequestMatcher]
|
||||
}
|
||||
|
||||
def "antMatchers(GET,'/a.*') uses AntPathRequestMatcher"() {
|
||||
when:
|
||||
def matchers = registry.antMatchers(HttpMethod.GET, "/a.*")
|
||||
then: 'matcher is a RegexRequestMatcher'
|
||||
matchers.collect {it.class } == [AntPathRequestMatcher]
|
||||
}
|
||||
|
||||
def "antMatchers('/a.*') uses AntPathRequestMatcher"() {
|
||||
when:
|
||||
def matchers = registry.antMatchers("/a.*")
|
||||
then: 'matcher is a AntPathRequestMatcher'
|
||||
matchers.collect {it.class } == [AntPathRequestMatcher]
|
||||
}
|
||||
|
||||
static class ConcreteAbstractRequestMatcherMappingConfigurer extends AbstractRequestMatcherMappingConfigurer<HttpSecurity,List<RequestMatcher>,DefaultSecurityFilterChain> {
|
||||
List<AccessDecisionVoter> decisionVoters() {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<RequestMatcher> chainRequestMatchersInternal(List<RequestMatcher> requestMatchers) {
|
||||
return requestMatchers;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web.configurers
|
||||
|
||||
import org.springframework.security.config.annotation.AnyObjectPostProcessor
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.web.access.channel.ChannelDecisionManagerImpl
|
||||
import org.springframework.security.web.access.channel.ChannelProcessingFilter
|
||||
import org.springframework.security.web.access.channel.InsecureChannelProcessor
|
||||
import org.springframework.security.web.access.channel.SecureChannelProcessor
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
class ChannelSecurityConfigurerTests extends BaseSpringSpec {
|
||||
|
||||
def "requiresChannel ObjectPostProcessor"() {
|
||||
setup: "initialize the AUTH_FILTER as a mock"
|
||||
AnyObjectPostProcessor objectPostProcessor = Mock()
|
||||
when:
|
||||
HttpSecurity http = new HttpSecurity(objectPostProcessor, authenticationBldr, [:])
|
||||
http
|
||||
.requiresChannel()
|
||||
.anyRequest().requiresSecure()
|
||||
.and()
|
||||
.build()
|
||||
|
||||
then: "InsecureChannelProcessor is registered with LifecycleManager"
|
||||
1 * objectPostProcessor.postProcess(_ as InsecureChannelProcessor) >> {InsecureChannelProcessor o -> o}
|
||||
and: "SecureChannelProcessor is registered with LifecycleManager"
|
||||
1 * objectPostProcessor.postProcess(_ as SecureChannelProcessor) >> {SecureChannelProcessor o -> o}
|
||||
and: "ChannelDecisionManagerImpl is registered with LifecycleManager"
|
||||
1 * objectPostProcessor.postProcess(_ as ChannelDecisionManagerImpl) >> {ChannelDecisionManagerImpl o -> o}
|
||||
and: "ChannelProcessingFilter is registered with LifecycleManager"
|
||||
1 * objectPostProcessor.postProcess(_ as ChannelProcessingFilter) >> {ChannelProcessingFilter o -> o}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web.configurers
|
||||
|
||||
import org.springframework.beans.factory.BeanCreationException
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.mock.web.MockFilterChain
|
||||
import org.springframework.mock.web.MockHttpServletRequest
|
||||
import org.springframework.mock.web.MockHttpServletResponse
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.web.WebSecurityConfigurer;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.builders.WebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.BaseWebConfig;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.web.DefaultSecurityFilterChain
|
||||
import org.springframework.security.web.FilterChainProxy
|
||||
import org.springframework.security.web.access.ExceptionTranslationFilter
|
||||
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor
|
||||
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
|
||||
import org.springframework.security.web.authentication.logout.LogoutFilter
|
||||
import org.springframework.security.web.context.SecurityContextPersistenceFilter
|
||||
import org.springframework.security.web.savedrequest.RequestCacheAwareFilter
|
||||
import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter
|
||||
import org.springframework.security.web.session.SessionManagementFilter;
|
||||
import org.springframework.security.web.util.AnyRequestMatcher
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
class DefaultFiltersTests extends BaseSpringSpec {
|
||||
def missingConfigMessage = "At least one non-null instance of "+ WebSecurityConfigurer.class.getSimpleName()+" must be exposed as a @Bean when using @EnableWebSecurity. Hint try extending "+ WebSecurityConfigurerAdapter.class.getSimpleName()
|
||||
|
||||
def "DefaultSecurityFilterChainBuilder cannot be null"() {
|
||||
when:
|
||||
context = new AnnotationConfigApplicationContext(FilterChainProxyBuilderMissingConfig)
|
||||
then:
|
||||
BeanCreationException e = thrown()
|
||||
e.message.contains missingConfigMessage
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
static class FilterChainProxyBuilderMissingConfig { }
|
||||
|
||||
def "FilterChainProxyBuilder no DefaultSecurityFilterChainBuilder specified"() {
|
||||
when:
|
||||
context = new AnnotationConfigApplicationContext(FilterChainProxyBuilderNoSecurityFilterBuildersConfig)
|
||||
then:
|
||||
BeanCreationException e = thrown()
|
||||
e.message.contains missingConfigMessage
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
static class FilterChainProxyBuilderNoSecurityFilterBuildersConfig {
|
||||
@Bean
|
||||
public WebSecurity filterChainProxyBuilder() {
|
||||
new WebSecurity()
|
||||
.ignoring()
|
||||
.antMatchers("/resources/**")
|
||||
}
|
||||
}
|
||||
|
||||
def "null WebInvocationPrivilegeEvaluator"() {
|
||||
when:
|
||||
context = new AnnotationConfigApplicationContext(NullWebInvocationPrivilegeEvaluatorConfig)
|
||||
then:
|
||||
List<DefaultSecurityFilterChain> filterChains = context.getBean(FilterChainProxy).filterChains
|
||||
filterChains.size() == 1
|
||||
filterChains[0].requestMatcher instanceof AnyRequestMatcher
|
||||
filterChains[0].filters.size() == 1
|
||||
filterChains[0].filters.find { it instanceof UsernamePasswordAuthenticationFilter }
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
static class NullWebInvocationPrivilegeEvaluatorConfig extends BaseWebConfig {
|
||||
NullWebInvocationPrivilegeEvaluatorConfig() {
|
||||
super(true)
|
||||
}
|
||||
|
||||
protected void configure(HttpSecurity http) {
|
||||
http.formLogin()
|
||||
}
|
||||
}
|
||||
|
||||
def "FilterChainProxyBuilder ignoring resources"() {
|
||||
when:
|
||||
context = new AnnotationConfigApplicationContext(FilterChainProxyBuilderIgnoringConfig)
|
||||
then:
|
||||
List<DefaultSecurityFilterChain> filterChains = context.getBean(FilterChainProxy).filterChains
|
||||
filterChains.size() == 2
|
||||
filterChains[0].requestMatcher.pattern == '/resources/**'
|
||||
filterChains[0].filters.empty
|
||||
filterChains[1].requestMatcher instanceof AnyRequestMatcher
|
||||
filterChains[1].filters.collect { it.class } ==
|
||||
[SecurityContextPersistenceFilter, LogoutFilter, RequestCacheAwareFilter,
|
||||
SecurityContextHolderAwareRequestFilter, AnonymousAuthenticationFilter, SessionManagementFilter,
|
||||
ExceptionTranslationFilter, FilterSecurityInterceptor ]
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
static class FilterChainProxyBuilderIgnoringConfig extends BaseWebConfig {
|
||||
@Override
|
||||
public void configure(WebSecurity builder) throws Exception {
|
||||
builder
|
||||
.ignoring()
|
||||
.antMatchers("/resources/**");
|
||||
}
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("USER");
|
||||
}
|
||||
}
|
||||
|
||||
def "DefaultFilters.permitAll()"() {
|
||||
when:
|
||||
context = new AnnotationConfigApplicationContext(DefaultFiltersConfigPermitAll)
|
||||
then:
|
||||
FilterChainProxy filterChain = context.getBean(FilterChainProxy)
|
||||
|
||||
expect:
|
||||
MockHttpServletResponse response = new MockHttpServletResponse()
|
||||
filterChain.doFilter(new MockHttpServletRequest(servletPath : uri, queryString: query), response, new MockFilterChain())
|
||||
response.redirectedUrl == null
|
||||
where:
|
||||
uri | query
|
||||
"/logout" | null
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
static class DefaultFiltersConfigPermitAll extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,343 @@
|
||||
|
||||
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web.configurers
|
||||
|
||||
import javax.servlet.http.HttpSession
|
||||
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.mock.web.MockFilterChain
|
||||
import org.springframework.mock.web.MockHttpServletRequest
|
||||
import org.springframework.mock.web.MockHttpServletResponse
|
||||
import org.springframework.security.config.annotation.AnyObjectPostProcessor
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.BaseWebConfig;
|
||||
import org.springframework.security.config.annotation.web.configurers.DefaultLoginPageConfigurer;
|
||||
import org.springframework.security.web.FilterChainProxy
|
||||
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
|
||||
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler
|
||||
import org.springframework.security.web.authentication.ui.DefaultLoginPageViewFilter;
|
||||
|
||||
/**
|
||||
* Tests to verify that {@link DefaultLoginPageConfigurer} works
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
public class DefaultLoginPageConfigurerTests extends BaseSpringSpec {
|
||||
FilterChainProxy springSecurityFilterChain
|
||||
MockHttpServletRequest request
|
||||
MockHttpServletResponse response
|
||||
MockFilterChain chain
|
||||
|
||||
def setup() {
|
||||
request = new MockHttpServletRequest(method:"GET")
|
||||
response = new MockHttpServletResponse()
|
||||
chain = new MockFilterChain()
|
||||
}
|
||||
|
||||
def "http/form-login default login generating page"() {
|
||||
setup:
|
||||
loadConfig(DefaultLoginPageConfig)
|
||||
springSecurityFilterChain = context.getBean(FilterChainProxy)
|
||||
when:
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
findFilter(DefaultLoginPageViewFilter)
|
||||
response.getRedirectedUrl() == "http://localhost/login"
|
||||
when: "request the login page"
|
||||
setup()
|
||||
request.requestURI = "/login"
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.getContentAsString() == """<html><head><title>Login Page</title></head><body onload='document.f.username.focus();'>
|
||||
<h3>Login with Username and Password</h3><form name='f' action='/login' method='POST'>
|
||||
<table>
|
||||
<tr><td>User:</td><td><input type='text' name='username' value=''></td></tr>
|
||||
<tr><td>Password:</td><td><input type='password' name='password'/></td></tr>
|
||||
<tr><td colspan='2'><input name="submit" type="submit" value="Login"/></td></tr>
|
||||
</table>
|
||||
</form></body></html>"""
|
||||
when: "fail to log in"
|
||||
setup()
|
||||
request.requestURI = "/login"
|
||||
request.method = "POST"
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then: "sent to login error page"
|
||||
response.getRedirectedUrl() == "/login?error"
|
||||
when: "request the error page"
|
||||
HttpSession session = request.session
|
||||
setup()
|
||||
request.session = session
|
||||
request.requestURI = "/login"
|
||||
request.queryString = "error"
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.getContentAsString() == """<html><head><title>Login Page</title></head><body onload='document.f.username.focus();'>
|
||||
<p><font color='red'>Your login attempt was not successful, try again.<br/><br/>Reason: Bad credentials</font></p><h3>Login with Username and Password</h3><form name='f' action='/login' method='POST'>
|
||||
<table>
|
||||
<tr><td>User:</td><td><input type='text' name='username' value=''></td></tr>
|
||||
<tr><td>Password:</td><td><input type='password' name='password'/></td></tr>
|
||||
<tr><td colspan='2'><input name="submit" type="submit" value="Login"/></td></tr>
|
||||
</table>
|
||||
</form></body></html>"""
|
||||
when: "login success"
|
||||
setup()
|
||||
request.requestURI = "/login"
|
||||
request.method = "POST"
|
||||
request.parameters.username = ["user"] as String[]
|
||||
request.parameters.password = ["password"] as String[]
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then: "sent to default succes page"
|
||||
response.getRedirectedUrl() == "/"
|
||||
}
|
||||
|
||||
def "logout success renders"() {
|
||||
setup:
|
||||
loadConfig(DefaultLoginPageConfig)
|
||||
springSecurityFilterChain = context.getBean(FilterChainProxy)
|
||||
when: "logout success"
|
||||
request.requestURI = "/login"
|
||||
request.queryString = "logout"
|
||||
request.method = "GET"
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then: "sent to default success page"
|
||||
response.getContentAsString() == """<html><head><title>Login Page</title></head><body onload='document.f.username.focus();'>
|
||||
<p><font color='green'>You have been logged out</font></p><h3>Login with Username and Password</h3><form name='f' action='/login' method='POST'>
|
||||
<table>
|
||||
<tr><td>User:</td><td><input type='text' name='username' value=''></td></tr>
|
||||
<tr><td>Password:</td><td><input type='password' name='password'/></td></tr>
|
||||
<tr><td colspan='2'><input name="submit" type="submit" value="Login"/></td></tr>
|
||||
</table>
|
||||
</form></body></html>"""
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class DefaultLoginPageConfig extends BaseWebConfig {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("USER")
|
||||
.and()
|
||||
.formLogin()
|
||||
}
|
||||
}
|
||||
|
||||
def "custom logout success handler prevents rendering"() {
|
||||
setup:
|
||||
loadConfig(DefaultLoginPageCustomLogoutSuccessHandlerConfig)
|
||||
springSecurityFilterChain = context.getBean(FilterChainProxy)
|
||||
when: "logout success"
|
||||
request.requestURI = "/login"
|
||||
request.queryString = "logout"
|
||||
request.method = "GET"
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then: "default success page is NOT rendered (application is in charge of it)"
|
||||
response.getContentAsString() == ""
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class DefaultLoginPageCustomLogoutSuccessHandlerConfig extends BaseWebConfig {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("USER")
|
||||
.and()
|
||||
.logout()
|
||||
.logoutSuccessHandler(new SimpleUrlLogoutSuccessHandler())
|
||||
.and()
|
||||
.formLogin()
|
||||
}
|
||||
}
|
||||
|
||||
def "custom logout success url prevents rendering"() {
|
||||
setup:
|
||||
loadConfig(DefaultLoginPageCustomLogoutConfig)
|
||||
springSecurityFilterChain = context.getBean(FilterChainProxy)
|
||||
when: "logout success"
|
||||
request.requestURI = "/login"
|
||||
request.queryString = "logout"
|
||||
request.method = "GET"
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then: "default success page is NOT rendered (application is in charge of it)"
|
||||
response.getContentAsString() == ""
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class DefaultLoginPageCustomLogoutConfig extends BaseWebConfig {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("USER")
|
||||
.and()
|
||||
.logout()
|
||||
.logoutSuccessUrl("/login?logout")
|
||||
.and()
|
||||
.formLogin()
|
||||
}
|
||||
}
|
||||
|
||||
def "http/form-login default login with remember me"() {
|
||||
setup:
|
||||
loadConfig(DefaultLoginPageWithRememberMeConfig)
|
||||
springSecurityFilterChain = context.getBean(FilterChainProxy)
|
||||
when: "request the login page"
|
||||
setup()
|
||||
request.requestURI = "/login"
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.getContentAsString() == """<html><head><title>Login Page</title></head><body onload='document.f.username.focus();'>
|
||||
<h3>Login with Username and Password</h3><form name='f' action='/login' method='POST'>
|
||||
<table>
|
||||
<tr><td>User:</td><td><input type='text' name='username' value=''></td></tr>
|
||||
<tr><td>Password:</td><td><input type='password' name='password'/></td></tr>
|
||||
<tr><td><input type='checkbox' name='remember-me'/></td><td>Remember me on this computer.</td></tr>
|
||||
<tr><td colspan='2'><input name="submit" type="submit" value="Login"/></td></tr>
|
||||
</table>
|
||||
</form></body></html>"""
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class DefaultLoginPageWithRememberMeConfig extends BaseWebConfig {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("USER")
|
||||
.and()
|
||||
.formLogin()
|
||||
.and()
|
||||
.rememberMe()
|
||||
}
|
||||
}
|
||||
|
||||
def "http/form-login default login with openid"() {
|
||||
setup:
|
||||
loadConfig(DefaultLoginPageWithOpenIDConfig)
|
||||
springSecurityFilterChain = context.getBean(FilterChainProxy)
|
||||
when: "request the login page"
|
||||
request.requestURI = "/login"
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.getContentAsString() == """<html><head><title>Login Page</title></head><h3>Login with OpenID Identity</h3><form name='oidf' action='/login/openid' method='POST'>
|
||||
<table>
|
||||
<tr><td>Identity:</td><td><input type='text' size='30' name='openid_identifier'/></td></tr>
|
||||
<tr><td colspan='2'><input name="submit" type="submit" value="Login"/></td></tr>
|
||||
</table>
|
||||
</form></body></html>"""
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class DefaultLoginPageWithOpenIDConfig extends BaseWebConfig {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("USER")
|
||||
.and()
|
||||
.openidLogin()
|
||||
}
|
||||
}
|
||||
|
||||
def "http/form-login default login with openid, form login, and rememberme"() {
|
||||
setup:
|
||||
loadConfig(DefaultLoginPageWithFormLoginOpenIDRememberMeConfig)
|
||||
springSecurityFilterChain = context.getBean(FilterChainProxy)
|
||||
when: "request the login page"
|
||||
request.requestURI = "/login"
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.getContentAsString() == """<html><head><title>Login Page</title></head><body onload='document.f.username.focus();'>
|
||||
<h3>Login with Username and Password</h3><form name='f' action='/login' method='POST'>
|
||||
<table>
|
||||
<tr><td>User:</td><td><input type='text' name='username' value=''></td></tr>
|
||||
<tr><td>Password:</td><td><input type='password' name='password'/></td></tr>
|
||||
<tr><td><input type='checkbox' name='remember-me'/></td><td>Remember me on this computer.</td></tr>
|
||||
<tr><td colspan='2'><input name="submit" type="submit" value="Login"/></td></tr>
|
||||
</table>
|
||||
</form><h3>Login with OpenID Identity</h3><form name='oidf' action='/login/openid' method='POST'>
|
||||
<table>
|
||||
<tr><td>Identity:</td><td><input type='text' size='30' name='openid_identifier'/></td></tr>
|
||||
<tr><td><input type='checkbox' name='remember-me'></td><td>Remember me on this computer.</td></tr>
|
||||
<tr><td colspan='2'><input name="submit" type="submit" value="Login"/></td></tr>
|
||||
</table>
|
||||
</form></body></html>"""
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class DefaultLoginPageWithFormLoginOpenIDRememberMeConfig extends BaseWebConfig {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("USER")
|
||||
.and()
|
||||
.rememberMe()
|
||||
.and()
|
||||
.formLogin()
|
||||
.and()
|
||||
.openidLogin()
|
||||
}
|
||||
}
|
||||
|
||||
def "default login with custom AuthenticationEntryPoint"() {
|
||||
when:
|
||||
loadConfig(DefaultLoginWithCustomAuthenticationEntryPointConfig)
|
||||
then:
|
||||
!findFilter(DefaultLoginPageViewFilter)
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class DefaultLoginWithCustomAuthenticationEntryPointConfig extends BaseWebConfig {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) {
|
||||
http
|
||||
.exceptionHandling()
|
||||
.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login"))
|
||||
.and()
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("USER")
|
||||
.and()
|
||||
.formLogin()
|
||||
}
|
||||
}
|
||||
|
||||
def "DefaultLoginPage ObjectPostProcessor"() {
|
||||
setup:
|
||||
AnyObjectPostProcessor objectPostProcessor = Mock()
|
||||
when:
|
||||
HttpSecurity http = new HttpSecurity(objectPostProcessor, authenticationBldr, [:])
|
||||
DefaultLoginPageConfigurer defaultLoginConfig = new DefaultLoginPageConfigurer([builder:http])
|
||||
defaultLoginConfig.addObjectPostProcessor(objectPostProcessor)
|
||||
http
|
||||
// must set builder manually due to groovy not selecting correct method
|
||||
.apply(defaultLoginConfig).and()
|
||||
.formLogin()
|
||||
.and()
|
||||
.build()
|
||||
|
||||
then: "DefaultLoginPageGeneratingFilter is registered with LifecycleManager"
|
||||
1 * objectPostProcessor.postProcess(_ as DefaultLoginPageViewFilter) >> {DefaultLoginPageViewFilter o -> o}
|
||||
1 * objectPostProcessor.postProcess(_ as UsernamePasswordAuthenticationFilter) >> {UsernamePasswordAuthenticationFilter o -> o}
|
||||
1 * objectPostProcessor.postProcess(_ as LoginUrlAuthenticationEntryPoint) >> {LoginUrlAuthenticationEntryPoint o -> o}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web.configurers
|
||||
|
||||
import org.springframework.security.config.annotation.AnyObjectPostProcessor
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.web.access.ExceptionTranslationFilter
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
class ExceptionHandlingConfigurerTests extends BaseSpringSpec {
|
||||
|
||||
def "exception ObjectPostProcessor"() {
|
||||
setup: "initialize the AUTH_FILTER as a mock"
|
||||
AnyObjectPostProcessor opp = Mock()
|
||||
when:
|
||||
HttpSecurity http = new HttpSecurity(opp, authenticationBldr, [:])
|
||||
http
|
||||
.exceptionHandling()
|
||||
.and()
|
||||
.build()
|
||||
|
||||
then: "ExceptionTranslationFilter is registered with LifecycleManager"
|
||||
1 * opp.postProcess(_ as ExceptionTranslationFilter) >> {ExceptionTranslationFilter o -> o}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,413 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web.configurers;
|
||||
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.access.vote.AffirmativeBased;
|
||||
import org.springframework.security.authentication.RememberMeAuthenticationToken
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.SecurityExpressions.*
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
|
||||
|
||||
public class ExpressionUrlAuthorizationConfigurerTests extends BaseSpringSpec {
|
||||
|
||||
def "hasAnyAuthority('ROLE_USER')"() {
|
||||
when:
|
||||
def expression = ExpressionUrlAuthorizationConfigurer.hasAnyAuthority("ROLE_USER")
|
||||
then:
|
||||
expression == "hasAnyAuthority('ROLE_USER')"
|
||||
}
|
||||
|
||||
def "hasAnyAuthority('ROLE_USER','ROLE_ADMIN')"() {
|
||||
when:
|
||||
def expression = ExpressionUrlAuthorizationConfigurer.hasAnyAuthority("ROLE_USER","ROLE_ADMIN")
|
||||
then:
|
||||
expression == "hasAnyAuthority('ROLE_USER','ROLE_ADMIN')"
|
||||
}
|
||||
|
||||
def "hasRole('ROLE_USER') is rejected due to starting with ROLE_"() {
|
||||
when:
|
||||
def expression = ExpressionUrlAuthorizationConfigurer.hasRole("ROLE_USER")
|
||||
then:
|
||||
IllegalArgumentException e = thrown()
|
||||
e.message == "role should not start with 'ROLE_' since it is automatically inserted. Got 'ROLE_USER'"
|
||||
}
|
||||
|
||||
def "authorizeUrls() uses AffirmativeBased AccessDecisionManager"() {
|
||||
when: "Load Config with no specific AccessDecisionManager"
|
||||
loadConfig(NoSpecificAccessDecessionManagerConfig)
|
||||
then: "AccessDecessionManager matches the HttpSecurityBuilder's default"
|
||||
findFilter(FilterSecurityInterceptor).accessDecisionManager.class == AffirmativeBased
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class NoSpecificAccessDecessionManagerConfig extends WebSecurityConfigurerAdapter {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("USER")
|
||||
}
|
||||
}
|
||||
|
||||
def "authorizeUrls() no requests"() {
|
||||
when: "Load Config with no requests"
|
||||
loadConfig(NoRequestsConfig)
|
||||
then: "A meaningful exception is thrown"
|
||||
BeanCreationException success = thrown()
|
||||
success.message.contains "At least one mapping is required (i.e. authorizeUrls().anyRequest.authenticated())"
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class NoRequestsConfig extends WebSecurityConfigurerAdapter {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeUrls()
|
||||
}
|
||||
}
|
||||
|
||||
def "authorizeUrls() incomplete mapping"() {
|
||||
when: "Load Config with incomplete mapping"
|
||||
loadConfig(IncompleteMappingConfig)
|
||||
then: "A meaningful exception is thrown"
|
||||
BeanCreationException success = thrown()
|
||||
success.message.contains "An incomplete mapping was found for "
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class IncompleteMappingConfig extends WebSecurityConfigurerAdapter {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.antMatchers("/a").authenticated()
|
||||
.anyRequest()
|
||||
}
|
||||
}
|
||||
|
||||
def "authorizeUrls() hasAuthority"() {
|
||||
setup:
|
||||
loadConfig(HasAuthorityConfig)
|
||||
when:
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.status == 403
|
||||
when:
|
||||
super.setup()
|
||||
login()
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.status == 200
|
||||
when:
|
||||
super.setup()
|
||||
login("user","ROLE_INVALID")
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.status == 403
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class HasAuthorityConfig extends WebSecurityConfigurerAdapter {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.httpBasic()
|
||||
.and()
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasAuthority("ROLE_USER")
|
||||
}
|
||||
}
|
||||
|
||||
def "authorizeUrls() hasAnyAuthority"() {
|
||||
setup:
|
||||
loadConfig(HasAnyAuthorityConfig)
|
||||
when:
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.status == 403
|
||||
when:
|
||||
super.setup()
|
||||
login("user","ROLE_ADMIN")
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.status == 200
|
||||
when:
|
||||
super.setup()
|
||||
login("user","ROLE_DBA")
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.status == 200
|
||||
when:
|
||||
super.setup()
|
||||
login("user","ROLE_INVALID")
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.status == 403
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class HasAnyAuthorityConfig extends WebSecurityConfigurerAdapter {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.httpBasic()
|
||||
.and()
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasAnyAuthority("ROLE_ADMIN","ROLE_DBA")
|
||||
}
|
||||
}
|
||||
|
||||
def "authorizeUrls() hasIpAddress"() {
|
||||
setup:
|
||||
loadConfig(HasIpAddressConfig)
|
||||
when:
|
||||
request.remoteAddr = "192.168.1.1"
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.status == 403
|
||||
when:
|
||||
super.setup()
|
||||
request.remoteAddr = "192.168.1.0"
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.status == 200
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class HasIpAddressConfig extends WebSecurityConfigurerAdapter {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.httpBasic()
|
||||
.and()
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasIpAddress("192.168.1.0")
|
||||
}
|
||||
}
|
||||
|
||||
def "authorizeUrls() anonymous"() {
|
||||
setup:
|
||||
loadConfig(AnonymousConfig)
|
||||
when:
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.status == 200
|
||||
when:
|
||||
super.setup()
|
||||
login()
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.status == 403
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class AnonymousConfig extends WebSecurityConfigurerAdapter {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.httpBasic()
|
||||
.and()
|
||||
.authorizeUrls()
|
||||
.anyRequest().anonymous()
|
||||
}
|
||||
}
|
||||
|
||||
def "authorizeUrls() rememberMe"() {
|
||||
setup:
|
||||
loadConfig(RememberMeConfig)
|
||||
when:
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.status == 403
|
||||
when:
|
||||
super.setup()
|
||||
login(new RememberMeAuthenticationToken("key", "user", AuthorityUtils.createAuthorityList("ROLE_USER")))
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.status == 200
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class RememberMeConfig extends WebSecurityConfigurerAdapter {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.rememberMe()
|
||||
.and()
|
||||
.httpBasic()
|
||||
.and()
|
||||
.authorizeUrls()
|
||||
.anyRequest().rememberMe()
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerAuthentication(AuthenticationManagerBuilder auth)
|
||||
throws Exception {
|
||||
auth
|
||||
.inMemoryAuthentication()
|
||||
.withUser("user").password("password").roles("USER")
|
||||
}
|
||||
}
|
||||
|
||||
def "authorizeUrls() denyAll"() {
|
||||
setup:
|
||||
loadConfig(DenyAllConfig)
|
||||
when:
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.status == 403
|
||||
when:
|
||||
super.setup()
|
||||
login(new RememberMeAuthenticationToken("key", "user", AuthorityUtils.createAuthorityList("ROLE_USER")))
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.status == 403
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class DenyAllConfig extends WebSecurityConfigurerAdapter {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.httpBasic()
|
||||
.and()
|
||||
.authorizeUrls()
|
||||
.anyRequest().denyAll()
|
||||
}
|
||||
}
|
||||
|
||||
def "authorizeUrls() not denyAll"() {
|
||||
setup:
|
||||
loadConfig(NotDenyAllConfig)
|
||||
when:
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.status == 200
|
||||
when:
|
||||
super.setup()
|
||||
login(new RememberMeAuthenticationToken("key", "user", AuthorityUtils.createAuthorityList("ROLE_USER")))
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.status == 200
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class NotDenyAllConfig extends WebSecurityConfigurerAdapter {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.httpBasic()
|
||||
.and()
|
||||
.authorizeUrls()
|
||||
.anyRequest().not().denyAll()
|
||||
}
|
||||
}
|
||||
|
||||
def "authorizeUrls() fullyAuthenticated"() {
|
||||
setup:
|
||||
loadConfig(FullyAuthenticatedConfig)
|
||||
when:
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.status == 403
|
||||
when:
|
||||
super.setup()
|
||||
login(new RememberMeAuthenticationToken("key", "user", AuthorityUtils.createAuthorityList("ROLE_USER")))
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.status == 403
|
||||
when:
|
||||
super.setup()
|
||||
login()
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.status == 200
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class FullyAuthenticatedConfig extends WebSecurityConfigurerAdapter {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.rememberMe()
|
||||
.and()
|
||||
.httpBasic()
|
||||
.and()
|
||||
.authorizeUrls()
|
||||
.anyRequest().fullyAuthenticated()
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerAuthentication(AuthenticationManagerBuilder auth)
|
||||
throws Exception {
|
||||
auth
|
||||
.inMemoryAuthentication()
|
||||
.withUser("user").password("password").roles("USER")
|
||||
}
|
||||
}
|
||||
|
||||
def "authorizeUrls() access"() {
|
||||
setup:
|
||||
loadConfig(AccessConfig)
|
||||
when:
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then: "Access is granted due to GET"
|
||||
response.status == 200
|
||||
when:
|
||||
super.setup()
|
||||
login()
|
||||
request.method = "POST"
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then: "Access is granted due to role"
|
||||
response.status == 200
|
||||
when:
|
||||
super.setup()
|
||||
request.method = "POST"
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then: "Access is denied"
|
||||
response.status == 403
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class AccessConfig extends WebSecurityConfigurerAdapter {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.rememberMe()
|
||||
.and()
|
||||
.httpBasic()
|
||||
.and()
|
||||
.authorizeUrls()
|
||||
.anyRequest().access("hasRole('ROLE_USER') or request.method == 'GET'")
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerAuthentication(AuthenticationManagerBuilder auth)
|
||||
throws Exception {
|
||||
auth
|
||||
.inMemoryAuthentication()
|
||||
.withUser("user").password("password").roles("USER")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web.configurers
|
||||
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.mock.web.MockFilterChain
|
||||
import org.springframework.mock.web.MockHttpServletRequest
|
||||
import org.springframework.mock.web.MockHttpServletResponse
|
||||
import org.springframework.security.authentication.BadCredentialsException
|
||||
import org.springframework.security.config.annotation.AnyObjectPostProcessor
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.annotation.web.builders.WebSecurity
|
||||
import org.springframework.security.config.annotation.web.configuration.BaseWebConfig
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
|
||||
import org.springframework.security.web.AuthenticationEntryPoint
|
||||
import org.springframework.security.web.FilterChainProxy
|
||||
import org.springframework.security.web.PortMapper
|
||||
import org.springframework.security.web.access.ExceptionTranslationFilter
|
||||
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor
|
||||
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter
|
||||
import org.springframework.security.web.authentication.AuthenticationFailureHandler
|
||||
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
|
||||
import org.springframework.security.web.authentication.logout.LogoutFilter
|
||||
import org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy
|
||||
import org.springframework.security.web.context.SecurityContextPersistenceFilter
|
||||
import org.springframework.security.web.savedrequest.RequestCacheAwareFilter
|
||||
import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter
|
||||
import org.springframework.security.web.session.SessionManagementFilter
|
||||
import org.springframework.security.web.util.AnyRequestMatcher
|
||||
import org.springframework.test.util.ReflectionTestUtils
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
class FormLoginConfigurerTests extends BaseSpringSpec {
|
||||
def "Form Login"() {
|
||||
when: "load formLogin()"
|
||||
context = new AnnotationConfigApplicationContext(FormLoginConfig)
|
||||
|
||||
then: "FilterChains configured correctly"
|
||||
def filterChains = filterChains()
|
||||
filterChains.size() == 2
|
||||
filterChains[0].requestMatcher.pattern == '/resources/**'
|
||||
filterChains[0].filters.empty
|
||||
filterChains[1].requestMatcher instanceof AnyRequestMatcher
|
||||
filterChains[1].filters.collect { it.class.name.contains('$') ? it.class.superclass : it.class } ==
|
||||
[SecurityContextPersistenceFilter, LogoutFilter, UsernamePasswordAuthenticationFilter,
|
||||
RequestCacheAwareFilter, SecurityContextHolderAwareRequestFilter,
|
||||
AnonymousAuthenticationFilter, SessionManagementFilter, ExceptionTranslationFilter, FilterSecurityInterceptor ]
|
||||
|
||||
and: "UsernamePasswordAuthentictionFilter is configured correctly"
|
||||
UsernamePasswordAuthenticationFilter authFilter = findFilter(UsernamePasswordAuthenticationFilter,1)
|
||||
authFilter.usernameParameter == "username"
|
||||
authFilter.passwordParameter == "password"
|
||||
authFilter.failureHandler.defaultFailureUrl == "/login?error"
|
||||
authFilter.successHandler.defaultTargetUrl == "/"
|
||||
authFilter.requiresAuthentication(new MockHttpServletRequest(requestURI : "/login", method: "POST"), new MockHttpServletResponse())
|
||||
!authFilter.requiresAuthentication(new MockHttpServletRequest(requestURI : "/login", method: "GET"), new MockHttpServletResponse())
|
||||
|
||||
and: "SessionFixationProtectionStrategy is configured correctly"
|
||||
SessionFixationProtectionStrategy sessionStrategy = ReflectionTestUtils.getField(authFilter,"sessionStrategy")
|
||||
sessionStrategy.migrateSessionAttributes
|
||||
|
||||
and: "Exception handling is configured correctly"
|
||||
AuthenticationEntryPoint authEntryPoint = filterChains[1].filters.find { it instanceof ExceptionTranslationFilter}.authenticationEntryPoint
|
||||
MockHttpServletResponse response = new MockHttpServletResponse()
|
||||
authEntryPoint.commence(new MockHttpServletRequest(requestURI: "/private/"), response, new BadCredentialsException(""))
|
||||
response.redirectedUrl == "http://localhost/login"
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
static class FormLoginConfig extends BaseWebConfig {
|
||||
@Override
|
||||
public void configure(WebSecurity web) throws Exception {
|
||||
web
|
||||
.ignoring()
|
||||
.antMatchers("/resources/**");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("USER")
|
||||
.and()
|
||||
.formLogin()
|
||||
.loginUrl("/login")
|
||||
}
|
||||
}
|
||||
|
||||
def "FormLogin.permitAll()"() {
|
||||
when: "load formLogin() with permitAll"
|
||||
context = new AnnotationConfigApplicationContext(FormLoginConfigPermitAll)
|
||||
|
||||
then: "the formLogin URLs are granted access"
|
||||
FilterChainProxy filterChain = context.getBean(FilterChainProxy)
|
||||
MockHttpServletResponse response = new MockHttpServletResponse()
|
||||
filterChain.doFilter(new MockHttpServletRequest(servletPath : servletPath, requestURI: servletPath, queryString: query, method: method), response, new MockFilterChain())
|
||||
response.redirectedUrl == redirectUrl
|
||||
|
||||
where:
|
||||
servletPath | method | query | redirectUrl
|
||||
"/login" | "GET" | null | null
|
||||
"/login" | "POST" | null | "/login?error"
|
||||
"/login" | "GET" | "error" | null
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
static class FormLoginConfigPermitAll extends BaseWebConfig {
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("USER")
|
||||
.and()
|
||||
.formLogin()
|
||||
.permitAll()
|
||||
}
|
||||
}
|
||||
|
||||
def "FormLogin uses PortMapper"() {
|
||||
when: "load formLogin() with permitAll"
|
||||
FormLoginUsesPortMapperConfig.PORT_MAPPER = Mock(PortMapper)
|
||||
loadConfig(FormLoginUsesPortMapperConfig)
|
||||
then: "the formLogin URLs are granted access"
|
||||
findFilter(ExceptionTranslationFilter).authenticationEntryPoint.portMapper == FormLoginUsesPortMapperConfig.PORT_MAPPER
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
static class FormLoginUsesPortMapperConfig extends BaseWebConfig {
|
||||
static PortMapper PORT_MAPPER
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("USER")
|
||||
.and()
|
||||
.formLogin()
|
||||
.permitAll()
|
||||
.and()
|
||||
.portMapper()
|
||||
.portMapper(PORT_MAPPER)
|
||||
}
|
||||
}
|
||||
|
||||
def "FormLogin permitAll ignores failureUrl when failureHandler set"() {
|
||||
setup:
|
||||
PermitAllIgnoresFailureHandlerConfig.FAILURE_HANDLER = Mock(AuthenticationFailureHandler)
|
||||
loadConfig(PermitAllIgnoresFailureHandlerConfig)
|
||||
FilterChainProxy springSecurityFilterChain = context.getBean(FilterChainProxy)
|
||||
when: "access default failureUrl and configured explicit FailureHandler"
|
||||
MockHttpServletRequest request = new MockHttpServletRequest(requestURI:"/login",queryString:"error")
|
||||
MockHttpServletResponse response = new MockHttpServletResponse()
|
||||
springSecurityFilterChain.doFilter(request,response,new MockFilterChain())
|
||||
then: "access is not granted to the failure handler (sent to login page)"
|
||||
response.status == 302
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class PermitAllIgnoresFailureHandlerConfig extends BaseWebConfig {
|
||||
static AuthenticationFailureHandler FAILURE_HANDLER
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("USER")
|
||||
.and()
|
||||
.formLogin()
|
||||
.failureHandler(FAILURE_HANDLER)
|
||||
.permitAll()
|
||||
}
|
||||
}
|
||||
|
||||
def "formLogin ObjectPostProcessor"() {
|
||||
setup: "initialize the AUTH_FILTER as a mock"
|
||||
AnyObjectPostProcessor opp = Mock()
|
||||
HttpSecurity http = new HttpSecurity(opp, authenticationBldr, [:])
|
||||
when:
|
||||
http
|
||||
.formLogin()
|
||||
.and()
|
||||
.build()
|
||||
|
||||
then: "UsernamePasswordAuthenticationFilter is registered with LifecycleManager"
|
||||
1 * opp.postProcess(_ as UsernamePasswordAuthenticationFilter) >> {UsernamePasswordAuthenticationFilter o -> o}
|
||||
and: "LoginUrlAuthenticationEntryPoint is registered with LifecycleManager"
|
||||
1 * opp.postProcess(_ as LoginUrlAuthenticationEntryPoint) >> {LoginUrlAuthenticationEntryPoint o -> o}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web.configurers
|
||||
|
||||
import org.springframework.security.config.annotation.AnyObjectPostProcessor
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
class HttpBasicConfigurerTests extends BaseSpringSpec {
|
||||
|
||||
def "httBasic ObjectPostProcessor"() {
|
||||
setup:
|
||||
AnyObjectPostProcessor opp = Mock()
|
||||
HttpSecurity http = new HttpSecurity(opp, authenticationBldr, [:])
|
||||
when:
|
||||
http
|
||||
.httpBasic()
|
||||
.and()
|
||||
.build()
|
||||
|
||||
then: "ExceptionTranslationFilter is registered with LifecycleManager"
|
||||
1 * opp.postProcess(_ as BasicAuthenticationFilter) >> {BasicAuthenticationFilter o -> o}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web.configurers
|
||||
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.security.authentication.AuthenticationManager
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.core.Authentication
|
||||
import org.springframework.security.core.AuthenticationException
|
||||
import org.springframework.security.web.FilterChainProxy
|
||||
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor
|
||||
import org.springframework.stereotype.Component
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
class Issue55Tests extends BaseSpringSpec {
|
||||
|
||||
def "WebSecurityConfigurerAdapter defaults to @Autowired"() {
|
||||
when:
|
||||
loadConfig(WebSecurityConfigurerAdapterDefaultsAuthManagerConfig)
|
||||
then:
|
||||
context.getBean(FilterChainProxy)
|
||||
findFilter(FilterSecurityInterceptor).authenticationManager.parent.class == CustomAuthenticationManager
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
static class WebSecurityConfigurerAdapterDefaultsAuthManagerConfig {
|
||||
@Component
|
||||
public static class WebSecurityAdapter extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("USER");
|
||||
}
|
||||
}
|
||||
@Configuration
|
||||
public static class AuthenticationManagerConfiguration {
|
||||
@Bean
|
||||
public AuthenticationManager authenticationManager() throws Exception {
|
||||
return new CustomAuthenticationManager();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def "multi http WebSecurityConfigurerAdapter defaults to @Autowired"() {
|
||||
when:
|
||||
loadConfig(MultiWebSecurityConfigurerAdapterDefaultsAuthManagerConfig)
|
||||
then:
|
||||
context.getBean(FilterChainProxy)
|
||||
findFilter(FilterSecurityInterceptor).authenticationManager.parent.class == CustomAuthenticationManager
|
||||
findFilter(FilterSecurityInterceptor,1).authenticationManager.parent.class == CustomAuthenticationManager
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
static class MultiWebSecurityConfigurerAdapterDefaultsAuthManagerConfig {
|
||||
@Component
|
||||
@Order(1)
|
||||
public static class ApiWebSecurityAdapter extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.antMatcher("/api/**")
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("USER");
|
||||
}
|
||||
}
|
||||
@Component
|
||||
public static class WebSecurityAdapter extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("USER");
|
||||
}
|
||||
}
|
||||
@Configuration
|
||||
public static class AuthenticationManagerConfiguration {
|
||||
@Bean
|
||||
public AuthenticationManager authenticationManager() throws Exception {
|
||||
return new CustomAuthenticationManager();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class CustomAuthenticationManager implements AuthenticationManager {
|
||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web.configurers
|
||||
|
||||
import org.springframework.security.config.annotation.AnyObjectPostProcessor
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.web.authentication.preauth.j2ee.J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource
|
||||
import org.springframework.security.web.authentication.preauth.j2ee.J2eePreAuthenticatedProcessingFilter
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
class JeeConfigurerTests extends BaseSpringSpec {
|
||||
|
||||
def "jee ObjectPostProcessor"() {
|
||||
setup:
|
||||
AnyObjectPostProcessor opp = Mock()
|
||||
HttpSecurity http = new HttpSecurity(opp, authenticationBldr, [:])
|
||||
when:
|
||||
http
|
||||
.jee()
|
||||
.and()
|
||||
.build()
|
||||
|
||||
then: "J2eePreAuthenticatedProcessingFilter is registered with LifecycleManager"
|
||||
1 * opp.postProcess(_ as J2eePreAuthenticatedProcessingFilter) >> {J2eePreAuthenticatedProcessingFilter o -> o}
|
||||
and: "J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource is registered with LifecycleManager"
|
||||
1 * opp.postProcess(_ as J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource) >> {J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource o -> o}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web.configurers
|
||||
|
||||
import org.springframework.security.config.annotation.AnyObjectPostProcessor
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.web.authentication.logout.LogoutFilter
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
class LogoutConfigurerTests extends BaseSpringSpec {
|
||||
|
||||
def "logout ObjectPostProcessor"() {
|
||||
setup:
|
||||
AnyObjectPostProcessor opp = Mock()
|
||||
HttpSecurity http = new HttpSecurity(opp, authenticationBldr, [:])
|
||||
when:
|
||||
http
|
||||
.logout()
|
||||
.and()
|
||||
.build()
|
||||
|
||||
then: "LogoutFilter is registered with LifecycleManager"
|
||||
1 * opp.postProcess(_ as LogoutFilter) >> {LogoutFilter o -> o}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web.configurers;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.security.access.AccessDecisionManager
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.access.ConfigAttribute
|
||||
import org.springframework.security.authentication.AnonymousAuthenticationToken
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.web.builders.DebugFilter;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint
|
||||
import org.springframework.security.web.FilterInvocation
|
||||
import org.springframework.security.web.access.AccessDeniedHandler;
|
||||
import org.springframework.security.web.access.AccessDeniedHandlerImpl;
|
||||
import org.springframework.security.web.access.ExceptionTranslationFilter
|
||||
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor
|
||||
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
|
||||
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
|
||||
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
|
||||
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
|
||||
import org.springframework.security.web.context.NullSecurityContextRepository;
|
||||
import org.springframework.security.web.context.SecurityContextPersistenceFilter
|
||||
import org.springframework.security.web.jaasapi.JaasApiIntegrationFilter;
|
||||
import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter;
|
||||
import org.springframework.security.web.util.AntPathRequestMatcher
|
||||
import org.springframework.security.web.util.AnyRequestMatcher;
|
||||
import org.springframework.security.web.util.RequestMatcher
|
||||
|
||||
import spock.lang.Ignore;
|
||||
|
||||
/**
|
||||
* Tests to verify that all the functionality of <anonymous> attributes is present
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
public class NamespaceDebugTests extends BaseSpringSpec {
|
||||
def "debug=true"() {
|
||||
when: "Load configuraiton with debug enabled"
|
||||
loadConfig(DebugWebSecurity)
|
||||
then: "The DebugFilter is present"
|
||||
context.getBean("springSecurityFilterChain").class == DebugFilter
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity(debug=true)
|
||||
static class DebugWebSecurity extends WebSecurityConfigurerAdapter {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web.configurers;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.BaseWebConfig;
|
||||
import org.springframework.security.web.access.AccessDeniedHandler;
|
||||
import org.springframework.security.web.access.ExceptionTranslationFilter;
|
||||
|
||||
/**
|
||||
* Tests to verify that all the functionality of <access-denied-handler> attributes is present
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
public class NamespaceHttpAccessDeniedHandlerTests extends BaseSpringSpec {
|
||||
def "http/access-denied-handler@error-page"() {
|
||||
when:
|
||||
loadConfig(AccessDeniedPageConfig)
|
||||
then:
|
||||
findFilter(ExceptionTranslationFilter).accessDeniedHandler.errorPage == "/AccessDeniedPageConfig"
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class AccessDeniedPageConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) {
|
||||
http.
|
||||
exceptionHandling()
|
||||
.accessDeniedPage("/AccessDeniedPageConfig")
|
||||
}
|
||||
}
|
||||
|
||||
def "http/access-denied-handler@ref"() {
|
||||
when:
|
||||
loadConfig(AccessDeniedHandlerRefConfig)
|
||||
then:
|
||||
findFilter(ExceptionTranslationFilter).accessDeniedHandler.class == AccessDeniedHandlerRefConfig.CustomAccessDeniedHandler
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class AccessDeniedHandlerRefConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) {
|
||||
CustomAccessDeniedHandler accessDeniedHandler = new CustomAccessDeniedHandler()
|
||||
http.
|
||||
exceptionHandling()
|
||||
.accessDeniedHandler(accessDeniedHandler)
|
||||
}
|
||||
|
||||
static class CustomAccessDeniedHandler implements AccessDeniedHandler {
|
||||
public void handle(HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
AccessDeniedException accessDeniedException)
|
||||
throws IOException, ServletException {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web.configurers;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.authentication.AnonymousAuthenticationToken
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.BaseWebConfig;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
|
||||
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
|
||||
|
||||
/**
|
||||
* Tests to verify that all the functionality of <anonymous> attributes is present
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
public class NamespaceHttpAnonymousTests extends BaseSpringSpec {
|
||||
def "http/anonymous@enabled = true (default)"() {
|
||||
when:
|
||||
loadConfig(AnonymousConfig)
|
||||
then:
|
||||
def filter = findFilter(AnonymousAuthenticationFilter)
|
||||
filter != null
|
||||
def authManager = findFilter(FilterSecurityInterceptor).authenticationManager
|
||||
authManager.authenticate(new AnonymousAuthenticationToken(filter.key, filter.principal, filter.authorities)).authenticated
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class AnonymousConfig extends BaseWebConfig {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("USER");
|
||||
}
|
||||
}
|
||||
|
||||
def "http/anonymous@enabled = false"() {
|
||||
when:
|
||||
loadConfig(AnonymousDisabledConfig)
|
||||
then:
|
||||
findFilter(AnonymousAuthenticationFilter) == null
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class AnonymousDisabledConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) {
|
||||
http.anonymous().disable()
|
||||
}
|
||||
}
|
||||
|
||||
def "http/anonymous@granted-authority"() {
|
||||
when:
|
||||
loadConfig(AnonymousGrantedAuthorityConfig)
|
||||
then:
|
||||
findFilter(AnonymousAuthenticationFilter).authorities == AuthorityUtils.createAuthorityList("ROLE_ANON")
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class AnonymousGrantedAuthorityConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) {
|
||||
http
|
||||
.anonymous()
|
||||
.authorities("ROLE_ANON")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def "http/anonymous@key"() {
|
||||
when:
|
||||
loadConfig(AnonymousKeyConfig)
|
||||
then:
|
||||
def filter = findFilter(AnonymousAuthenticationFilter)
|
||||
filter != null
|
||||
filter.key == "AnonymousKeyConfig"
|
||||
def authManager = findFilter(FilterSecurityInterceptor).authenticationManager
|
||||
authManager.authenticate(new AnonymousAuthenticationToken(filter.key, filter.principal, filter.authorities)).authenticated
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class AnonymousKeyConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("USER")
|
||||
.and()
|
||||
.anonymous().key("AnonymousKeyConfig")
|
||||
}
|
||||
}
|
||||
|
||||
def "http/anonymous@username"() {
|
||||
when:
|
||||
loadConfig(AnonymousUsernameConfig)
|
||||
then:
|
||||
def filter = findFilter(AnonymousAuthenticationFilter)
|
||||
filter != null
|
||||
filter.principal == "AnonymousUsernameConfig"
|
||||
def authManager = findFilter(FilterSecurityInterceptor).authenticationManager
|
||||
authManager.authenticate(new AnonymousAuthenticationToken(filter.key, filter.principal, filter.authorities)).principal == "AnonymousUsernameConfig"
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class AnonymousUsernameConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("USER")
|
||||
.and()
|
||||
.anonymous().principal("AnonymousUsernameConfig")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web.configurers;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.mock.web.MockFilterChain;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.BaseWebConfig;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.security.web.FilterChainProxy;
|
||||
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
|
||||
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
|
||||
|
||||
/**
|
||||
* Tests to verify that all the functionality of <http-basic> attributes is present
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
public class NamespaceHttpBasicTests extends BaseSpringSpec {
|
||||
FilterChainProxy springSecurityFilterChain
|
||||
MockHttpServletRequest request
|
||||
MockHttpServletResponse response
|
||||
MockFilterChain chain
|
||||
|
||||
def setup() {
|
||||
request = new MockHttpServletRequest()
|
||||
response = new MockHttpServletResponse()
|
||||
chain = new MockFilterChain()
|
||||
}
|
||||
|
||||
def "http/http-basic"() {
|
||||
setup:
|
||||
loadConfig(HttpBasicConfig)
|
||||
springSecurityFilterChain = context.getBean(FilterChainProxy)
|
||||
when:
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.status == HttpServletResponse.SC_FORBIDDEN
|
||||
when: "fail to log in"
|
||||
setup()
|
||||
login("user","invalid")
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then: "unauthorized"
|
||||
response.status == HttpServletResponse.SC_UNAUTHORIZED
|
||||
response.getHeader("WWW-Authenticate") == 'Basic realm="Spring Security Application"'
|
||||
when: "login success"
|
||||
setup()
|
||||
login()
|
||||
then: "sent to default succes page"
|
||||
!response.committed
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class HttpBasicConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("USER")
|
||||
.and()
|
||||
.httpBasic();
|
||||
}
|
||||
}
|
||||
|
||||
def "http@realm"() {
|
||||
setup:
|
||||
loadConfig(CustomHttpBasicConfig)
|
||||
springSecurityFilterChain = context.getBean(FilterChainProxy)
|
||||
when:
|
||||
login("user","invalid")
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then: "unauthorized"
|
||||
response.status == HttpServletResponse.SC_UNAUTHORIZED
|
||||
response.getHeader("WWW-Authenticate") == 'Basic realm="Custom Realm"'
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class CustomHttpBasicConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("USER")
|
||||
.and()
|
||||
.httpBasic().realmName("Custom Realm");
|
||||
}
|
||||
}
|
||||
|
||||
def "http-basic@authentication-details-source-ref"() {
|
||||
when:
|
||||
loadConfig(AuthenticationDetailsSourceHttpBasicConfig)
|
||||
springSecurityFilterChain = context.getBean(FilterChainProxy)
|
||||
then:
|
||||
findFilter(BasicAuthenticationFilter).authenticationDetailsSource.class == CustomAuthenticationDetailsSource
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class AuthenticationDetailsSourceHttpBasicConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) {
|
||||
http
|
||||
.httpBasic()
|
||||
.authenticationDetailsSource(new CustomAuthenticationDetailsSource())
|
||||
}
|
||||
}
|
||||
|
||||
static class CustomAuthenticationDetailsSource extends WebAuthenticationDetailsSource {}
|
||||
|
||||
def "http-basic@entry-point-ref"() {
|
||||
setup:
|
||||
loadConfig(EntryPointRefHttpBasicConfig)
|
||||
springSecurityFilterChain = context.getBean(FilterChainProxy)
|
||||
when:
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.status == HttpServletResponse.SC_FORBIDDEN
|
||||
when: "fail to log in"
|
||||
setup()
|
||||
login("user","invalid")
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then: "custom"
|
||||
response.status == HttpServletResponse.SC_INTERNAL_SERVER_ERROR
|
||||
when: "login success"
|
||||
setup()
|
||||
login()
|
||||
then: "sent to default succes page"
|
||||
!response.committed
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class EntryPointRefHttpBasicConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("USER")
|
||||
.and()
|
||||
.httpBasic()
|
||||
.authenticationEntryPoint(new AuthenticationEntryPoint() {
|
||||
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) {
|
||||
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
def login(String username="user",String password="password") {
|
||||
def credentials = username + ":" + password
|
||||
request.addHeader("Authorization", "Basic " + credentials.bytes.encodeBase64())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web.configurers;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.security.access.AccessDecisionManager
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.access.ConfigAttribute
|
||||
import org.springframework.security.authentication.AnonymousAuthenticationToken
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.BaseWebConfig;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint
|
||||
import org.springframework.security.web.FilterInvocation
|
||||
import org.springframework.security.web.access.AccessDeniedHandler;
|
||||
import org.springframework.security.web.access.AccessDeniedHandlerImpl;
|
||||
import org.springframework.security.web.access.ExceptionTranslationFilter
|
||||
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor
|
||||
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
|
||||
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
|
||||
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
|
||||
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
|
||||
import org.springframework.security.web.context.NullSecurityContextRepository;
|
||||
import org.springframework.security.web.context.SecurityContextPersistenceFilter
|
||||
import org.springframework.security.web.jaasapi.JaasApiIntegrationFilter;
|
||||
import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter;
|
||||
import org.springframework.security.web.util.AntPathRequestMatcher
|
||||
import org.springframework.security.web.util.AnyRequestMatcher;
|
||||
import org.springframework.security.web.util.RequestMatcher
|
||||
|
||||
import spock.lang.Ignore;
|
||||
|
||||
/**
|
||||
* Tests to verify that all the functionality of <anonymous> attributes is present
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
public class NamespaceHttpCustomFilterTests extends BaseSpringSpec {
|
||||
def "http/custom-filter@before"() {
|
||||
when:
|
||||
loadConfig(CustomFilterBeforeConfig)
|
||||
then:
|
||||
filterChain().filters[0].class == CustomFilter
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class CustomFilterBeforeConfig extends BaseWebConfig {
|
||||
CustomFilterBeforeConfig() {
|
||||
// do not add the default filters to make testing easier
|
||||
super(true)
|
||||
}
|
||||
|
||||
protected void configure(HttpSecurity http) {
|
||||
http
|
||||
.addFilterBefore(new CustomFilter(), UsernamePasswordAuthenticationFilter.class)
|
||||
.formLogin()
|
||||
}
|
||||
}
|
||||
|
||||
def "http/custom-filter@after"() {
|
||||
when:
|
||||
loadConfig(CustomFilterAfterConfig)
|
||||
then:
|
||||
filterChain().filters[1].class == CustomFilter
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class CustomFilterAfterConfig extends BaseWebConfig {
|
||||
CustomFilterAfterConfig() {
|
||||
// do not add the default filters to make testing easier
|
||||
super(true)
|
||||
}
|
||||
|
||||
protected void configure(HttpSecurity http) {
|
||||
http
|
||||
.addFilterAfter(new CustomFilter(), UsernamePasswordAuthenticationFilter.class)
|
||||
.formLogin()
|
||||
}
|
||||
}
|
||||
|
||||
def "http/custom-filter@position"() {
|
||||
when:
|
||||
loadConfig(CustomFilterPositionConfig)
|
||||
then:
|
||||
filterChain().filters.collect { it.class } == [CustomFilter]
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class CustomFilterPositionConfig extends BaseWebConfig {
|
||||
CustomFilterPositionConfig() {
|
||||
// do not add the default filters to make testing easier
|
||||
super(true)
|
||||
}
|
||||
|
||||
protected void configure(HttpSecurity http) {
|
||||
http
|
||||
// this works so long as the CustomFilter extends one of the standard filters
|
||||
// if not, use addFilterBefore or addFilterAfter
|
||||
.addFilter(new CustomFilter())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
def "http/custom-filter no AuthenticationManager in HttpSecurity"() {
|
||||
when:
|
||||
loadConfig(NoAuthenticationManagerInHtppConfigurationConfig)
|
||||
then:
|
||||
filterChain().filters[0].class == CustomFilter
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
static class NoAuthenticationManagerInHtppConfigurationConfig extends WebSecurityConfigurerAdapter {
|
||||
NoAuthenticationManagerInHtppConfigurationConfig() {
|
||||
super(true)
|
||||
}
|
||||
|
||||
protected AuthenticationManager authenticationManager()
|
||||
throws Exception {
|
||||
return new CustomAuthenticationManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("USER")
|
||||
.and()
|
||||
.addFilterBefore(new CustomFilter(), UsernamePasswordAuthenticationFilter.class)
|
||||
}
|
||||
}
|
||||
|
||||
static class CustomFilter extends UsernamePasswordAuthenticationFilter {}
|
||||
|
||||
static class CustomAuthenticationManager implements AuthenticationManager {
|
||||
public Authentication authenticate(Authentication authentication)
|
||||
throws AuthenticationException {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web.configurers
|
||||
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser
|
||||
import org.springframework.security.access.expression.SecurityExpressionHandler
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.BaseWebConfig;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
|
||||
/**
|
||||
* Tests to verify that all the functionality of <anonymous> attributes is present
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
public class NamespaceHttpExpressionHandlerTests extends BaseSpringSpec {
|
||||
def "http/expression-handler@ref"() {
|
||||
when:
|
||||
def parser = new SpelExpressionParser()
|
||||
ExpressionHandlerConfig.EXPRESSION_HANDLER = Mock(SecurityExpressionHandler.class)
|
||||
ExpressionHandlerConfig.EXPRESSION_HANDLER.getExpressionParser() >> parser
|
||||
loadConfig(ExpressionHandlerConfig)
|
||||
then:
|
||||
noExceptionThrown()
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
static class ExpressionHandlerConfig extends BaseWebConfig {
|
||||
static EXPRESSION_HANDLER;
|
||||
|
||||
protected void configure(HttpSecurity http) {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.expressionHandler(EXPRESSION_HANDLER)
|
||||
.antMatchers("/users**","/sessions/**").hasRole("ADMIN")
|
||||
.antMatchers("/signup").permitAll()
|
||||
.anyRequest().hasRole("USER")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web.configurers;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest
|
||||
import javax.servlet.http.HttpServletResponse
|
||||
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.mock.web.MockFilterChain
|
||||
import org.springframework.mock.web.MockHttpServletRequest
|
||||
import org.springframework.mock.web.MockHttpServletResponse
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.annotation.web.builders.WebSecurity
|
||||
import org.springframework.security.config.annotation.web.configuration.BaseWebConfig
|
||||
import org.springframework.security.web.FilterChainProxy
|
||||
import org.springframework.security.web.firewall.DefaultHttpFirewall
|
||||
import org.springframework.security.web.firewall.FirewalledRequest
|
||||
import org.springframework.security.web.firewall.RequestRejectedException
|
||||
|
||||
/**
|
||||
* Tests to verify that all the functionality of <http-firewall> attributes is present
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
public class NamespaceHttpFirewallTests extends BaseSpringSpec {
|
||||
FilterChainProxy springSecurityFilterChain
|
||||
MockHttpServletRequest request
|
||||
MockHttpServletResponse response
|
||||
MockFilterChain chain
|
||||
|
||||
def setup() {
|
||||
request = new MockHttpServletRequest()
|
||||
response = new MockHttpServletResponse()
|
||||
chain = new MockFilterChain()
|
||||
}
|
||||
|
||||
def "http-firewall"() {
|
||||
setup:
|
||||
loadConfig(HttpFirewallConfig)
|
||||
springSecurityFilterChain = context.getBean(FilterChainProxy)
|
||||
request.setPathInfo("/public/../private/")
|
||||
when:
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then: "the default firewall is used"
|
||||
thrown(RequestRejectedException)
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class HttpFirewallConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) {
|
||||
}
|
||||
}
|
||||
|
||||
def "http-firewall@ref"() {
|
||||
setup:
|
||||
loadConfig(CustomHttpFirewallConfig)
|
||||
springSecurityFilterChain = context.getBean(FilterChainProxy)
|
||||
request.setParameter("deny", "true")
|
||||
when:
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then: "the custom firewall is used"
|
||||
thrown(RequestRejectedException)
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class CustomHttpFirewallConfig extends BaseWebConfig {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) { }
|
||||
|
||||
@Override
|
||||
public void configure(WebSecurity builder) throws Exception {
|
||||
builder
|
||||
.httpFirewall(new CustomHttpFirewall())
|
||||
}
|
||||
}
|
||||
|
||||
static class CustomHttpFirewall extends DefaultHttpFirewall {
|
||||
|
||||
@Override
|
||||
public FirewalledRequest getFirewalledRequest(HttpServletRequest request)
|
||||
throws RequestRejectedException {
|
||||
if(request.getParameter("deny")) {
|
||||
throw new RequestRejectedException("custom rejection")
|
||||
}
|
||||
return super.getFirewalledRequest(request)
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpServletResponse getFirewalledResponse(
|
||||
HttpServletResponse response) {
|
||||
return super.getFirewalledRequest(response)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
|
||||
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web.configurers
|
||||
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.mock.web.MockFilterChain
|
||||
import org.springframework.mock.web.MockHttpServletRequest
|
||||
import org.springframework.mock.web.MockHttpServletResponse
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.annotation.web.builders.WebSecurity
|
||||
import org.springframework.security.config.annotation.web.configuration.BaseWebConfig
|
||||
import org.springframework.security.web.FilterChainProxy
|
||||
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler
|
||||
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
|
||||
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource
|
||||
|
||||
/**
|
||||
* Tests to verify that all the functionality of <anonymous> attributes is present
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
public class NamespaceHttpFormLoginTests extends BaseSpringSpec {
|
||||
FilterChainProxy springSecurityFilterChain
|
||||
|
||||
def "http/form-login"() {
|
||||
setup:
|
||||
loadConfig(FormLoginConfig)
|
||||
springSecurityFilterChain = context.getBean(FilterChainProxy)
|
||||
when:
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.getRedirectedUrl() == "http://localhost/login"
|
||||
when: "fail to log in"
|
||||
super.setup()
|
||||
request.requestURI = "/login"
|
||||
request.method = "POST"
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then: "sent to login error page"
|
||||
response.getRedirectedUrl() == "/login?error"
|
||||
when: "login success"
|
||||
super.setup()
|
||||
request.requestURI = "/login"
|
||||
request.method = "POST"
|
||||
request.parameters.username = ["user"] as String[]
|
||||
request.parameters.password = ["password"] as String[]
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then: "sent to default succes page"
|
||||
response.getRedirectedUrl() == "/"
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class FormLoginConfig extends BaseWebConfig {
|
||||
|
||||
@Override
|
||||
public void configure(WebSecurity web) throws Exception {
|
||||
web
|
||||
.ignoring()
|
||||
.antMatchers("/resources/**");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("USER")
|
||||
.and()
|
||||
.formLogin()
|
||||
}
|
||||
}
|
||||
|
||||
def "http/form-login custom"() {
|
||||
setup:
|
||||
loadConfig(FormLoginCustomConfig)
|
||||
springSecurityFilterChain = context.getBean(FilterChainProxy)
|
||||
when:
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.getRedirectedUrl() == "http://localhost/authentication/login"
|
||||
when: "fail to log in"
|
||||
super.setup()
|
||||
request.requestURI = "/authentication/login/process"
|
||||
request.method = "POST"
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then: "sent to login error page"
|
||||
response.getRedirectedUrl() == "/authentication/login?failed"
|
||||
when: "login success"
|
||||
super.setup()
|
||||
request.requestURI = "/authentication/login/process"
|
||||
request.method = "POST"
|
||||
request.parameters.j_username = ["user"] as String[]
|
||||
request.parameters.j_password = ["password"] as String[]
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then: "sent to default succes page"
|
||||
response.getRedirectedUrl() == "/default"
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class FormLoginCustomConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
boolean alwaysUseDefaultSuccess = true;
|
||||
http
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("USER")
|
||||
.and()
|
||||
.formLogin()
|
||||
.usernameParameter("j_username") // form-login@username-parameter
|
||||
.passwordParameter("j_password") // form-login@password-parameter
|
||||
.loginPage("/authentication/login") // form-login@login-page
|
||||
.failureUrl("/authentication/login?failed") // form-login@authentication-failure-url
|
||||
.loginProcessingUrl("/authentication/login/process") // form-login@login-processing-url
|
||||
.defaultSuccessUrl("/default", alwaysUseDefaultSuccess) // form-login@default-target-url / form-login@always-use-default-target
|
||||
}
|
||||
}
|
||||
|
||||
def "http/form-login custom refs"() {
|
||||
when:
|
||||
loadConfig(FormLoginCustomRefsConfig)
|
||||
springSecurityFilterChain = context.getBean(FilterChainProxy)
|
||||
then: "CustomWebAuthenticationDetailsSource is used"
|
||||
findFilter(UsernamePasswordAuthenticationFilter).authenticationDetailsSource.class == CustomWebAuthenticationDetailsSource
|
||||
when: "fail to log in"
|
||||
request.requestURI = "/login"
|
||||
request.method = "POST"
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then: "sent to login error page"
|
||||
response.getRedirectedUrl() == "/custom/failure"
|
||||
when: "login success"
|
||||
super.setup()
|
||||
request.requestURI = "/login"
|
||||
request.method = "POST"
|
||||
request.parameters.username = ["user"] as String[]
|
||||
request.parameters.password = ["password"] as String[]
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then: "sent to default succes page"
|
||||
response.getRedirectedUrl() == "/custom/targetUrl"
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class FormLoginCustomRefsConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.formLogin()
|
||||
.loginPage("/login")
|
||||
.failureHandler(new SimpleUrlAuthenticationFailureHandler("/custom/failure")) // form-login@authentication-failure-handler-ref
|
||||
.successHandler(new SavedRequestAwareAuthenticationSuccessHandler( defaultTargetUrl : "/custom/targetUrl" )) // form-login@authentication-success-handler-ref
|
||||
.authenticationDetailsSource(new CustomWebAuthenticationDetailsSource()) // form-login@authentication-details-source-ref
|
||||
.and();
|
||||
}
|
||||
}
|
||||
|
||||
static class CustomWebAuthenticationDetailsSource extends WebAuthenticationDetailsSource {}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web.configurers
|
||||
|
||||
import javax.servlet.http.HttpServletResponse
|
||||
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.mock.web.MockFilterChain
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.core.context.SecurityContextImpl
|
||||
import org.springframework.security.web.FilterChainProxy
|
||||
import org.springframework.security.web.context.HttpRequestResponseHolder
|
||||
import org.springframework.security.web.context.HttpSessionSecurityContextRepository
|
||||
|
||||
/**
|
||||
* Tests to verify that all the functionality of <intercept-url> attributes is present
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
public class NamespaceHttpInterceptUrlTests extends BaseSpringSpec {
|
||||
|
||||
def "http/intercept-url denied when not logged in"() {
|
||||
setup:
|
||||
loadConfig(HttpInterceptUrlConfig)
|
||||
request.servletPath == "/users"
|
||||
when:
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.status == HttpServletResponse.SC_FORBIDDEN
|
||||
}
|
||||
|
||||
def "http/intercept-url denied when logged in"() {
|
||||
setup:
|
||||
loadConfig(HttpInterceptUrlConfig)
|
||||
login()
|
||||
request.setServletPath("/users")
|
||||
when:
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.status == HttpServletResponse.SC_FORBIDDEN
|
||||
}
|
||||
|
||||
def "http/intercept-url allowed when logged in"() {
|
||||
setup:
|
||||
loadConfig(HttpInterceptUrlConfig)
|
||||
login("admin","ROLE_ADMIN")
|
||||
request.setServletPath("/users")
|
||||
when:
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.status == HttpServletResponse.SC_OK
|
||||
!response.isCommitted()
|
||||
}
|
||||
|
||||
def "http/intercept-url@method=POST"() {
|
||||
setup:
|
||||
loadConfig(HttpInterceptUrlConfig)
|
||||
when:
|
||||
login()
|
||||
request.setServletPath("/admin/post")
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.status == HttpServletResponse.SC_OK
|
||||
!response.isCommitted()
|
||||
when:
|
||||
super.setup()
|
||||
login()
|
||||
request.setServletPath("/admin/post")
|
||||
request.setMethod("POST")
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.status == HttpServletResponse.SC_FORBIDDEN
|
||||
when:
|
||||
super.setup()
|
||||
login("admin","ROLE_ADMIN")
|
||||
request.setServletPath("/admin/post")
|
||||
request.setMethod("POST")
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.status == HttpServletResponse.SC_OK
|
||||
!response.committed
|
||||
}
|
||||
|
||||
def "http/intercept-url@requires-channel"() {
|
||||
setup:
|
||||
loadConfig(HttpInterceptUrlConfig)
|
||||
when:
|
||||
request.setServletPath("/login")
|
||||
request.setRequestURI("/login")
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.redirectedUrl == "https://localhost/login"
|
||||
when:
|
||||
super.setup()
|
||||
request.setServletPath("/secured/a")
|
||||
request.setRequestURI("/secured/a")
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.redirectedUrl == "https://localhost/secured/a"
|
||||
when:
|
||||
super.setup()
|
||||
request.setSecure(true)
|
||||
request.setScheme("https")
|
||||
request.setServletPath("/user")
|
||||
request.setRequestURI("/user")
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.redirectedUrl == "http://localhost/user"
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
static class HttpInterceptUrlConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeUrls()
|
||||
// the line below is similar to intercept-url@pattern:
|
||||
// <intercept-url pattern="/users**" access="hasRole('ROLE_ADMIN')"/>
|
||||
// <intercept-url pattern="/sessions/**" access="hasRole('ROLE_ADMIN')"/>
|
||||
.antMatchers("/users**","/sessions/**").hasRole("ADMIN")
|
||||
// the line below is similar to intercept-url@method:
|
||||
// <intercept-url pattern="/admin/post" access="hasRole('ROLE_ADMIN')" method="POST"/>
|
||||
// <intercept-url pattern="/admin/another-post/**" access="hasRole('ROLE_ADMIN')" method="POST"/>
|
||||
.antMatchers(HttpMethod.POST, "/admin/post","/admin/another-post/**").hasRole("ADMIN")
|
||||
.antMatchers("/signup").permitAll()
|
||||
.anyRequest().hasRole("USER")
|
||||
.and()
|
||||
.requiresChannel()
|
||||
// NOTE: channel security is configured separately of authorization (i.e. intercept-url@access
|
||||
// the line below is similar to intercept-url@requires-channel="https":
|
||||
// <intercept-url pattern="/login" requires-channel="https"/>
|
||||
// <intercept-url pattern="/secured/**" requires-channel="https"/>
|
||||
.antMatchers("/login","/secured/**").requiresSecure()
|
||||
// the line below is similar to intercept-url@requires-channel="http":
|
||||
// <intercept-url pattern="/**" requires-channel="http"/>
|
||||
.anyRequest().requiresInsecure()
|
||||
}
|
||||
protected void registerAuthentication(
|
||||
AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.inMemoryAuthentication()
|
||||
.withUser("user").password("password").roles("USER").and()
|
||||
.withUser("admin").password("password").roles("USER", "ADMIN")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web.configurers;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.mock.web.MockFilterChain
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse
|
||||
import org.springframework.security.access.AccessDecisionManager
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.access.ConfigAttribute
|
||||
import org.springframework.security.authentication.AnonymousAuthenticationToken
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.security.crypto.codec.Base64;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint
|
||||
import org.springframework.security.web.FilterChainProxy
|
||||
import org.springframework.security.web.FilterInvocation
|
||||
import org.springframework.security.web.access.AccessDeniedHandler;
|
||||
import org.springframework.security.web.access.AccessDeniedHandlerImpl;
|
||||
import org.springframework.security.web.access.ExceptionTranslationFilter
|
||||
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor
|
||||
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
|
||||
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
|
||||
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler
|
||||
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource
|
||||
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider;
|
||||
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
|
||||
import org.springframework.security.web.authentication.preauth.PreAuthenticatedGrantedAuthoritiesUserDetailsService;
|
||||
import org.springframework.security.web.authentication.preauth.j2ee.J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource;
|
||||
import org.springframework.security.web.authentication.preauth.j2ee.J2eePreAuthenticatedProcessingFilter;
|
||||
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
|
||||
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
|
||||
import org.springframework.security.web.context.NullSecurityContextRepository;
|
||||
import org.springframework.security.web.context.SecurityContextPersistenceFilter
|
||||
import org.springframework.security.web.jaasapi.JaasApiIntegrationFilter;
|
||||
import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter;
|
||||
import org.springframework.security.web.util.AntPathRequestMatcher
|
||||
import org.springframework.security.web.util.AnyRequestMatcher;
|
||||
import org.springframework.security.web.util.RequestMatcher
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
/**
|
||||
* Tests to verify that all the functionality of <jee> attributes is present
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
public class NamespaceHttpJeeTests extends BaseSpringSpec {
|
||||
|
||||
def "http/jee@mappable-roles"() {
|
||||
when:
|
||||
loadConfig(JeeMappableRolesConfig)
|
||||
J2eePreAuthenticatedProcessingFilter filter = findFilter(J2eePreAuthenticatedProcessingFilter)
|
||||
AuthenticationManager authenticationManager = ReflectionTestUtils.getField(filter,"authenticationManager")
|
||||
then:
|
||||
authenticationManager
|
||||
filter.authenticationDetailsSource.class == J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource
|
||||
filter.authenticationDetailsSource.j2eeMappableRoles == ["ROLE_USER", "ROLE_ADMIN"] as Set
|
||||
authenticationManager.providers.find { it instanceof PreAuthenticatedAuthenticationProvider }.preAuthenticatedUserDetailsService.class == PreAuthenticatedGrantedAuthoritiesUserDetailsService
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public static class JeeMappableRolesConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("USER")
|
||||
.and()
|
||||
.jee()
|
||||
.mappableRoles("USER","ADMIN");
|
||||
}
|
||||
}
|
||||
|
||||
def "http/jee@user-service-ref"() {
|
||||
when:
|
||||
loadConfig(JeeUserServiceRefConfig)
|
||||
J2eePreAuthenticatedProcessingFilter filter = findFilter(J2eePreAuthenticatedProcessingFilter)
|
||||
AuthenticationManager authenticationManager = ReflectionTestUtils.getField(filter,"authenticationManager")
|
||||
then:
|
||||
authenticationManager
|
||||
filter.authenticationDetailsSource.class == J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource
|
||||
filter.authenticationDetailsSource.j2eeMappableRoles == ["ROLE_USER", "ROLE_ADMIN"] as Set
|
||||
authenticationManager.providers.find { it instanceof PreAuthenticatedAuthenticationProvider }.preAuthenticatedUserDetailsService.class == CustomUserService
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public static class JeeUserServiceRefConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("USER")
|
||||
.and()
|
||||
.jee()
|
||||
.mappableAuthorities("ROLE_USER","ROLE_ADMIN")
|
||||
.authenticatedUserDetailsService(new CustomUserService());
|
||||
}
|
||||
}
|
||||
|
||||
static class CustomUserService implements AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> {
|
||||
public UserDetails loadUserDetails(
|
||||
PreAuthenticatedAuthenticationToken token)
|
||||
throws UsernameNotFoundException {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web.configurers;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.mock.web.MockFilterChain
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse
|
||||
import org.springframework.security.access.AccessDecisionManager
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.access.ConfigAttribute
|
||||
import org.springframework.security.authentication.AnonymousAuthenticationToken
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.BaseWebConfig;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.core.context.SecurityContextImpl
|
||||
import org.springframework.security.crypto.codec.Base64;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint
|
||||
import org.springframework.security.web.FilterChainProxy
|
||||
import org.springframework.security.web.FilterInvocation
|
||||
import org.springframework.security.web.access.AccessDeniedHandler;
|
||||
import org.springframework.security.web.access.AccessDeniedHandlerImpl;
|
||||
import org.springframework.security.web.access.ExceptionTranslationFilter
|
||||
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor
|
||||
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
|
||||
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
|
||||
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler
|
||||
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource
|
||||
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
|
||||
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
|
||||
import org.springframework.security.web.context.HttpRequestResponseHolder
|
||||
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
|
||||
import org.springframework.security.web.context.NullSecurityContextRepository;
|
||||
import org.springframework.security.web.context.SecurityContextPersistenceFilter
|
||||
import org.springframework.security.web.jaasapi.JaasApiIntegrationFilter;
|
||||
import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter;
|
||||
import org.springframework.security.web.util.AntPathRequestMatcher
|
||||
import org.springframework.security.web.util.AnyRequestMatcher;
|
||||
import org.springframework.security.web.util.RequestMatcher
|
||||
|
||||
/**
|
||||
* Tests to verify that all the functionality of <logout> attributes is present
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
public class NamespaceHttpLogoutTests extends BaseSpringSpec {
|
||||
FilterChainProxy springSecurityFilterChain
|
||||
MockHttpServletRequest request
|
||||
MockHttpServletResponse response
|
||||
MockFilterChain chain
|
||||
|
||||
def setup() {
|
||||
request = new MockHttpServletRequest()
|
||||
response = new MockHttpServletResponse()
|
||||
chain = new MockFilterChain()
|
||||
}
|
||||
|
||||
def "http/logout"() {
|
||||
setup:
|
||||
loadConfig(HttpLogoutConfig)
|
||||
springSecurityFilterChain = context.getBean(FilterChainProxy)
|
||||
login()
|
||||
request.setRequestURI("/logout")
|
||||
when:
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
!authenticated()
|
||||
!request.getSession(false)
|
||||
response.redirectedUrl == "/login?logout"
|
||||
!response.getCookies()
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class HttpLogoutConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
}
|
||||
}
|
||||
|
||||
def "http/logout custom"() {
|
||||
setup:
|
||||
loadConfig(CustomHttpLogoutConfig)
|
||||
springSecurityFilterChain = context.getBean(FilterChainProxy)
|
||||
login()
|
||||
request.setRequestURI("/custom-logout")
|
||||
when:
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
!authenticated()
|
||||
request.getSession(false)
|
||||
response.redirectedUrl == "/logout-success"
|
||||
response.getCookies().length == 1
|
||||
response.getCookies()[0].name == "remove"
|
||||
response.getCookies()[0].maxAge == 0
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class CustomHttpLogoutConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.logout()
|
||||
.deleteCookies("remove") // logout@delete-cookies
|
||||
.invalidateHttpSession(false) // logout@invalidate-session=false (default is true)
|
||||
.logoutUrl("/custom-logout") // logout@logout-url (default is /logout)
|
||||
.logoutSuccessUrl("/logout-success") // logout@success-url (default is /login?logout)
|
||||
}
|
||||
}
|
||||
|
||||
def "http/logout@success-handler-ref"() {
|
||||
setup:
|
||||
loadConfig(SuccessHandlerRefHttpLogoutConfig)
|
||||
springSecurityFilterChain = context.getBean(FilterChainProxy)
|
||||
login()
|
||||
request.setRequestURI("/logout")
|
||||
when:
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
!authenticated()
|
||||
!request.getSession(false)
|
||||
response.redirectedUrl == "/SuccessHandlerRefHttpLogoutConfig"
|
||||
!response.getCookies()
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class SuccessHandlerRefHttpLogoutConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
SimpleUrlLogoutSuccessHandler logoutSuccessHandler = new SimpleUrlLogoutSuccessHandler(defaultTargetUrl:"/SuccessHandlerRefHttpLogoutConfig")
|
||||
http
|
||||
.logout()
|
||||
.logoutSuccessHandler(logoutSuccessHandler)
|
||||
}
|
||||
}
|
||||
|
||||
def login(String username="user", String role="ROLE_USER") {
|
||||
HttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository()
|
||||
HttpRequestResponseHolder requestResponseHolder = new HttpRequestResponseHolder(request, response)
|
||||
repo.loadContext(requestResponseHolder)
|
||||
repo.saveContext(new SecurityContextImpl(authentication: new UsernamePasswordAuthenticationToken(username, null, AuthorityUtils.createAuthorityList(role))), requestResponseHolder.request, requestResponseHolder.response)
|
||||
}
|
||||
|
||||
def authenticated() {
|
||||
HttpRequestResponseHolder requestResponseHolder = new HttpRequestResponseHolder(request, response)
|
||||
new HttpSessionSecurityContextRepository().loadContext(requestResponseHolder)?.authentication?.authenticated
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
|
||||
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web.configurers
|
||||
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.mock.web.MockFilterChain
|
||||
import org.springframework.mock.web.MockHttpServletRequest
|
||||
import org.springframework.mock.web.MockHttpServletResponse
|
||||
import org.springframework.security.authentication.AuthenticationManager
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.BaseWebConfig;
|
||||
import org.springframework.security.core.userdetails.AuthenticationUserDetailsService
|
||||
import org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper;
|
||||
import org.springframework.security.openid.OpenID4JavaConsumer
|
||||
import org.springframework.security.openid.OpenIDAuthenticationFilter
|
||||
import org.springframework.security.openid.OpenIDAuthenticationProvider
|
||||
import org.springframework.security.openid.OpenIDAuthenticationToken;
|
||||
import org.springframework.security.web.FilterChainProxy
|
||||
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler
|
||||
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler
|
||||
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource
|
||||
|
||||
/**
|
||||
* Tests to verify that all the functionality of <openid-login> attributes is present
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
public class NamespaceHttpOpenIDLoginTests extends BaseSpringSpec {
|
||||
FilterChainProxy springSecurityFilterChain
|
||||
MockHttpServletRequest request
|
||||
MockHttpServletResponse response
|
||||
MockFilterChain chain
|
||||
|
||||
def setup() {
|
||||
request = new MockHttpServletRequest()
|
||||
response = new MockHttpServletResponse()
|
||||
chain = new MockFilterChain()
|
||||
}
|
||||
|
||||
def "http/openid-login"() {
|
||||
when:
|
||||
loadConfig(OpenIDLoginConfig)
|
||||
springSecurityFilterChain = context.getBean(FilterChainProxy)
|
||||
then:
|
||||
findFilter(OpenIDAuthenticationFilter).consumer.class == OpenID4JavaConsumer
|
||||
when:
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.getRedirectedUrl() == "http://localhost/login"
|
||||
when: "fail to log in"
|
||||
setup()
|
||||
request.requestURI = "/login/openid"
|
||||
request.method = "POST"
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then: "sent to login error page"
|
||||
response.getRedirectedUrl() == "/login?error"
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class OpenIDLoginConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("USER")
|
||||
.and()
|
||||
.openidLogin()
|
||||
.permitAll();
|
||||
}
|
||||
}
|
||||
|
||||
def "http/openid-login/attribute-exchange"() {
|
||||
when:
|
||||
loadConfig(OpenIDLoginAttributeExchangeConfig)
|
||||
springSecurityFilterChain = context.getBean(FilterChainProxy)
|
||||
OpenID4JavaConsumer consumer = findFilter(OpenIDAuthenticationFilter).consumer
|
||||
then:
|
||||
consumer.class == OpenID4JavaConsumer
|
||||
|
||||
def googleAttrs = consumer.attributesToFetchFactory.createAttributeList("https://www.google.com/1")
|
||||
googleAttrs[0].name == "email"
|
||||
googleAttrs[0].type == "http://axschema.org/contact/email"
|
||||
googleAttrs[0].required
|
||||
googleAttrs[1].name == "firstname"
|
||||
googleAttrs[1].type == "http://axschema.org/namePerson/first"
|
||||
googleAttrs[1].required
|
||||
googleAttrs[2].name == "lastname"
|
||||
googleAttrs[2].type == "http://axschema.org/namePerson/last"
|
||||
googleAttrs[2].required
|
||||
|
||||
def yahooAttrs = consumer.attributesToFetchFactory.createAttributeList("https://rwinch.yahoo.com/rwinch/id")
|
||||
yahooAttrs[0].name == "email"
|
||||
yahooAttrs[0].type == "http://schema.openid.net/contact/email"
|
||||
yahooAttrs[0].required
|
||||
yahooAttrs[1].name == "fullname"
|
||||
yahooAttrs[1].type == "http://axschema.org/namePerson"
|
||||
yahooAttrs[1].required
|
||||
when:
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.getRedirectedUrl() == "http://localhost/login"
|
||||
when: "fail to log in"
|
||||
setup()
|
||||
request.requestURI = "/login/openid"
|
||||
request.method = "POST"
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then: "sent to login error page"
|
||||
response.getRedirectedUrl() == "/login?error"
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class OpenIDLoginAttributeExchangeConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("USER")
|
||||
.and()
|
||||
.openidLogin()
|
||||
.attributeExchange("https://www.google.com/.*") // attribute-exchange@identifier-match
|
||||
.attribute("email") // openid-attribute@name
|
||||
.type("http://axschema.org/contact/email") // openid-attribute@type
|
||||
.required(true) // openid-attribute@required
|
||||
.count(1) // openid-attribute@count
|
||||
.and()
|
||||
.attribute("firstname")
|
||||
.type("http://axschema.org/namePerson/first")
|
||||
.required(true)
|
||||
.and()
|
||||
.attribute("lastname")
|
||||
.type("http://axschema.org/namePerson/last")
|
||||
.required(true)
|
||||
.and()
|
||||
.and()
|
||||
.attributeExchange(".*yahoo.com.*")
|
||||
.attribute("email")
|
||||
.type("http://schema.openid.net/contact/email")
|
||||
.required(true)
|
||||
.and()
|
||||
.attribute("fullname")
|
||||
.type("http://axschema.org/namePerson")
|
||||
.required(true)
|
||||
.and()
|
||||
.and()
|
||||
.permitAll();
|
||||
}
|
||||
}
|
||||
|
||||
def "http/openid-login custom"() {
|
||||
setup:
|
||||
loadConfig(OpenIDLoginCustomConfig)
|
||||
springSecurityFilterChain = context.getBean(FilterChainProxy)
|
||||
when:
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.getRedirectedUrl() == "http://localhost/authentication/login"
|
||||
when: "fail to log in"
|
||||
setup()
|
||||
request.requestURI = "/authentication/login/process"
|
||||
request.method = "POST"
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then: "sent to login error page"
|
||||
response.getRedirectedUrl() == "/authentication/login?failed"
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class OpenIDLoginCustomConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
boolean alwaysUseDefaultSuccess = true;
|
||||
http
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("USER")
|
||||
.and()
|
||||
.openidLogin()
|
||||
.permitAll()
|
||||
.loginPage("/authentication/login") // openid-login@login-page
|
||||
.failureUrl("/authentication/login?failed") // openid-login@authentication-failure-url
|
||||
.loginProcessingUrl("/authentication/login/process") // openid-login@login-processing-url
|
||||
.defaultSuccessUrl("/default", alwaysUseDefaultSuccess) // openid-login@default-target-url / openid-login@always-use-default-target
|
||||
}
|
||||
}
|
||||
|
||||
def "http/openid-login custom refs"() {
|
||||
when:
|
||||
OpenIDLoginCustomRefsConfig.AUDS = Mock(AuthenticationUserDetailsService)
|
||||
loadConfig(OpenIDLoginCustomRefsConfig)
|
||||
springSecurityFilterChain = context.getBean(FilterChainProxy)
|
||||
then: "CustomWebAuthenticationDetailsSource is used"
|
||||
findFilter(OpenIDAuthenticationFilter).authenticationDetailsSource.class == CustomWebAuthenticationDetailsSource
|
||||
findAuthenticationProvider(OpenIDAuthenticationProvider).userDetailsService == OpenIDLoginCustomRefsConfig.AUDS
|
||||
when: "fail to log in"
|
||||
request.requestURI = "/login/openid"
|
||||
request.method = "POST"
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then: "sent to login error page"
|
||||
response.getRedirectedUrl() == "/custom/failure"
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class OpenIDLoginCustomRefsConfig extends BaseWebConfig {
|
||||
static AuthenticationUserDetailsService AUDS
|
||||
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("USER")
|
||||
.and()
|
||||
.openidLogin()
|
||||
// if using UserDetailsService wrap with new UserDetailsByNameServiceWrapper<OpenIDAuthenticationToken>()
|
||||
.authenticationUserDetailsService(AUDS) // openid-login@user-service-ref
|
||||
.failureHandler(new SimpleUrlAuthenticationFailureHandler("/custom/failure")) // openid-login@authentication-failure-handler-ref
|
||||
.successHandler(new SavedRequestAwareAuthenticationSuccessHandler( defaultTargetUrl : "/custom/targetUrl" )) // openid-login@authentication-success-handler-ref
|
||||
.authenticationDetailsSource(new CustomWebAuthenticationDetailsSource()); // openid-login@authentication-details-source-ref
|
||||
}
|
||||
|
||||
// only necessary to have easy access to the AuthenticationManager for testing/verification
|
||||
@Bean
|
||||
@Override
|
||||
public AuthenticationManager authenticationManagerBean()
|
||||
throws Exception {
|
||||
return super.authenticationManagerBean();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class CustomWebAuthenticationDetailsSource extends WebAuthenticationDetailsSource {}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web.configurers
|
||||
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.mock.web.MockFilterChain
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.core.context.SecurityContextImpl
|
||||
import org.springframework.security.web.FilterChainProxy
|
||||
import org.springframework.security.web.context.HttpRequestResponseHolder
|
||||
import org.springframework.security.web.context.HttpSessionSecurityContextRepository
|
||||
|
||||
/**
|
||||
* Tests to verify that all the functionality of <port-mappings> attributes is present
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
public class NamespaceHttpPortMappingsTests extends BaseSpringSpec {
|
||||
FilterChainProxy springSecurityFilterChain
|
||||
MockHttpServletRequest request
|
||||
MockHttpServletResponse response
|
||||
MockFilterChain chain
|
||||
|
||||
def setup() {
|
||||
request = new MockHttpServletRequest()
|
||||
request.setMethod("GET")
|
||||
response = new MockHttpServletResponse()
|
||||
chain = new MockFilterChain()
|
||||
}
|
||||
|
||||
def "http/port-mapper works with http/intercept-url@requires-channel"() {
|
||||
setup:
|
||||
loadConfig(HttpInterceptUrlWithPortMapperConfig)
|
||||
springSecurityFilterChain = context.getBean(FilterChainProxy)
|
||||
when:
|
||||
request.setServletPath("/login")
|
||||
request.setRequestURI("/login")
|
||||
request.setServerPort(9080);
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.redirectedUrl == "https://localhost:9443/login"
|
||||
when:
|
||||
setup()
|
||||
request.setServletPath("/secured/a")
|
||||
request.setRequestURI("/secured/a")
|
||||
request.setServerPort(9080);
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.redirectedUrl == "https://localhost:9443/secured/a"
|
||||
when:
|
||||
setup()
|
||||
request.setSecure(true)
|
||||
request.setScheme("https")
|
||||
request.setServerPort(9443);
|
||||
request.setServletPath("/user")
|
||||
request.setRequestURI("/user")
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.redirectedUrl == "http://localhost:9080/user"
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
static class HttpInterceptUrlWithPortMapperConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("USER")
|
||||
.and()
|
||||
.portMapper()
|
||||
.http(9080).mapsTo(9443)
|
||||
.and()
|
||||
.requiresChannel()
|
||||
.antMatchers("/login","/secured/**").requiresSecure()
|
||||
.anyRequest().requiresInsecure()
|
||||
}
|
||||
|
||||
protected void registerAuthentication(
|
||||
AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.inMemoryAuthentication()
|
||||
.withUser("user").password("password").roles("USER").and()
|
||||
.withUser("admin").password("password").roles("USER", "ADMIN")
|
||||
}
|
||||
}
|
||||
|
||||
def login(String username="user", String role="ROLE_USER") {
|
||||
HttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository()
|
||||
HttpRequestResponseHolder requestResponseHolder = new HttpRequestResponseHolder(request, response)
|
||||
repo.loadContext(requestResponseHolder)
|
||||
repo.saveContext(new SecurityContextImpl(authentication: new UsernamePasswordAuthenticationToken(username, null, AuthorityUtils.createAuthorityList(role))), requestResponseHolder.request, requestResponseHolder.response)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web.configurers;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.BaseWebConfig;
|
||||
import org.springframework.security.web.access.AccessDeniedHandler;
|
||||
import org.springframework.security.web.access.ExceptionTranslationFilter;
|
||||
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
|
||||
import org.springframework.security.web.savedrequest.RequestCache;
|
||||
|
||||
/**
|
||||
* Tests to verify that all the functionality of <request-cache> attributes is present
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
public class NamespaceHttpRequestCacheTests extends BaseSpringSpec {
|
||||
def "http/request-cache@ref"() {
|
||||
setup:
|
||||
RequestCacheRefConfig.REQUEST_CACHE = Mock(RequestCache)
|
||||
when:
|
||||
loadConfig(RequestCacheRefConfig)
|
||||
then:
|
||||
findFilter(ExceptionTranslationFilter).requestCache == RequestCacheRefConfig.REQUEST_CACHE
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class RequestCacheRefConfig extends BaseWebConfig {
|
||||
static RequestCache REQUEST_CACHE
|
||||
protected void configure(HttpSecurity http) {
|
||||
http.
|
||||
requestCache()
|
||||
.requestCache(REQUEST_CACHE)
|
||||
}
|
||||
}
|
||||
|
||||
def "http/request-cache@ref defaults to HttpSessionRequestCache"() {
|
||||
when:
|
||||
loadConfig(DefaultRequestCacheRefConfig)
|
||||
then:
|
||||
findFilter(ExceptionTranslationFilter).requestCache.class == HttpSessionRequestCache
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class DefaultRequestCacheRefConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,275 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web.configurers;
|
||||
|
||||
import java.security.cert.CertificateFactory
|
||||
import java.security.cert.X509Certificate
|
||||
|
||||
import javax.servlet.http.HttpServletRequest
|
||||
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.mock.web.MockFilterChain
|
||||
import org.springframework.mock.web.MockHttpServletRequest
|
||||
import org.springframework.mock.web.MockHttpServletResponse
|
||||
import org.springframework.security.authentication.AuthenticationDetailsSource
|
||||
import org.springframework.security.authentication.AuthenticationManager
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.core.authority.AuthorityUtils
|
||||
import org.springframework.security.core.userdetails.AuthenticationUserDetailsService
|
||||
import org.springframework.security.core.userdetails.User
|
||||
import org.springframework.security.core.userdetails.UserDetails
|
||||
import org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper
|
||||
import org.springframework.security.core.userdetails.UserDetailsService
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException
|
||||
import org.springframework.security.web.FilterChainProxy
|
||||
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource
|
||||
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider
|
||||
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken
|
||||
import org.springframework.security.web.authentication.preauth.PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails
|
||||
import org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter
|
||||
import org.springframework.security.web.context.HttpRequestResponseHolder
|
||||
import org.springframework.security.web.context.HttpSessionSecurityContextRepository
|
||||
import org.springframework.test.util.ReflectionTestUtils
|
||||
|
||||
/**
|
||||
* Tests to verify that all the functionality of <jee> attributes is present
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
public class NamespaceHttpX509Tests extends BaseSpringSpec {
|
||||
FilterChainProxy springSecurityFilterChain
|
||||
MockHttpServletRequest request
|
||||
MockHttpServletResponse response
|
||||
MockFilterChain chain
|
||||
|
||||
def setup() {
|
||||
request = new MockHttpServletRequest()
|
||||
response = new MockHttpServletResponse()
|
||||
chain = new MockFilterChain()
|
||||
}
|
||||
|
||||
def "http/x509 can authenticate"() {
|
||||
setup:
|
||||
X509Certificate certificate = loadCert("rod.cer")
|
||||
loadConfig(X509Config)
|
||||
springSecurityFilterChain = context.getBean(FilterChainProxy)
|
||||
when:
|
||||
request.setAttribute("javax.servlet.request.X509Certificate", [certificate] as X509Certificate[] )
|
||||
springSecurityFilterChain.doFilter(request, response, chain);
|
||||
then:
|
||||
response.status == 200
|
||||
authentication().name == 'rod'
|
||||
}
|
||||
|
||||
def "http/x509"() {
|
||||
when:
|
||||
loadConfig(X509Config)
|
||||
X509AuthenticationFilter filter = findFilter(X509AuthenticationFilter)
|
||||
AuthenticationManager authenticationManager = ReflectionTestUtils.getField(filter,"authenticationManager")
|
||||
then:
|
||||
authenticationManager
|
||||
filter.authenticationDetailsSource.class == WebAuthenticationDetailsSource
|
||||
authenticationManager.providers.find { it instanceof PreAuthenticatedAuthenticationProvider }.preAuthenticatedUserDetailsService.class == UserDetailsByNameServiceWrapper
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public static class X509Config extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth.
|
||||
inMemoryAuthentication()
|
||||
.withUser("rod").password("password").roles("USER","ADMIN");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("USER")
|
||||
.and()
|
||||
.x509();
|
||||
}
|
||||
}
|
||||
|
||||
def "http/x509@authentication-details-source-ref"() {
|
||||
setup:
|
||||
AuthenticationDetailsSourceRefConfig.AUTHENTICATION_DETAILS_SOURCE = Mock(AuthenticationDetailsSource)
|
||||
when:
|
||||
loadConfig(AuthenticationDetailsSourceRefConfig)
|
||||
X509AuthenticationFilter filter = findFilter(X509AuthenticationFilter)
|
||||
AuthenticationManager authenticationManager = ReflectionTestUtils.getField(filter,"authenticationManager")
|
||||
then:
|
||||
authenticationManager
|
||||
filter.authenticationDetailsSource == AuthenticationDetailsSourceRefConfig.AUTHENTICATION_DETAILS_SOURCE
|
||||
authenticationManager.providers.find { it instanceof PreAuthenticatedAuthenticationProvider }.preAuthenticatedUserDetailsService.class == UserDetailsByNameServiceWrapper
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public static class AuthenticationDetailsSourceRefConfig extends WebSecurityConfigurerAdapter {
|
||||
static AuthenticationDetailsSource<HttpServletRequest, PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails> AUTHENTICATION_DETAILS_SOURCE
|
||||
|
||||
protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth.
|
||||
inMemoryAuthentication()
|
||||
.withUser("rod").password("password").roles("USER","ADMIN");
|
||||
}
|
||||
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("USER")
|
||||
.and()
|
||||
.x509()
|
||||
.authenticationDetailsSource(AUTHENTICATION_DETAILS_SOURCE);
|
||||
}
|
||||
}
|
||||
|
||||
def "http/x509@subject-principal-regex"() {
|
||||
setup:
|
||||
X509Certificate certificate = loadCert("rodatexampledotcom.cer")
|
||||
loadConfig(SubjectPrincipalRegexConfig)
|
||||
springSecurityFilterChain = context.getBean(FilterChainProxy)
|
||||
when:
|
||||
request.setAttribute("javax.servlet.request.X509Certificate", [certificate] as X509Certificate[] )
|
||||
springSecurityFilterChain.doFilter(request, response, chain);
|
||||
then:
|
||||
response.status == 200
|
||||
authentication().name == 'rod'
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public static class SubjectPrincipalRegexConfig extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth.
|
||||
inMemoryAuthentication()
|
||||
.withUser("rod").password("password").roles("USER","ADMIN");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("USER")
|
||||
.and()
|
||||
.x509()
|
||||
.subjectPrincipalRegex('CN=(.*?)@example.com(?:,|$)');
|
||||
}
|
||||
}
|
||||
|
||||
def "http/x509@user-service-ref"() {
|
||||
setup:
|
||||
X509Certificate certificate = loadCert("rodatexampledotcom.cer")
|
||||
loadConfig(UserDetailsServiceRefConfig)
|
||||
springSecurityFilterChain = context.getBean(FilterChainProxy)
|
||||
when:
|
||||
request.setAttribute("javax.servlet.request.X509Certificate", [certificate] as X509Certificate[] )
|
||||
springSecurityFilterChain.doFilter(request, response, chain);
|
||||
then:
|
||||
response.status == 200
|
||||
authentication().name == 'customuser'
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public static class UserDetailsServiceRefConfig extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth.
|
||||
inMemoryAuthentication()
|
||||
.withUser("rod").password("password").roles("USER","ADMIN");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("USER")
|
||||
.and()
|
||||
.x509()
|
||||
.userDetailsService(new CustomUserDetailsService());
|
||||
}
|
||||
}
|
||||
|
||||
def "http/x509 custom AuthenticationUserDetailsService"() {
|
||||
setup:
|
||||
X509Certificate certificate = loadCert("rodatexampledotcom.cer")
|
||||
loadConfig(AuthenticationUserDetailsServiceConfig)
|
||||
springSecurityFilterChain = context.getBean(FilterChainProxy)
|
||||
when:
|
||||
request.setAttribute("javax.servlet.request.X509Certificate", [certificate] as X509Certificate[] )
|
||||
springSecurityFilterChain.doFilter(request, response, chain);
|
||||
then:
|
||||
response.status == 200
|
||||
authentication().name == 'customuser'
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public static class AuthenticationUserDetailsServiceConfig extends WebSecurityConfigurerAdapter {
|
||||
static AuthenticationDetailsSource<HttpServletRequest, PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails> AUTHENTICATION_DETAILS_SOURCE
|
||||
|
||||
protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth.
|
||||
inMemoryAuthentication()
|
||||
.withUser("rod").password("password").roles("USER","ADMIN");
|
||||
}
|
||||
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("USER")
|
||||
.and()
|
||||
.x509()
|
||||
.userDetailsService(new CustomUserDetailsService());
|
||||
}
|
||||
}
|
||||
|
||||
def loadCert(String location) {
|
||||
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
|
||||
certFactory.generateCertificate(Thread.currentThread().contextClassLoader.getResourceAsStream(location))
|
||||
}
|
||||
|
||||
static class CustomUserDetailsService implements UserDetailsService {
|
||||
|
||||
public UserDetails loadUserByUsername(String username)
|
||||
throws UsernameNotFoundException {
|
||||
return new User("customuser", "password", AuthorityUtils.createAuthorityList("ROLE_USER"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class CustomAuthenticationUserDetailsService implements AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> {
|
||||
public UserDetails loadUserDetails(
|
||||
PreAuthenticatedAuthenticationToken token)
|
||||
throws UsernameNotFoundException {
|
||||
return new User("customuser", "password", AuthorityUtils.createAuthorityList("ROLE_USER"));
|
||||
}
|
||||
}
|
||||
|
||||
def authentication() {
|
||||
HttpRequestResponseHolder requestResponseHolder = new HttpRequestResponseHolder(request, response)
|
||||
new HttpSessionSecurityContextRepository().loadContext(requestResponseHolder)?.authentication
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,345 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web.configurers
|
||||
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.BaseWebConfig;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
|
||||
import javax.servlet.http.Cookie
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.mock.web.MockFilterChain
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse
|
||||
import org.springframework.mock.web.MockHttpSession
|
||||
import org.springframework.security.authentication.AuthenticationManager
|
||||
import org.springframework.security.authentication.RememberMeAuthenticationToken;
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.core.context.SecurityContext
|
||||
import org.springframework.security.core.userdetails.UserDetailsService
|
||||
import org.springframework.security.web.FilterChainProxy
|
||||
import org.springframework.security.web.authentication.AuthenticationSuccessHandler
|
||||
import org.springframework.security.web.authentication.RememberMeServices
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
|
||||
import org.springframework.security.web.authentication.rememberme.AbstractRememberMeServices
|
||||
import org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices;
|
||||
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
|
||||
import org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter
|
||||
import org.springframework.security.web.context.HttpRequestResponseHolder
|
||||
import org.springframework.security.web.context.HttpSessionSecurityContextRepository
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
/**
|
||||
* Tests to verify that all the functionality of <anonymous> attributes is present
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
public class NamespaceRememberMeTests extends BaseSpringSpec {
|
||||
FilterChainProxy springSecurityFilterChain
|
||||
MockHttpServletRequest request
|
||||
MockHttpServletResponse response
|
||||
MockFilterChain chain
|
||||
|
||||
def setup() {
|
||||
request = new MockHttpServletRequest()
|
||||
response = new MockHttpServletResponse()
|
||||
chain = new MockFilterChain()
|
||||
}
|
||||
|
||||
def "http/remember-me"() {
|
||||
setup:
|
||||
loadConfig(RememberMeConfig)
|
||||
springSecurityFilterChain = context.getBean(FilterChainProxy)
|
||||
when: "login with remember me"
|
||||
setup()
|
||||
request.requestURI = "/login"
|
||||
request.method = "POST"
|
||||
request.parameters.username = ["user"] as String[]
|
||||
request.parameters.password = ["password"] as String[]
|
||||
request.parameters.'remember-me' = ["true"] as String[]
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
Cookie rememberMeCookie = getRememberMeCookie()
|
||||
then: "response contains remember me cookie"
|
||||
rememberMeCookie != null
|
||||
when: "session expires"
|
||||
setup()
|
||||
request.setCookies(rememberMeCookie)
|
||||
request.requestURI = "/abc"
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
MockHttpSession session = request.getSession()
|
||||
then: "initialized to RememberMeAuthenticationToken"
|
||||
SecurityContext context = new HttpSessionSecurityContextRepository().loadContext(new HttpRequestResponseHolder(request, response))
|
||||
context.getAuthentication() instanceof RememberMeAuthenticationToken
|
||||
when: "logout"
|
||||
setup()
|
||||
request.setSession(session)
|
||||
request.setCookies(rememberMeCookie)
|
||||
request.requestURI = "/logout"
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
rememberMeCookie = getRememberMeCookie()
|
||||
then: "logout cookie expired"
|
||||
response.getRedirectedUrl() == "/login?logout"
|
||||
rememberMeCookie.maxAge == 0
|
||||
when: "use remember me after logout"
|
||||
setup()
|
||||
request.setCookies(rememberMeCookie)
|
||||
request.requestURI = "/abc"
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then: "sent to default login page"
|
||||
response.getRedirectedUrl() == "http://localhost/login"
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class RememberMeConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("USER")
|
||||
.and()
|
||||
.formLogin()
|
||||
.and()
|
||||
.rememberMe()
|
||||
}
|
||||
}
|
||||
|
||||
def "http/remember-me@services-ref"() {
|
||||
setup:
|
||||
RememberMeServicesRefConfig.REMEMBER_ME_SERVICES = Mock(RememberMeServices)
|
||||
when: "use custom remember-me services"
|
||||
loadConfig(RememberMeServicesRefConfig)
|
||||
then: "custom remember-me services used"
|
||||
findFilter(RememberMeAuthenticationFilter).rememberMeServices == RememberMeServicesRefConfig.REMEMBER_ME_SERVICES
|
||||
findFilter(UsernamePasswordAuthenticationFilter).rememberMeServices == RememberMeServicesRefConfig.REMEMBER_ME_SERVICES
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class RememberMeServicesRefConfig extends BaseWebConfig {
|
||||
static RememberMeServices REMEMBER_ME_SERVICES
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.formLogin()
|
||||
.and()
|
||||
.rememberMe()
|
||||
.rememberMeServices(REMEMBER_ME_SERVICES)
|
||||
}
|
||||
}
|
||||
|
||||
def "http/remember-me@authentication-success-handler-ref"() {
|
||||
setup:
|
||||
AuthSuccessConfig.SUCCESS_HANDLER = Mock(AuthenticationSuccessHandler)
|
||||
when: "use custom success handler"
|
||||
loadConfig(AuthSuccessConfig)
|
||||
then: "custom remember-me success handler is used"
|
||||
findFilter(RememberMeAuthenticationFilter).successHandler == AuthSuccessConfig.SUCCESS_HANDLER
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class AuthSuccessConfig extends BaseWebConfig {
|
||||
static AuthenticationSuccessHandler SUCCESS_HANDLER
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.formLogin()
|
||||
.and()
|
||||
.rememberMe()
|
||||
.authenticationSuccessHandler(SUCCESS_HANDLER)
|
||||
}
|
||||
}
|
||||
|
||||
// http/remember-me@data-source-ref is not supported directly. Instead use http/remember-me@token-repository-ref example
|
||||
|
||||
def "http/remember-me@key"() {
|
||||
when: "use custom key"
|
||||
loadConfig(KeyConfig)
|
||||
AuthenticationManager authManager = context.getBean(AuthenticationManager)
|
||||
then: "custom key services used"
|
||||
findFilter(RememberMeAuthenticationFilter).rememberMeServices.key == "KeyConfig"
|
||||
authManager.authenticate(new RememberMeAuthenticationToken("KeyConfig", "user", AuthorityUtils.createAuthorityList("ROLE_USER")))
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class KeyConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.formLogin()
|
||||
.and()
|
||||
.rememberMe()
|
||||
.key("KeyConfig")
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Override
|
||||
public AuthenticationManager authenticationManagerBean()
|
||||
throws Exception {
|
||||
return super.authenticationManagerBean();
|
||||
}
|
||||
}
|
||||
|
||||
// http/remember-me@services-alias is not supported use standard aliasing instead (i.e. @Bean("alias"))
|
||||
|
||||
def "http/remember-me@token-repository-ref"() {
|
||||
setup:
|
||||
TokenRepositoryRefConfig.TOKEN_REPOSITORY = Mock(PersistentTokenRepository)
|
||||
when: "use custom token services"
|
||||
loadConfig(TokenRepositoryRefConfig)
|
||||
then: "custom token services used with PersistentTokenBasedRememberMeServices"
|
||||
PersistentTokenBasedRememberMeServices rememberMeServices = findFilter(RememberMeAuthenticationFilter).rememberMeServices
|
||||
findFilter(RememberMeAuthenticationFilter).rememberMeServices.tokenRepository == TokenRepositoryRefConfig.TOKEN_REPOSITORY
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class TokenRepositoryRefConfig extends BaseWebConfig {
|
||||
static PersistentTokenRepository TOKEN_REPOSITORY
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
// JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl()
|
||||
// tokenRepository.setDataSource(dataSource);
|
||||
http
|
||||
.formLogin()
|
||||
.and()
|
||||
.rememberMe()
|
||||
.tokenRepository(TOKEN_REPOSITORY)
|
||||
}
|
||||
}
|
||||
|
||||
def "http/remember-me@token-validity-seconds"() {
|
||||
when: "use token validity"
|
||||
loadConfig(TokenValiditySecondsConfig)
|
||||
then: "custom token validity used"
|
||||
findFilter(RememberMeAuthenticationFilter).rememberMeServices.tokenValiditySeconds == 1
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class TokenValiditySecondsConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.formLogin()
|
||||
.and()
|
||||
.rememberMe()
|
||||
.tokenValiditySeconds(1)
|
||||
}
|
||||
}
|
||||
|
||||
def "http/remember-me@token-validity-seconds default"() {
|
||||
when: "use token validity"
|
||||
loadConfig(DefaultTokenValiditySecondsConfig)
|
||||
then: "custom token validity used"
|
||||
findFilter(RememberMeAuthenticationFilter).rememberMeServices.tokenValiditySeconds == AbstractRememberMeServices.TWO_WEEKS_S
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class DefaultTokenValiditySecondsConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.formLogin()
|
||||
.and()
|
||||
.rememberMe()
|
||||
}
|
||||
}
|
||||
|
||||
def "http/remember-me@use-secure-cookie"() {
|
||||
when: "use secure cookies = true"
|
||||
loadConfig(UseSecureCookieConfig)
|
||||
then: "secure cookies will be used"
|
||||
ReflectionTestUtils.getField(findFilter(RememberMeAuthenticationFilter).rememberMeServices, "useSecureCookie") == true
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class UseSecureCookieConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.formLogin()
|
||||
.and()
|
||||
.rememberMe()
|
||||
.useSecureCookie(true)
|
||||
}
|
||||
}
|
||||
|
||||
def "http/remember-me@use-secure-cookie defaults"() {
|
||||
when: "use secure cookies not specified"
|
||||
loadConfig(DefaultUseSecureCookieConfig)
|
||||
then: "secure cookies will be null (use secure if the request is secure)"
|
||||
ReflectionTestUtils.getField(findFilter(RememberMeAuthenticationFilter).rememberMeServices, "useSecureCookie") == null
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class DefaultUseSecureCookieConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.formLogin()
|
||||
.and()
|
||||
.rememberMe()
|
||||
}
|
||||
}
|
||||
|
||||
def "http/remember-me defaults UserDetailsService with custom UserDetailsService"() {
|
||||
setup:
|
||||
DefaultsUserDetailsServiceWithDaoConfig.USERDETAILS_SERVICE = Mock(UserDetailsService)
|
||||
when: "use secure cookies not specified"
|
||||
loadConfig(DefaultsUserDetailsServiceWithDaoConfig)
|
||||
then: "RememberMeServices defaults to the custom UserDetailsService"
|
||||
ReflectionTestUtils.getField(findFilter(RememberMeAuthenticationFilter).rememberMeServices, "userDetailsService") == DefaultsUserDetailsServiceWithDaoConfig.USERDETAILS_SERVICE
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
static class DefaultsUserDetailsServiceWithDaoConfig extends WebSecurityConfigurerAdapter {
|
||||
static UserDetailsService USERDETAILS_SERVICE
|
||||
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.formLogin()
|
||||
.and()
|
||||
.rememberMe()
|
||||
}
|
||||
|
||||
protected void registerAuthentication(
|
||||
AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.userDetailsService(USERDETAILS_SERVICE);
|
||||
}
|
||||
}
|
||||
|
||||
def "http/remember-me@user-service-ref"() {
|
||||
setup:
|
||||
UserServiceRefConfig.USERDETAILS_SERVICE = Mock(UserDetailsService)
|
||||
when: "use custom UserDetailsService"
|
||||
loadConfig(UserServiceRefConfig)
|
||||
then: "custom UserDetailsService is used"
|
||||
ReflectionTestUtils.getField(findFilter(RememberMeAuthenticationFilter).rememberMeServices, "userDetailsService") == UserServiceRefConfig.USERDETAILS_SERVICE
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class UserServiceRefConfig extends BaseWebConfig {
|
||||
static UserDetailsService USERDETAILS_SERVICE
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.formLogin()
|
||||
.and()
|
||||
.rememberMe()
|
||||
.userDetailsService(USERDETAILS_SERVICE)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Cookie getRememberMeCookie(String cookieName="remember-me") {
|
||||
response.getCookie(cookieName)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web.configurers
|
||||
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.ObjectPostProcessor
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.core.session.SessionRegistry
|
||||
import org.springframework.security.web.authentication.session.NullAuthenticatedSessionStrategy;
|
||||
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
|
||||
import org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy
|
||||
import org.springframework.security.web.context.SecurityContextPersistenceFilter
|
||||
import org.springframework.security.web.context.SecurityContextRepository
|
||||
import org.springframework.security.web.session.ConcurrentSessionFilter
|
||||
import org.springframework.security.web.session.SessionManagementFilter
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
class NamespaceSessionManagementTests extends BaseSpringSpec {
|
||||
|
||||
def "http/session-management"() {
|
||||
when:
|
||||
loadConfig(SessionManagementConfig)
|
||||
then:
|
||||
findFilter(SessionManagementFilter).sessionAuthenticationStrategy instanceof SessionFixationProtectionStrategy
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class SessionManagementConfig extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
// enabled by default
|
||||
}
|
||||
}
|
||||
|
||||
def "http/session-management custom"() {
|
||||
setup:
|
||||
CustomSessionManagementConfig.SR = Mock(SessionRegistry)
|
||||
when:
|
||||
loadConfig(CustomSessionManagementConfig)
|
||||
then:
|
||||
findFilter(SessionManagementFilter).invalidSessionStrategy.destinationUrl == "/invalid-session"
|
||||
findFilter(SessionManagementFilter).failureHandler.defaultFailureUrl == "/session-auth-error"
|
||||
findFilter(SessionManagementFilter).sessionAuthenticationStrategy.maximumSessions == 1
|
||||
findFilter(SessionManagementFilter).sessionAuthenticationStrategy.exceptionIfMaximumExceeded
|
||||
findFilter(SessionManagementFilter).sessionAuthenticationStrategy.sessionRegistry == CustomSessionManagementConfig.SR
|
||||
findFilter(ConcurrentSessionFilter).expiredUrl == "/expired-session"
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class CustomSessionManagementConfig extends WebSecurityConfigurerAdapter {
|
||||
static SessionRegistry SR
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.sessionManagement()
|
||||
.invalidSessionUrl("/invalid-session") // session-management@invalid-session-url
|
||||
.sessionAuthenticationErrorUrl("/session-auth-error") // session-management@session-authentication-error-url
|
||||
.maximumSessions(1) // session-management/concurrency-control@max-sessions
|
||||
.maxSessionsPreventsLogin(true) // session-management/concurrency-control@error-if-maximum-exceeded
|
||||
.expiredUrl("/expired-session") // session-management/concurrency-control@expired-url
|
||||
.sessionRegistry(SR) // session-management/concurrency-control@session-registry-ref
|
||||
}
|
||||
}
|
||||
|
||||
def "http/session-management refs"() {
|
||||
setup:
|
||||
RefsSessionManagementConfig.SAS = Mock(SessionAuthenticationStrategy)
|
||||
when:
|
||||
loadConfig(RefsSessionManagementConfig)
|
||||
then:
|
||||
findFilter(SessionManagementFilter).sessionAuthenticationStrategy == RefsSessionManagementConfig.SAS
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class RefsSessionManagementConfig extends WebSecurityConfigurerAdapter {
|
||||
static SessionAuthenticationStrategy SAS
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.sessionManagement()
|
||||
.sessionAuthenticationStrategy(SAS) // session-management@session-authentication-strategy-ref
|
||||
}
|
||||
}
|
||||
|
||||
def "http/session-management@session-fixation-protection=none"() {
|
||||
when:
|
||||
loadConfig(SFPNoneSessionManagementConfig)
|
||||
then:
|
||||
findFilter(SessionManagementFilter).sessionAuthenticationStrategy.class == NullAuthenticatedSessionStrategy
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class SFPNoneSessionManagementConfig extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.sessionManagement()
|
||||
.sessionAuthenticationStrategy(new NullAuthenticatedSessionStrategy())
|
||||
}
|
||||
}
|
||||
|
||||
def "http/session-management@session-fixation-protection=migrateSession (default)"() {
|
||||
when:
|
||||
loadConfig(SFPMigrateSessionManagementConfig)
|
||||
then:
|
||||
findFilter(SessionManagementFilter).sessionAuthenticationStrategy.migrateSessionAttributes
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class SFPMigrateSessionManagementConfig extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.sessionManagement()
|
||||
}
|
||||
}
|
||||
|
||||
def "http/session-management@session-fixation-protection=newSession"() {
|
||||
when:
|
||||
loadConfig(SFPNewSessionSessionManagementConfig)
|
||||
then:
|
||||
!findFilter(SessionManagementFilter).sessionAuthenticationStrategy.migrateSessionAttributes
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class SFPNewSessionSessionManagementConfig extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.sessionManagement()
|
||||
.sessionAuthenticationStrategy(new SessionFixationProtectionStrategy(migrateSessionAttributes : false))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web.configurers
|
||||
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.mock.web.MockHttpServletRequest
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.web.util.RequestMatcher
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
class PermitAllSupportTests extends BaseSpringSpec {
|
||||
def "PermitAllSupport.ExactUrlRequestMatcher"() {
|
||||
expect:
|
||||
RequestMatcher matcher = new PermitAllSupport.ExactUrlRequestMatcher(processUrl)
|
||||
matcher.matches(new MockHttpServletRequest(requestURI:requestURI,contextPath:contextPath,queryString: query)) == matches
|
||||
where:
|
||||
processUrl | requestURI | contextPath | query | matches
|
||||
"/login" | "/sample/login" | "/sample" | null | true
|
||||
"/login" | "/sample/login" | "/sample" | "error" | false
|
||||
"/login?error" | "/sample/login" | "/sample" | "error" | true
|
||||
}
|
||||
|
||||
def "PermitAllSupport throws Exception when authorizedUrls() not invoked"() {
|
||||
when:
|
||||
loadConfig(NoAuthorizedUrlsConfig)
|
||||
then:
|
||||
BeanCreationException e = thrown()
|
||||
e.message.contains "permitAll only works with HttpSecurity.authorizeUrls"
|
||||
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class NoAuthorizedUrlsConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Override
|
||||
protected void registerAuthentication(AuthenticationManagerBuilder auth)
|
||||
throws Exception {
|
||||
auth
|
||||
.inMemoryAuthentication()
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.formLogin()
|
||||
.permitAll()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web.configurers
|
||||
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.security.config.annotation.AnyObjectPostProcessor
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService
|
||||
import org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter
|
||||
|
||||
/**
|
||||
* Tests for RememberMeConfigurer that flex edge cases. {@link NamespaceRememberMeTests} demonstrate mapping of the XML namespace to Java Config.
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
public class RememberMeConfigurerTests extends BaseSpringSpec {
|
||||
|
||||
def "rememberMe() null UserDetailsService provides meaningful error"() {
|
||||
when: "Load Config without UserDetailsService specified"
|
||||
loadConfig(NullUserDetailsConfig)
|
||||
then: "A good error message is provided"
|
||||
Exception success = thrown()
|
||||
success.message.contains "Invoke RememberMeConfigurer#userDetailsService(UserDetailsService) or see its javadoc for alternative approaches."
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class NullUserDetailsConfig extends WebSecurityConfigurerAdapter {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("USER")
|
||||
.and()
|
||||
.formLogin()
|
||||
.and()
|
||||
.rememberMe()
|
||||
}
|
||||
}
|
||||
|
||||
def "rememberMe ObjectPostProcessor"() {
|
||||
setup:
|
||||
AnyObjectPostProcessor opp = Mock()
|
||||
HttpSecurity http = new HttpSecurity(opp, authenticationBldr, [:])
|
||||
UserDetailsService uds = authenticationBldr.getDefaultUserDetailsService()
|
||||
when:
|
||||
http
|
||||
.rememberMe()
|
||||
.userDetailsService(authenticationBldr.getDefaultUserDetailsService())
|
||||
.and()
|
||||
.build()
|
||||
|
||||
then: "RememberMeAuthenticationFilter is registered with LifecycleManager"
|
||||
1 * opp.postProcess(_ as RememberMeAuthenticationFilter) >> {RememberMeAuthenticationFilter o -> o}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web.configurers
|
||||
|
||||
import org.springframework.security.config.annotation.AnyObjectPostProcessor
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.web.savedrequest.RequestCacheAwareFilter
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
class RequestCacheConfigurerTests extends BaseSpringSpec {
|
||||
|
||||
def "requestCache ObjectPostProcessor"() {
|
||||
setup:
|
||||
AnyObjectPostProcessor opp = Mock()
|
||||
HttpSecurity http = new HttpSecurity(opp, authenticationBldr, [:])
|
||||
when:
|
||||
http
|
||||
.requestCache()
|
||||
.and()
|
||||
.build()
|
||||
|
||||
then: "RequestCacheAwareFilter is registered with LifecycleManager"
|
||||
1 * opp.postProcess(_ as RequestCacheAwareFilter) >> {RequestCacheAwareFilter o -> o}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web.configurers
|
||||
|
||||
import org.springframework.security.config.annotation.AnyObjectPostProcessor
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.web.context.SecurityContextPersistenceFilter
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
class SecurityContextConfigurerTests extends BaseSpringSpec {
|
||||
|
||||
def "securityContext ObjectPostProcessor"() {
|
||||
setup:
|
||||
AnyObjectPostProcessor opp = Mock()
|
||||
HttpSecurity http = new HttpSecurity(opp, authenticationBldr, [:])
|
||||
when:
|
||||
http
|
||||
.securityContext()
|
||||
.and()
|
||||
.build()
|
||||
|
||||
then: "SecurityContextPersistenceFilter is registered with LifecycleManager"
|
||||
1 * opp.postProcess(_ as SecurityContextPersistenceFilter) >> {SecurityContextPersistenceFilter o -> o}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web.configurers
|
||||
|
||||
import org.springframework.security.config.annotation.AnyObjectPostProcessor
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
class ServletApiConfigurerTests extends BaseSpringSpec {
|
||||
|
||||
def "servletApi ObjectPostProcessor"() {
|
||||
setup:
|
||||
AnyObjectPostProcessor opp = Mock()
|
||||
HttpSecurity http = new HttpSecurity(opp, authenticationBldr, [:])
|
||||
when:
|
||||
http
|
||||
.servletApi()
|
||||
.and()
|
||||
.build()
|
||||
|
||||
then: "SecurityContextHolderAwareRequestFilter is registered with LifecycleManager"
|
||||
1 * opp.postProcess(_ as SecurityContextHolderAwareRequestFilter) >> {SecurityContextHolderAwareRequestFilter o -> o}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web.configurers
|
||||
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.security.config.annotation.AnyObjectPostProcessor
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.annotation.web.configurers.SessionCreationPolicy;
|
||||
import org.springframework.security.web.access.ExceptionTranslationFilter
|
||||
import org.springframework.security.web.context.SecurityContextPersistenceFilter
|
||||
import org.springframework.security.web.context.SecurityContextRepository
|
||||
import org.springframework.security.web.savedrequest.RequestCache
|
||||
import org.springframework.security.web.session.ConcurrentSessionFilter
|
||||
import org.springframework.security.web.session.SessionManagementFilter
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
class SessionManagementConfigurerTests extends BaseSpringSpec {
|
||||
|
||||
def "sessionManagement does not override explicit RequestCache"() {
|
||||
setup:
|
||||
SessionManagementDoesNotOverrideExplicitRequestCacheConfig.REQUEST_CACHE = Mock(RequestCache)
|
||||
when:
|
||||
loadConfig(SessionManagementDoesNotOverrideExplicitRequestCacheConfig)
|
||||
then:
|
||||
findFilter(ExceptionTranslationFilter).requestCache == SessionManagementDoesNotOverrideExplicitRequestCacheConfig.REQUEST_CACHE
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class SessionManagementDoesNotOverrideExplicitRequestCacheConfig extends WebSecurityConfigurerAdapter {
|
||||
static RequestCache REQUEST_CACHE
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.requestCache()
|
||||
.requestCache(REQUEST_CACHE)
|
||||
.and()
|
||||
.sessionManagement()
|
||||
.sessionCreationPolicy(SessionCreationPolicy.stateless)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
def "sessionManagement does not override explict SecurityContextRepository"() {
|
||||
setup:
|
||||
SessionManagementDoesNotOverrideExplicitSecurityContextRepositoryConfig.SECURITY_CONTEXT_REPO = Mock(SecurityContextRepository)
|
||||
when:
|
||||
loadConfig(SessionManagementDoesNotOverrideExplicitSecurityContextRepositoryConfig)
|
||||
then:
|
||||
findFilter(SecurityContextPersistenceFilter).repo == SessionManagementDoesNotOverrideExplicitSecurityContextRepositoryConfig.SECURITY_CONTEXT_REPO
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
static class SessionManagementDoesNotOverrideExplicitSecurityContextRepositoryConfig extends WebSecurityConfigurerAdapter {
|
||||
static SecurityContextRepository SECURITY_CONTEXT_REPO
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.securityContext()
|
||||
.securityContextRepository(SECURITY_CONTEXT_REPO)
|
||||
.and()
|
||||
.sessionManagement()
|
||||
.sessionCreationPolicy(SessionCreationPolicy.stateless)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
def "sessionManagement ObjectPostProcessor"() {
|
||||
setup:
|
||||
AnyObjectPostProcessor opp = Mock()
|
||||
HttpSecurity http = new HttpSecurity(opp, authenticationBldr, [:])
|
||||
when:
|
||||
http
|
||||
.sessionManagement()
|
||||
.maximumSessions(1)
|
||||
.and()
|
||||
.and()
|
||||
.build()
|
||||
|
||||
then: "SessionManagementFilter is registered with LifecycleManager"
|
||||
1 * opp.postProcess(_ as SessionManagementFilter) >> {SessionManagementFilter o -> o}
|
||||
and: "ConcurrentSessionFilter is registered with LifecycleManager"
|
||||
1 * opp.postProcess(_ as ConcurrentSessionFilter) >> {ConcurrentSessionFilter o -> o}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web.configurers;
|
||||
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.security.access.vote.AffirmativeBased
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.SecurityExpressions.*
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.annotation.web.configurers.UrlAuthorizationConfigurer;
|
||||
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
public class UrlAuthorizationsTests extends BaseSpringSpec {
|
||||
|
||||
def "hasAnyAuthority('ROLE_USER')"() {
|
||||
when:
|
||||
def expression = UrlAuthorizationConfigurer.hasAnyAuthority("ROLE_USER")
|
||||
then:
|
||||
expression == ["ROLE_USER"]
|
||||
}
|
||||
|
||||
def "hasAnyAuthority('ROLE_USER','ROLE_ADMIN')"() {
|
||||
when:
|
||||
def expression = UrlAuthorizationConfigurer.hasAnyAuthority("ROLE_USER","ROLE_ADMIN")
|
||||
then:
|
||||
expression == ["ROLE_USER","ROLE_ADMIN"]
|
||||
}
|
||||
|
||||
def "hasAnyRole('USER')"() {
|
||||
when:
|
||||
def expression = UrlAuthorizationConfigurer.hasAnyRole("USER")
|
||||
then:
|
||||
expression == ["ROLE_USER"]
|
||||
}
|
||||
|
||||
def "hasAnyRole('ROLE_USER','ROLE_ADMIN')"() {
|
||||
when:
|
||||
def expression = UrlAuthorizationConfigurer.hasAnyRole("USER","ADMIN")
|
||||
then:
|
||||
expression == ["ROLE_USER","ROLE_ADMIN"]
|
||||
}
|
||||
|
||||
def "uses AffirmativeBased AccessDecisionManager"() {
|
||||
when: "Load Config with no specific AccessDecisionManager"
|
||||
loadConfig(NoSpecificAccessDecessionManagerConfig)
|
||||
then: "AccessDecessionManager matches the HttpSecurityBuilder's default"
|
||||
findFilter(FilterSecurityInterceptor).accessDecisionManager.class == AffirmativeBased
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class NoSpecificAccessDecessionManagerConfig extends WebSecurityConfigurerAdapter {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.apply(new UrlAuthorizationConfigurer())
|
||||
.anyRequest().hasRole("USER")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web.configurers
|
||||
|
||||
import org.springframework.security.config.annotation.AnyObjectPostProcessor
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
class X509ConfigurerTests extends BaseSpringSpec {
|
||||
|
||||
def "x509 ObjectPostProcessor"() {
|
||||
setup:
|
||||
AnyObjectPostProcessor opp = Mock()
|
||||
HttpSecurity http = new HttpSecurity(opp, authenticationBldr, [:])
|
||||
when:
|
||||
http
|
||||
.x509()
|
||||
.and()
|
||||
.build()
|
||||
|
||||
then: "X509AuthenticationFilter is registered with LifecycleManager"
|
||||
1 * opp.postProcess(_ as X509AuthenticationFilter) >> {X509AuthenticationFilter o -> o}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web.configurers.openid
|
||||
|
||||
import org.springframework.security.config.annotation.AnyObjectPostProcessor
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper
|
||||
import org.springframework.security.core.userdetails.UserDetailsService
|
||||
import org.springframework.security.openid.OpenIDAuthenticationFilter
|
||||
import org.springframework.security.openid.OpenIDAuthenticationProvider
|
||||
import org.springframework.security.openid.OpenIDAuthenticationToken
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
class OpenIDLoginConfigurerTests extends BaseSpringSpec {
|
||||
|
||||
def "openidLogin ObjectPostProcessor"() {
|
||||
setup:
|
||||
AnyObjectPostProcessor opp = Mock()
|
||||
HttpSecurity http = new HttpSecurity(opp, authenticationBldr, [:])
|
||||
UserDetailsService uds = authenticationBldr.getDefaultUserDetailsService()
|
||||
when:
|
||||
http
|
||||
.openidLogin()
|
||||
.authenticationUserDetailsService(new UserDetailsByNameServiceWrapper<OpenIDAuthenticationToken>(uds))
|
||||
.and()
|
||||
.build()
|
||||
|
||||
then: "OpenIDAuthenticationFilter is registered with LifecycleManager"
|
||||
1 * opp.postProcess(_ as OpenIDAuthenticationFilter) >> {OpenIDAuthenticationFilter o -> o}
|
||||
and: "OpenIDAuthenticationProvider is registered with LifecycleManager"
|
||||
1 * opp.postProcess(_ as OpenIDAuthenticationProvider) >> {OpenIDAuthenticationProvider o -> o}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
create table users(principal varchar_ignorecase(50) not null primary key,credentials varchar_ignorecase(50) not null);
|
||||
create table roles (principal varchar_ignorecase(50) not null,role varchar_ignorecase(50) not null,constraint fk_roles_users foreign key(principal) references users(principal));
|
||||
create unique index ix_auth_principal on roles (principal,role);
|
||||
create table groups (id bigint generated by default as identity(start with 0) primary key,group_name varchar_ignorecase(50) not null);
|
||||
create table group_authorities (group_id bigint not null,authority varchar(50) not null,constraint fk_group_authorities_group foreign key(group_id) references groups(id));
|
||||
create table group_members (id bigint generated by default as identity(start with 0) primary key,username varchar(50) not null,group_id bigint not null,constraint fk_group_members_group foreign key(group_id) references groups(id));
|
||||
|
||||
insert into users values('user','password');
|
||||
insert into roles values('user','USER');
|
||||
|
||||
insert into groups values(1,'OPERATIONS');
|
||||
insert into group_authorities values(1,'DBA');
|
||||
insert into group_members values(1,'user',1);
|
||||
BIN
config/src/test/resources/rod.cer
Normal file
BIN
config/src/test/resources/rod.cer
Normal file
Binary file not shown.
BIN
config/src/test/resources/rodatexampledotcom.cer
Normal file
BIN
config/src/test/resources/rodatexampledotcom.cer
Normal file
Binary file not shown.
Reference in New Issue
Block a user