diff --git a/config/config.gradle b/config/config.gradle index 4e4689e758..6d8a34e0de 100644 --- a/config/config.gradle +++ b/config/config.gradle @@ -15,12 +15,22 @@ dependencies { // NB: Don't add other compile time dependencies to the config module as this breaks tooling compile project(':spring-security-core'), project(':spring-security-web'), + project(':spring-security-openid'), + project(':spring-security-ldap'), "org.aspectj:aspectjweaver:$aspectjVersion", 'aopalliance:aopalliance:1.0', "org.springframework:spring-aop:$springVersion", "org.springframework:spring-context:$springVersion", "org.springframework:spring-web:$springVersion", - "org.springframework:spring-beans:$springVersion" + "org.springframework:spring-beans:$springVersion", + "org.springframework:spring-jdbc:$springVersion", + "org.springframework:spring-tx:$springVersion", + "org.springframework.ldap:spring-ldap-core:$springLdapVersion" + compile('org.openid4java:openid4java-nodeps:0.9.6') { + exclude group: 'com.google.code.guice', module: 'guice' + } + compile 'com.google.inject:guice:2.0' + compile apacheds_libs provided "org.apache.tomcat:tomcat-servlet-api:$servletApiVersion" @@ -28,6 +38,7 @@ dependencies { testCompile project(':spring-security-ldap'), project(':spring-security-openid'), + project(':spring-security-cas'), project(':spring-security-core').sourceSets.test.output, 'javax.annotation:jsr250-api:1.0', "org.springframework.ldap:spring-ldap-core:$springLdapVersion", diff --git a/config/src/integration-test/java/org/springframework/security/config/annotation/authentication/ldap/LdapAuthenticationProviderBuilderSecurityBuilderTests.groovy b/config/src/integration-test/java/org/springframework/security/config/annotation/authentication/ldap/LdapAuthenticationProviderBuilderSecurityBuilderTests.groovy new file mode 100644 index 0000000000..e47a834571 --- /dev/null +++ b/config/src/integration-test/java/org/springframework/security/config/annotation/authentication/ldap/LdapAuthenticationProviderBuilderSecurityBuilderTests.groovy @@ -0,0 +1,177 @@ +/* + * 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.ldap + +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.ldap.DefaultSpringSecurityContextSource; +import org.springframework.security.ldap.authentication.LdapAuthenticationProvider; +import org.springframework.security.ldap.server.ApacheDSContainer; +import org.springframework.test.util.ReflectionTestUtils; + +/** + * + * @author Rob Winch + * + */ +class LdapAuthenticationProviderBuilderSecurityBuilderTests extends BaseSpringSpec { + def "default configuration"() { + when: + loadConfig(DefaultLdapConfig) + LdapAuthenticationProvider provider = ldapProvider() + then: + provider.authoritiesPopulator.groupRoleAttribute == "cn" + provider.authoritiesPopulator.groupSearchBase == "" + provider.authoritiesPopulator.groupSearchFilter == "(uniqueMember={0})" + ReflectionTestUtils.getField(provider,"authoritiesMapper").prefix == "ROLE_" + + } + + @Configuration + static class DefaultLdapConfig extends BaseLdapProviderConfig { + protected void registerAuthentication( + AuthenticationManagerBuilder auth) throws Exception { + auth + .ldapAuthentication() + .contextSource(contextSource()) + } + } + + def "group roles custom"() { + when: + loadConfig(GroupRolesConfig) + LdapAuthenticationProvider provider = ldapProvider() + then: + provider.authoritiesPopulator.groupRoleAttribute == "group" + } + + @Configuration + static class GroupRolesConfig extends BaseLdapProviderConfig { + protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception { + auth + .ldapAuthentication() + .contextSource(contextSource()) + .groupRoleAttribute("group") + } + } + + def "group search custom"() { + when: + loadConfig(GroupSearchConfig) + LdapAuthenticationProvider provider = ldapProvider() + then: + provider.authoritiesPopulator.groupSearchFilter == "ou=groupName" + } + + @Configuration + static class GroupSearchConfig extends BaseLdapProviderConfig { + protected void registerAuthentication( + AuthenticationManagerBuilder auth) throws Exception { + auth + .ldapAuthentication() + .contextSource(contextSource()) + .groupSearchFilter("ou=groupName"); + } + } + + def "role prefix custom"() { + when: + loadConfig(RolePrefixConfig) + LdapAuthenticationProvider provider = ldapProvider() + then: + ReflectionTestUtils.getField(provider,"authoritiesMapper").prefix == "role_" + } + + @Configuration + static class RolePrefixConfig extends BaseLdapProviderConfig { + protected void registerAuthentication( + AuthenticationManagerBuilder auth) throws Exception { + auth + .ldapAuthentication() + .contextSource(contextSource()) + .rolePrefix("role_") + } + } + + def "bind authentication"() { + when: + loadConfig(BindAuthenticationConfig) + AuthenticationManager auth = context.getBean(AuthenticationManager) + then: + auth + auth.authenticate(new UsernamePasswordAuthenticationToken("admin","password")).authorities.collect { it.authority }.sort() == ["ROLE_ADMIN","ROLE_USER"] + } + + @Configuration + static class BindAuthenticationConfig extends BaseLdapServerConfig { + protected void registerAuthentication( + AuthenticationManagerBuilder auth) throws Exception { + auth + .ldapAuthentication() + .contextSource(contextSource()) + .groupSearchBase("ou=groups") + .userDnPatterns("uid={0},ou=people"); + } + } + + def ldapProvider() { + context.getBean(AuthenticationManager).providers[0] + } + + @Configuration + static abstract class BaseLdapServerConfig extends BaseLdapProviderConfig { + @Bean + public ApacheDSContainer ldapServer() throws Exception { + ApacheDSContainer apacheDSContainer = new ApacheDSContainer("dc=springframework,dc=org", "classpath:/users.ldif"); + apacheDSContainer.setPort(33389); + return apacheDSContainer; + } + } + + @Configuration + static abstract class BaseLdapProviderConfig { + @Bean + public AuthenticationManager authenticationManager() { + AuthenticationManagerBuilder registry = new AuthenticationManagerBuilder(); + registerAuthentication(registry); + return registry.build(); + } + + protected abstract void registerAuthentication( + AuthenticationManagerBuilder auth) throws Exception; + + @Bean + public BaseLdapPathContextSource contextSource() throws Exception { + DefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource( + "ldap://127.0.0.1:33389/dc=springframework,dc=org") + contextSource.userDn = "uid=admin,ou=system" + contextSource.password = "secret" + contextSource.afterPropertiesSet(); + return contextSource; + } + } +} diff --git a/config/src/integration-test/java/org/springframework/security/config/annotation/authentication/ldap/NamespaceLdapAuthenticationProviderTests.groovy b/config/src/integration-test/java/org/springframework/security/config/annotation/authentication/ldap/NamespaceLdapAuthenticationProviderTests.groovy new file mode 100644 index 0000000000..6a8fd83b1d --- /dev/null +++ b/config/src/integration-test/java/org/springframework/security/config/annotation/authentication/ldap/NamespaceLdapAuthenticationProviderTests.groovy @@ -0,0 +1,68 @@ +/* + * 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.ldap + +import static org.springframework.security.config.annotation.authentication.ldap.NamespaceLdapAuthenticationProviderTestsConfigs.* + +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.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.authentication.ldap.NamespaceLdapAuthenticationProviderTestsConfigs.LdapAuthenticationProviderConfig; +import org.springframework.security.ldap.authentication.LdapAuthenticationProvider; +import org.springframework.security.ldap.authentication.PasswordComparisonAuthenticator; +import org.springframework.security.ldap.userdetails.PersonContextMapper; +import org.springframework.test.util.ReflectionTestUtils; + +/** + * + * @author Rob Winch + * + */ +class NamespaceLdapAuthenticationProviderTests extends BaseSpringSpec { + def "ldap-authentication-provider"() { + when: + loadConfig(LdapAuthenticationProviderConfig) + then: + authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("user","password")).authorities*.authority.sort() == ['ROLE_USER'] + } + + def "ldap-authentication-provider custom"() { + when: + loadConfig(CustomLdapAuthenticationProviderConfig) + LdapAuthenticationProvider provider = findAuthenticationProvider(LdapAuthenticationProvider) + then: + provider.authoritiesPopulator.groupRoleAttribute == "cn" + provider.authoritiesPopulator.groupSearchBase == "ou=groups" + provider.authoritiesPopulator.groupSearchFilter == "(member={0})" + ReflectionTestUtils.getField(provider,"authoritiesMapper").prefix == "PREFIX_" + provider.userDetailsContextMapper instanceof PersonContextMapper + provider.authenticator.getUserDns("user") == ["uid=user,ou=people"] + provider.authenticator.userSearch.searchBase == "ou=users" + provider.authenticator.userSearch.searchFilter == "(uid={0})" + } + + def "ldap-authentication-provider password compare"() { + when: + loadConfig(PasswordCompareLdapConfig) + LdapAuthenticationProvider provider = findAuthenticationProvider(LdapAuthenticationProvider) + then: + provider.authenticator instanceof PasswordComparisonAuthenticator + authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("user","password")).authorities*.authority.sort() == ['ROLE_USER'] + } +} diff --git a/config/src/integration-test/java/org/springframework/security/config/annotation/authentication/ldap/NamespaceLdapAuthenticationProviderTestsConfigs.java b/config/src/integration-test/java/org/springframework/security/config/annotation/authentication/ldap/NamespaceLdapAuthenticationProviderTestsConfigs.java new file mode 100644 index 0000000000..094a0b133c --- /dev/null +++ b/config/src/integration-test/java/org/springframework/security/config/annotation/authentication/ldap/NamespaceLdapAuthenticationProviderTestsConfigs.java @@ -0,0 +1,84 @@ +/* + * 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.ldap; + +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.encoding.PlaintextPasswordEncoder; +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.ldap.userdetails.PersonContextMapper; + +/** + * @author Rob Winch + * + */ +public class NamespaceLdapAuthenticationProviderTestsConfigs { + + @Configuration + @EnableWebSecurity + static class LdapAuthenticationProviderConfig extends WebSecurityConfigurerAdapter { + protected void registerAuthentication( + AuthenticationManagerBuilder auth) throws Exception { + auth + .ldapAuthentication() + .groupSearchBase("ou=groups") + .userDnPatterns("uid={0},ou=people"); // ldap-server@user-dn-pattern + } + } + + @Configuration + @EnableWebSecurity + static class CustomLdapAuthenticationProviderConfig extends WebSecurityConfigurerAdapter { + protected void registerAuthentication( + AuthenticationManagerBuilder auth) throws Exception { + auth + .ldapAuthentication() + .groupRoleAttribute("cn") // ldap-authentication-provider@group-role-attribute + .groupSearchBase("ou=groups") // ldap-authentication-provider@group-search-base + .groupSearchFilter("(member={0})") // ldap-authentication-provider@group-search-filter + .rolePrefix("PREFIX_") // ldap-authentication-provider@group-search-filter + .userDetailsContextMapper(new PersonContextMapper()) // ldap-authentication-provider@user-context-mapper-ref / ldap-authentication-provider@user-details-class + .userDnPatterns("uid={0},ou=people") // ldap-authentication-provider@user-dn-pattern + .userSearchBase("ou=users") // ldap-authentication-provider@user-dn-pattern + .userSearchFilter("(uid={0})") // ldap-authentication-provider@user-search-filter + // .contextSource(contextSource) // ldap-authentication-provider@server-ref + .contextSource() + .ldif("classpath:user.ldif") // ldap-server@ldif + .managerDn("uid=admin,ou=system") // ldap-server@manager-dn + .managerPassword("secret") // ldap-server@manager-password + .port(33399) // ldap-server@port + .root("dc=springframework,dc=org") // ldap-server@root + // .url("ldap://localhost:33389/dc-springframework,dc=org") this overrides root and port and is used for external + ; + } + } + + @Configuration + @EnableWebSecurity + static class PasswordCompareLdapConfig extends WebSecurityConfigurerAdapter { + protected void registerAuthentication( + AuthenticationManagerBuilder auth) throws Exception { + auth + .ldapAuthentication() + .groupSearchBase("ou=groups") + .userSearchFilter("(uid={0})") + .passwordCompare() + .passwordEncoder(new PlaintextPasswordEncoder()) // ldap-authentication-provider/password-compare/password-encoder@ref + .passwordAttribute("userPassword"); // ldap-authentication-provider/password-compare@password-attribute + } + } +} diff --git a/config/src/integration-test/resources/users.ldif b/config/src/integration-test/resources/users.ldif new file mode 100644 index 0000000000..fde2456d46 --- /dev/null +++ b/config/src/integration-test/resources/users.ldif @@ -0,0 +1,42 @@ +dn: ou=groups,dc=springframework,dc=org +objectclass: top +objectclass: organizationalUnit +ou: groups + +dn: ou=people,dc=springframework,dc=org +objectclass: top +objectclass: organizationalUnit +ou: people + +dn: uid=admin,ou=people,dc=springframework,dc=org +objectclass: top +objectclass: person +objectclass: organizationalPerson +objectclass: inetOrgPerson +cn: Rod Johnson +sn: Johnson +uid: admin +userPassword: password + +dn: uid=user,ou=people,dc=springframework,dc=org +objectclass: top +objectclass: person +objectclass: organizationalPerson +objectclass: inetOrgPerson +cn: Dianne Emu +sn: Emu +uid: user +userPassword: password + +dn: cn=user,ou=groups,dc=springframework,dc=org +objectclass: top +objectclass: groupOfNames +cn: user +uniqueMember: uid=admin,ou=people,dc=springframework,dc=org +uniqueMember: uid=user,ou=people,dc=springframework,dc=org + +dn: cn=admin,ou=groups,dc=springframework,dc=org +objectclass: top +objectclass: groupOfNames +cn: admin +uniqueMember: uid=admin,ou=people,dc=springframework,dc=org \ No newline at end of file diff --git a/config/src/main/java/org/springframework/security/config/annotation/AbstractConfiguredSecurityBuilder.java b/config/src/main/java/org/springframework/security/config/annotation/AbstractConfiguredSecurityBuilder.java new file mode 100644 index 0000000000..a3d0202f08 --- /dev/null +++ b/config/src/main/java/org/springframework/security/config/annotation/AbstractConfiguredSecurityBuilder.java @@ -0,0 +1,432 @@ +/* + * 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.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.springframework.security.config.annotation.web.builders.WebSecurity; +import org.springframework.util.Assert; +import org.springframework.web.filter.DelegatingFilterProxy; + +import com.google.inject.internal.ImmutableList.Builder; + +/** + *
A base {@link SecurityBuilder} that allows {@link SecurityConfigurer} to be + * applied to it. This makes modifying the {@link SecurityBuilder} a strategy + * that can be customized and broken up into a number of + * {@link SecurityConfigurer} objects that have more specific goals than that + * of the {@link SecurityBuilder}.
+ * + *For example, a {@link SecurityBuilder} may build an + * {@link DelegatingFilterProxy}, but a {@link SecurityConfigurer} might + * populate the {@link SecurityBuilder} with the filters necessary for session + * management, form based login, authorization, etc.
+ * + * @see WebSecurity + * + * @author Rob Winch + * + * @param