9 changed files with 1035 additions and 0 deletions
@ -0,0 +1,185 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2002-2022 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 |
||||||
|
* |
||||||
|
* https://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.ldap; |
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test; |
||||||
|
import org.junit.jupiter.api.extension.ExtendWith; |
||||||
|
|
||||||
|
import org.springframework.beans.factory.UnsatisfiedDependencyException; |
||||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||||
|
import org.springframework.context.annotation.Bean; |
||||||
|
import org.springframework.ldap.core.support.LdapContextSource; |
||||||
|
import org.springframework.security.authentication.AuthenticationManager; |
||||||
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; |
||||||
|
import org.springframework.security.config.test.SpringTestContext; |
||||||
|
import org.springframework.security.config.test.SpringTestContextExtension; |
||||||
|
import org.springframework.security.crypto.password.NoOpPasswordEncoder; |
||||||
|
import org.springframework.test.web.servlet.MockMvc; |
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatExceptionOfType; |
||||||
|
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin; |
||||||
|
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated; |
||||||
|
|
||||||
|
@ExtendWith(SpringTestContextExtension.class) |
||||||
|
public class EmbeddedLdapServerContextSourceFactoryBeanITests { |
||||||
|
|
||||||
|
public final SpringTestContext spring = new SpringTestContext(this); |
||||||
|
|
||||||
|
@Autowired |
||||||
|
private MockMvc mockMvc; |
||||||
|
|
||||||
|
@Test |
||||||
|
public void contextSourceFactoryBeanWhenEmbeddedServerThenAuthenticates() throws Exception { |
||||||
|
this.spring.register(FromEmbeddedLdapServerConfig.class).autowire(); |
||||||
|
|
||||||
|
this.mockMvc.perform(formLogin().user("bob").password("bobspassword")) |
||||||
|
.andExpect(authenticated().withUsername("bob")); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void contextSourceFactoryBeanWhenPortZeroThenAuthenticates() throws Exception { |
||||||
|
this.spring.register(PortZeroConfig.class).autowire(); |
||||||
|
|
||||||
|
this.mockMvc.perform(formLogin().user("bob").password("bobspassword")) |
||||||
|
.andExpect(authenticated().withUsername("bob")); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void contextSourceFactoryBeanWhenCustomLdifAndRootThenAuthenticates() throws Exception { |
||||||
|
this.spring.register(CustomLdifAndRootConfig.class).autowire(); |
||||||
|
|
||||||
|
this.mockMvc.perform(formLogin().user("pg").password("password")).andExpect(authenticated().withUsername("pg")); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void contextSourceFactoryBeanWhenCustomManagerDnThenAuthenticates() throws Exception { |
||||||
|
this.spring.register(CustomManagerDnConfig.class).autowire(); |
||||||
|
|
||||||
|
this.mockMvc.perform(formLogin().user("bob").password("bobspassword")) |
||||||
|
.andExpect(authenticated().withUsername("bob")); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void contextSourceFactoryBeanWhenManagerDnAndNoPasswordThenException() { |
||||||
|
assertThatExceptionOfType(UnsatisfiedDependencyException.class) |
||||||
|
.isThrownBy(() -> this.spring.register(CustomManagerDnNoPasswordConfig.class).autowire()) |
||||||
|
.withRootCauseInstanceOf(IllegalStateException.class) |
||||||
|
.withMessageContaining("managerPassword is required if managerDn is supplied"); |
||||||
|
} |
||||||
|
|
||||||
|
@EnableWebSecurity |
||||||
|
static class FromEmbeddedLdapServerConfig { |
||||||
|
|
||||||
|
@Bean |
||||||
|
EmbeddedLdapServerContextSourceFactoryBean contextSourceFactoryBean() { |
||||||
|
return EmbeddedLdapServerContextSourceFactoryBean.fromEmbeddedLdapServer(); |
||||||
|
} |
||||||
|
|
||||||
|
@Bean |
||||||
|
AuthenticationManager authenticationManager(LdapContextSource contextSource) { |
||||||
|
LdapBindAuthenticationManagerFactory factory = new LdapBindAuthenticationManagerFactory(contextSource); |
||||||
|
factory.setUserDnPatterns("uid={0},ou=people"); |
||||||
|
return factory.createAuthenticationManager(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@EnableWebSecurity |
||||||
|
static class PortZeroConfig { |
||||||
|
|
||||||
|
@Bean |
||||||
|
EmbeddedLdapServerContextSourceFactoryBean contextSourceFactoryBean() { |
||||||
|
EmbeddedLdapServerContextSourceFactoryBean factoryBean = EmbeddedLdapServerContextSourceFactoryBean |
||||||
|
.fromEmbeddedLdapServer(); |
||||||
|
factoryBean.setPort(0); |
||||||
|
return factoryBean; |
||||||
|
} |
||||||
|
|
||||||
|
@Bean |
||||||
|
AuthenticationManager authenticationManager(LdapContextSource contextSource) { |
||||||
|
LdapBindAuthenticationManagerFactory factory = new LdapBindAuthenticationManagerFactory(contextSource); |
||||||
|
factory.setUserDnPatterns("uid={0},ou=people"); |
||||||
|
return factory.createAuthenticationManager(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@EnableWebSecurity |
||||||
|
static class CustomLdifAndRootConfig { |
||||||
|
|
||||||
|
@Bean |
||||||
|
EmbeddedLdapServerContextSourceFactoryBean contextSourceFactoryBean() { |
||||||
|
EmbeddedLdapServerContextSourceFactoryBean factoryBean = EmbeddedLdapServerContextSourceFactoryBean |
||||||
|
.fromEmbeddedLdapServer(); |
||||||
|
factoryBean.setLdif("classpath*:test-server2.xldif"); |
||||||
|
factoryBean.setRoot("dc=monkeymachine,dc=co,dc=uk"); |
||||||
|
return factoryBean; |
||||||
|
} |
||||||
|
|
||||||
|
@Bean |
||||||
|
AuthenticationManager authenticationManager(LdapContextSource contextSource) { |
||||||
|
LdapBindAuthenticationManagerFactory factory = new LdapBindAuthenticationManagerFactory(contextSource); |
||||||
|
factory.setUserDnPatterns("uid={0},ou=gorillas"); |
||||||
|
return factory.createAuthenticationManager(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@EnableWebSecurity |
||||||
|
static class CustomManagerDnConfig { |
||||||
|
|
||||||
|
@Bean |
||||||
|
EmbeddedLdapServerContextSourceFactoryBean contextSourceFactoryBean() { |
||||||
|
EmbeddedLdapServerContextSourceFactoryBean factoryBean = EmbeddedLdapServerContextSourceFactoryBean |
||||||
|
.fromEmbeddedLdapServer(); |
||||||
|
factoryBean.setManagerDn("uid=admin,ou=system"); |
||||||
|
factoryBean.setManagerPassword("secret"); |
||||||
|
return factoryBean; |
||||||
|
} |
||||||
|
|
||||||
|
@Bean |
||||||
|
AuthenticationManager authenticationManager(LdapContextSource contextSource) { |
||||||
|
LdapPasswordComparisonAuthenticationManagerFactory factory = new LdapPasswordComparisonAuthenticationManagerFactory( |
||||||
|
contextSource, NoOpPasswordEncoder.getInstance()); |
||||||
|
factory.setUserDnPatterns("uid={0},ou=people"); |
||||||
|
return factory.createAuthenticationManager(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@EnableWebSecurity |
||||||
|
static class CustomManagerDnNoPasswordConfig { |
||||||
|
|
||||||
|
@Bean |
||||||
|
EmbeddedLdapServerContextSourceFactoryBean contextSourceFactoryBean() { |
||||||
|
EmbeddedLdapServerContextSourceFactoryBean factoryBean = EmbeddedLdapServerContextSourceFactoryBean |
||||||
|
.fromEmbeddedLdapServer(); |
||||||
|
factoryBean.setManagerDn("uid=admin,ou=system"); |
||||||
|
return factoryBean; |
||||||
|
} |
||||||
|
|
||||||
|
@Bean |
||||||
|
AuthenticationManager authenticationManager(LdapContextSource contextSource) { |
||||||
|
LdapPasswordComparisonAuthenticationManagerFactory factory = new LdapPasswordComparisonAuthenticationManagerFactory( |
||||||
|
contextSource, NoOpPasswordEncoder.getInstance()); |
||||||
|
factory.setUserDnPatterns("uid={0},ou=people"); |
||||||
|
return factory.createAuthenticationManager(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,241 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2002-2022 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 |
||||||
|
* |
||||||
|
* https://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.ldap; |
||||||
|
|
||||||
|
import java.util.Collection; |
||||||
|
import java.util.Collections; |
||||||
|
import java.util.HashSet; |
||||||
|
import java.util.Set; |
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test; |
||||||
|
import org.junit.jupiter.api.extension.ExtendWith; |
||||||
|
|
||||||
|
import org.springframework.beans.factory.DisposableBean; |
||||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||||
|
import org.springframework.context.annotation.Bean; |
||||||
|
import org.springframework.ldap.core.DirContextAdapter; |
||||||
|
import org.springframework.ldap.core.DirContextOperations; |
||||||
|
import org.springframework.ldap.core.support.BaseLdapPathContextSource; |
||||||
|
import org.springframework.ldap.core.support.LdapContextSource; |
||||||
|
import org.springframework.security.authentication.AuthenticationManager; |
||||||
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; |
||||||
|
import org.springframework.security.config.test.SpringTestContext; |
||||||
|
import org.springframework.security.config.test.SpringTestContextExtension; |
||||||
|
import org.springframework.security.core.GrantedAuthority; |
||||||
|
import org.springframework.security.core.authority.AuthorityUtils; |
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority; |
||||||
|
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper; |
||||||
|
import org.springframework.security.core.userdetails.User; |
||||||
|
import org.springframework.security.core.userdetails.UserDetails; |
||||||
|
import org.springframework.security.ldap.DefaultSpringSecurityContextSource; |
||||||
|
import org.springframework.security.ldap.server.ApacheDSContainer; |
||||||
|
import org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator; |
||||||
|
import org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator; |
||||||
|
import org.springframework.security.ldap.userdetails.UserDetailsContextMapper; |
||||||
|
import org.springframework.test.web.servlet.MockMvc; |
||||||
|
|
||||||
|
import static org.mockito.Mockito.mock; |
||||||
|
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin; |
||||||
|
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated; |
||||||
|
|
||||||
|
@ExtendWith(SpringTestContextExtension.class) |
||||||
|
public class LdapBindAuthenticationManagerFactoryITests { |
||||||
|
|
||||||
|
public final SpringTestContext spring = new SpringTestContext(this); |
||||||
|
|
||||||
|
@Autowired |
||||||
|
private MockMvc mockMvc; |
||||||
|
|
||||||
|
@Test |
||||||
|
public void authenticationManagerFactoryWhenFromContextSourceThenAuthenticates() throws Exception { |
||||||
|
this.spring.register(FromContextSourceConfig.class).autowire(); |
||||||
|
|
||||||
|
this.mockMvc.perform(formLogin().user("bob").password("bobspassword")) |
||||||
|
.andExpect(authenticated().withUsername("bob")); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void ldapAuthenticationProviderCustomLdapAuthoritiesPopulator() throws Exception { |
||||||
|
CustomAuthoritiesPopulatorConfig.LAP = new DefaultLdapAuthoritiesPopulator(mock(LdapContextSource.class), |
||||||
|
null) { |
||||||
|
@Override |
||||||
|
protected Set<GrantedAuthority> getAdditionalRoles(DirContextOperations user, String username) { |
||||||
|
return new HashSet<>(AuthorityUtils.createAuthorityList("ROLE_EXTRA")); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
this.spring.register(CustomAuthoritiesPopulatorConfig.class).autowire(); |
||||||
|
|
||||||
|
this.mockMvc.perform(formLogin().user("bob").password("bobspassword")).andExpect( |
||||||
|
authenticated().withAuthorities(Collections.singleton(new SimpleGrantedAuthority("ROLE_EXTRA")))); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void authenticationManagerFactoryWhenCustomAuthoritiesMapperThenUsed() throws Exception { |
||||||
|
CustomAuthoritiesMapperConfig.AUTHORITIES_MAPPER = ((authorities) -> AuthorityUtils |
||||||
|
.createAuthorityList("ROLE_CUSTOM")); |
||||||
|
|
||||||
|
this.spring.register(CustomAuthoritiesMapperConfig.class).autowire(); |
||||||
|
|
||||||
|
this.mockMvc.perform(formLogin().user("bob").password("bobspassword")).andExpect( |
||||||
|
authenticated().withAuthorities(Collections.singleton(new SimpleGrantedAuthority("ROLE_CUSTOM")))); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void authenticationManagerFactoryWhenCustomUserDetailsContextMapperThenUsed() throws Exception { |
||||||
|
CustomUserDetailsContextMapperConfig.CONTEXT_MAPPER = new UserDetailsContextMapper() { |
||||||
|
@Override |
||||||
|
public UserDetails mapUserFromContext(DirContextOperations ctx, String username, |
||||||
|
Collection<? extends GrantedAuthority> authorities) { |
||||||
|
return User.withUsername("other").password("password").roles("USER").build(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void mapUserToContext(UserDetails user, DirContextAdapter ctx) { |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
this.spring.register(CustomUserDetailsContextMapperConfig.class).autowire(); |
||||||
|
|
||||||
|
this.mockMvc.perform(formLogin().user("bob").password("bobspassword")) |
||||||
|
.andExpect(authenticated().withUsername("other")); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void authenticationManagerFactoryWhenCustomUserDnPatternsThenUsed() throws Exception { |
||||||
|
this.spring.register(CustomUserDnPatternsConfig.class).autowire(); |
||||||
|
|
||||||
|
this.mockMvc.perform(formLogin().user("bob").password("bobspassword")) |
||||||
|
.andExpect(authenticated().withUsername("bob")); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void authenticationManagerFactoryWhenCustomUserSearchThenUsed() throws Exception { |
||||||
|
this.spring.register(CustomUserSearchConfig.class).autowire(); |
||||||
|
|
||||||
|
this.mockMvc.perform(formLogin().user("bob").password("bobspassword")) |
||||||
|
.andExpect(authenticated().withUsername("bob")); |
||||||
|
} |
||||||
|
|
||||||
|
@EnableWebSecurity |
||||||
|
static class FromContextSourceConfig extends BaseLdapServerConfig { |
||||||
|
|
||||||
|
@Bean |
||||||
|
AuthenticationManager authenticationManager(BaseLdapPathContextSource contextSource) { |
||||||
|
LdapBindAuthenticationManagerFactory factory = new LdapBindAuthenticationManagerFactory(contextSource); |
||||||
|
factory.setUserDnPatterns("uid={0},ou=people"); |
||||||
|
return factory.createAuthenticationManager(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@EnableWebSecurity |
||||||
|
static class CustomAuthoritiesMapperConfig extends BaseLdapServerConfig { |
||||||
|
|
||||||
|
static GrantedAuthoritiesMapper AUTHORITIES_MAPPER; |
||||||
|
|
||||||
|
@Bean |
||||||
|
AuthenticationManager authenticationManager(BaseLdapPathContextSource contextSource) { |
||||||
|
LdapBindAuthenticationManagerFactory factory = new LdapBindAuthenticationManagerFactory(contextSource); |
||||||
|
factory.setUserDnPatterns("uid={0},ou=people"); |
||||||
|
factory.setAuthoritiesMapper(AUTHORITIES_MAPPER); |
||||||
|
return factory.createAuthenticationManager(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@EnableWebSecurity |
||||||
|
static class CustomAuthoritiesPopulatorConfig extends BaseLdapServerConfig { |
||||||
|
|
||||||
|
static LdapAuthoritiesPopulator LAP; |
||||||
|
|
||||||
|
@Bean |
||||||
|
AuthenticationManager authenticationManager(BaseLdapPathContextSource contextSource) { |
||||||
|
LdapBindAuthenticationManagerFactory factory = new LdapBindAuthenticationManagerFactory(contextSource); |
||||||
|
factory.setUserDnPatterns("uid={0},ou=people"); |
||||||
|
factory.setLdapAuthoritiesPopulator(LAP); |
||||||
|
return factory.createAuthenticationManager(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@EnableWebSecurity |
||||||
|
static class CustomUserDetailsContextMapperConfig extends BaseLdapServerConfig { |
||||||
|
|
||||||
|
static UserDetailsContextMapper CONTEXT_MAPPER; |
||||||
|
|
||||||
|
@Bean |
||||||
|
AuthenticationManager authenticationManager(BaseLdapPathContextSource contextSource) { |
||||||
|
LdapBindAuthenticationManagerFactory factory = new LdapBindAuthenticationManagerFactory(contextSource); |
||||||
|
factory.setUserDnPatterns("uid={0},ou=people"); |
||||||
|
factory.setUserDetailsContextMapper(CONTEXT_MAPPER); |
||||||
|
return factory.createAuthenticationManager(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@EnableWebSecurity |
||||||
|
static class CustomUserDnPatternsConfig extends BaseLdapServerConfig { |
||||||
|
|
||||||
|
@Bean |
||||||
|
AuthenticationManager authenticationManager(BaseLdapPathContextSource contextSource) { |
||||||
|
LdapBindAuthenticationManagerFactory factory = new LdapBindAuthenticationManagerFactory(contextSource); |
||||||
|
factory.setUserDnPatterns("uid={0},ou=people"); |
||||||
|
return factory.createAuthenticationManager(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@EnableWebSecurity |
||||||
|
static class CustomUserSearchConfig extends BaseLdapServerConfig { |
||||||
|
|
||||||
|
@Bean |
||||||
|
AuthenticationManager authenticationManager(BaseLdapPathContextSource contextSource) { |
||||||
|
LdapBindAuthenticationManagerFactory factory = new LdapBindAuthenticationManagerFactory(contextSource); |
||||||
|
factory.setUserSearchFilter("uid={0}"); |
||||||
|
factory.setUserSearchBase("ou=people"); |
||||||
|
return factory.createAuthenticationManager(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@EnableWebSecurity |
||||||
|
abstract static class BaseLdapServerConfig implements DisposableBean { |
||||||
|
|
||||||
|
private ApacheDSContainer container; |
||||||
|
|
||||||
|
@Bean |
||||||
|
ApacheDSContainer ldapServer() throws Exception { |
||||||
|
this.container = new ApacheDSContainer("dc=springframework,dc=org", "classpath:/test-server.ldif"); |
||||||
|
this.container.setPort(0); |
||||||
|
return this.container; |
||||||
|
} |
||||||
|
|
||||||
|
@Bean |
||||||
|
BaseLdapPathContextSource contextSource(ApacheDSContainer container) { |
||||||
|
int port = container.getLocalPort(); |
||||||
|
return new DefaultSpringSecurityContextSource("ldap://localhost:" + port + "/dc=springframework,dc=org"); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void destroy() { |
||||||
|
this.container.stop(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,114 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2002-2022 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 |
||||||
|
* |
||||||
|
* https://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.ldap; |
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test; |
||||||
|
import org.junit.jupiter.api.extension.ExtendWith; |
||||||
|
|
||||||
|
import org.springframework.beans.factory.DisposableBean; |
||||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||||
|
import org.springframework.context.annotation.Bean; |
||||||
|
import org.springframework.ldap.core.support.BaseLdapPathContextSource; |
||||||
|
import org.springframework.security.authentication.AuthenticationManager; |
||||||
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; |
||||||
|
import org.springframework.security.config.test.SpringTestContext; |
||||||
|
import org.springframework.security.config.test.SpringTestContextExtension; |
||||||
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; |
||||||
|
import org.springframework.security.crypto.password.NoOpPasswordEncoder; |
||||||
|
import org.springframework.security.ldap.DefaultSpringSecurityContextSource; |
||||||
|
import org.springframework.security.ldap.server.ApacheDSContainer; |
||||||
|
import org.springframework.test.web.servlet.MockMvc; |
||||||
|
|
||||||
|
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin; |
||||||
|
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated; |
||||||
|
|
||||||
|
@ExtendWith(SpringTestContextExtension.class) |
||||||
|
public class LdapPasswordComparisonAuthenticationManagerFactoryITests { |
||||||
|
|
||||||
|
public final SpringTestContext spring = new SpringTestContext(this); |
||||||
|
|
||||||
|
@Autowired |
||||||
|
private MockMvc mockMvc; |
||||||
|
|
||||||
|
@Test |
||||||
|
public void authenticationManagerFactoryWhenCustomPasswordEncoderThenUsed() throws Exception { |
||||||
|
this.spring.register(CustomPasswordEncoderConfig.class).autowire(); |
||||||
|
|
||||||
|
this.mockMvc.perform(formLogin().user("bcrypt").password("password")) |
||||||
|
.andExpect(authenticated().withUsername("bcrypt")); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void authenticationManagerFactoryWhenCustomPasswordAttributeThenUsed() throws Exception { |
||||||
|
this.spring.register(CustomPasswordAttributeConfig.class).autowire(); |
||||||
|
|
||||||
|
this.mockMvc.perform(formLogin().user("bob").password("bob")).andExpect(authenticated().withUsername("bob")); |
||||||
|
} |
||||||
|
|
||||||
|
@EnableWebSecurity |
||||||
|
static class CustomPasswordEncoderConfig extends BaseLdapServerConfig { |
||||||
|
|
||||||
|
@Bean |
||||||
|
AuthenticationManager authenticationManager(BaseLdapPathContextSource contextSource) { |
||||||
|
LdapPasswordComparisonAuthenticationManagerFactory factory = new LdapPasswordComparisonAuthenticationManagerFactory( |
||||||
|
contextSource, new BCryptPasswordEncoder()); |
||||||
|
factory.setUserDnPatterns("uid={0},ou=people"); |
||||||
|
return factory.createAuthenticationManager(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@EnableWebSecurity |
||||||
|
static class CustomPasswordAttributeConfig extends BaseLdapServerConfig { |
||||||
|
|
||||||
|
@Bean |
||||||
|
AuthenticationManager authenticationManager(BaseLdapPathContextSource contextSource) { |
||||||
|
LdapPasswordComparisonAuthenticationManagerFactory factory = new LdapPasswordComparisonAuthenticationManagerFactory( |
||||||
|
contextSource, NoOpPasswordEncoder.getInstance()); |
||||||
|
factory.setPasswordAttribute("uid"); |
||||||
|
factory.setUserDnPatterns("uid={0},ou=people"); |
||||||
|
return factory.createAuthenticationManager(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@EnableWebSecurity |
||||||
|
abstract static class BaseLdapServerConfig implements DisposableBean { |
||||||
|
|
||||||
|
private ApacheDSContainer container; |
||||||
|
|
||||||
|
@Bean |
||||||
|
ApacheDSContainer ldapServer() throws Exception { |
||||||
|
this.container = new ApacheDSContainer("dc=springframework,dc=org", "classpath:/test-server.ldif"); |
||||||
|
this.container.setPort(0); |
||||||
|
return this.container; |
||||||
|
} |
||||||
|
|
||||||
|
@Bean |
||||||
|
BaseLdapPathContextSource contextSource(ApacheDSContainer container) { |
||||||
|
int port = container.getLocalPort(); |
||||||
|
return new DefaultSpringSecurityContextSource("ldap://localhost:" + port + "/dc=springframework,dc=org"); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void destroy() { |
||||||
|
this.container.stop(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,183 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2002-2022 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 |
||||||
|
* |
||||||
|
* https://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.ldap; |
||||||
|
|
||||||
|
import org.springframework.ldap.core.support.BaseLdapPathContextSource; |
||||||
|
import org.springframework.security.authentication.AuthenticationManager; |
||||||
|
import org.springframework.security.authentication.ProviderManager; |
||||||
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; |
||||||
|
import org.springframework.security.core.Authentication; |
||||||
|
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper; |
||||||
|
import org.springframework.security.core.userdetails.UserDetails; |
||||||
|
import org.springframework.security.ldap.authentication.AbstractLdapAuthenticator; |
||||||
|
import org.springframework.security.ldap.authentication.LdapAuthenticationProvider; |
||||||
|
import org.springframework.security.ldap.search.FilterBasedLdapUserSearch; |
||||||
|
import org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator; |
||||||
|
import org.springframework.security.ldap.userdetails.UserDetailsContextMapper; |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates an {@link AuthenticationManager} that can perform LDAP authentication. |
||||||
|
* |
||||||
|
* @author Eleftheria Stein |
||||||
|
* @since 5.7 |
||||||
|
*/ |
||||||
|
public abstract class AbstractLdapAuthenticationManagerFactory<T extends AbstractLdapAuthenticator> { |
||||||
|
|
||||||
|
AbstractLdapAuthenticationManagerFactory(BaseLdapPathContextSource contextSource) { |
||||||
|
this.contextSource = contextSource; |
||||||
|
} |
||||||
|
|
||||||
|
private BaseLdapPathContextSource contextSource; |
||||||
|
|
||||||
|
private String[] userDnPatterns; |
||||||
|
|
||||||
|
private LdapAuthoritiesPopulator ldapAuthoritiesPopulator; |
||||||
|
|
||||||
|
private GrantedAuthoritiesMapper authoritiesMapper; |
||||||
|
|
||||||
|
private UserDetailsContextMapper userDetailsContextMapper; |
||||||
|
|
||||||
|
private String userSearchFilter; |
||||||
|
|
||||||
|
private String userSearchBase = ""; |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the {@link BaseLdapPathContextSource} used to perform LDAP authentication. |
||||||
|
* @param contextSource the {@link BaseLdapPathContextSource} used to perform LDAP |
||||||
|
* authentication |
||||||
|
*/ |
||||||
|
public void setContextSource(BaseLdapPathContextSource contextSource) { |
||||||
|
this.contextSource = contextSource; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the {@link BaseLdapPathContextSource} used to perform LDAP authentication. |
||||||
|
* @return the {@link BaseLdapPathContextSource} used to perform LDAP authentication |
||||||
|
*/ |
||||||
|
protected final BaseLdapPathContextSource getContextSource() { |
||||||
|
return this.contextSource; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the {@link LdapAuthoritiesPopulator} used to obtain a list of granted |
||||||
|
* authorities for an LDAP user. |
||||||
|
* @param ldapAuthoritiesPopulator the {@link LdapAuthoritiesPopulator} to use |
||||||
|
*/ |
||||||
|
public void setLdapAuthoritiesPopulator(LdapAuthoritiesPopulator ldapAuthoritiesPopulator) { |
||||||
|
this.ldapAuthoritiesPopulator = ldapAuthoritiesPopulator; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the {@link GrantedAuthoritiesMapper} used for converting the authorities |
||||||
|
* loaded from storage to a new set of authorities which will be associated to the |
||||||
|
* {@link UsernamePasswordAuthenticationToken}. |
||||||
|
* @param authoritiesMapper the {@link GrantedAuthoritiesMapper} used for mapping the |
||||||
|
* user's authorities |
||||||
|
*/ |
||||||
|
public void setAuthoritiesMapper(GrantedAuthoritiesMapper authoritiesMapper) { |
||||||
|
this.authoritiesMapper = authoritiesMapper; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets a custom strategy to be used for creating the {@link UserDetails} which will |
||||||
|
* be stored as the principal in the {@link Authentication}. |
||||||
|
* @param userDetailsContextMapper the strategy instance |
||||||
|
*/ |
||||||
|
public void setUserDetailsContextMapper(UserDetailsContextMapper userDetailsContextMapper) { |
||||||
|
this.userDetailsContextMapper = userDetailsContextMapper; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* If your users are at a fixed location in the directory (i.e. you can work out the |
||||||
|
* DN directly from the username without doing a directory search), you can use this |
||||||
|
* attribute to map directly to the DN. It maps directly to the userDnPatterns |
||||||
|
* property of AbstractLdapAuthenticator. The value is a specific pattern used to |
||||||
|
* build the user's DN, for example "uid={0},ou=people". The key "{0}" must be present |
||||||
|
* and will be substituted with the username. |
||||||
|
* @param userDnPatterns the LDAP patterns for finding the usernames |
||||||
|
*/ |
||||||
|
public void setUserDnPatterns(String... userDnPatterns) { |
||||||
|
this.userDnPatterns = userDnPatterns; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* The LDAP filter used to search for users (optional). For example "(uid={0})". The |
||||||
|
* substituted parameter is the user's login name. |
||||||
|
* @param userSearchFilter the LDAP filter used to search for users |
||||||
|
*/ |
||||||
|
public void setUserSearchFilter(String userSearchFilter) { |
||||||
|
this.userSearchFilter = userSearchFilter; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Search base for user searches. Defaults to "". Only used with |
||||||
|
* {@link #setUserSearchFilter(String)}. |
||||||
|
* @param userSearchBase search base for user searches |
||||||
|
*/ |
||||||
|
public void setUserSearchBase(String userSearchBase) { |
||||||
|
this.userSearchBase = userSearchBase; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the configured {@link AuthenticationManager} that can be used to perform |
||||||
|
* LDAP authentication. |
||||||
|
* @return the configured {@link AuthenticationManager} |
||||||
|
*/ |
||||||
|
public final AuthenticationManager createAuthenticationManager() { |
||||||
|
LdapAuthenticationProvider ldapAuthenticationProvider = getProvider(); |
||||||
|
return new ProviderManager(ldapAuthenticationProvider); |
||||||
|
} |
||||||
|
|
||||||
|
private LdapAuthenticationProvider getProvider() { |
||||||
|
AbstractLdapAuthenticator authenticator = getAuthenticator(); |
||||||
|
LdapAuthenticationProvider provider; |
||||||
|
if (this.ldapAuthoritiesPopulator != null) { |
||||||
|
provider = new LdapAuthenticationProvider(authenticator, this.ldapAuthoritiesPopulator); |
||||||
|
} |
||||||
|
else { |
||||||
|
provider = new LdapAuthenticationProvider(authenticator); |
||||||
|
} |
||||||
|
if (this.authoritiesMapper != null) { |
||||||
|
provider.setAuthoritiesMapper(this.authoritiesMapper); |
||||||
|
} |
||||||
|
if (this.userDetailsContextMapper != null) { |
||||||
|
provider.setUserDetailsContextMapper(this.userDetailsContextMapper); |
||||||
|
} |
||||||
|
return provider; |
||||||
|
} |
||||||
|
|
||||||
|
private AbstractLdapAuthenticator getAuthenticator() { |
||||||
|
AbstractLdapAuthenticator authenticator = createDefaultLdapAuthenticator(); |
||||||
|
if (this.userSearchFilter != null) { |
||||||
|
authenticator.setUserSearch( |
||||||
|
new FilterBasedLdapUserSearch(this.userSearchBase, this.userSearchFilter, this.contextSource)); |
||||||
|
} |
||||||
|
if (this.userDnPatterns != null && this.userDnPatterns.length > 0) { |
||||||
|
authenticator.setUserDnPatterns(this.userDnPatterns); |
||||||
|
} |
||||||
|
authenticator.afterPropertiesSet(); |
||||||
|
return authenticator; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Allows subclasses to supply the default {@link AbstractLdapAuthenticator}. |
||||||
|
* @return the {@link AbstractLdapAuthenticator} that will be configured for LDAP |
||||||
|
* authentication |
||||||
|
*/ |
||||||
|
protected abstract T createDefaultLdapAuthenticator(); |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,185 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2002-2022 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 |
||||||
|
* |
||||||
|
* https://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.ldap; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
import java.net.ServerSocket; |
||||||
|
|
||||||
|
import org.springframework.beans.BeansException; |
||||||
|
import org.springframework.beans.factory.DisposableBean; |
||||||
|
import org.springframework.beans.factory.FactoryBean; |
||||||
|
import org.springframework.context.ApplicationContext; |
||||||
|
import org.springframework.context.ApplicationContextAware; |
||||||
|
import org.springframework.context.Lifecycle; |
||||||
|
import org.springframework.security.ldap.DefaultSpringSecurityContextSource; |
||||||
|
import org.springframework.security.ldap.server.EmbeddedLdapServerContainer; |
||||||
|
import org.springframework.security.ldap.server.UnboundIdContainer; |
||||||
|
import org.springframework.util.ClassUtils; |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a {@link DefaultSpringSecurityContextSource} used to perform LDAP |
||||||
|
* authentication and starts and in-memory LDAP server. |
||||||
|
* |
||||||
|
* @author Eleftheria Stein |
||||||
|
* @since 5.7 |
||||||
|
*/ |
||||||
|
public class EmbeddedLdapServerContextSourceFactoryBean |
||||||
|
implements FactoryBean<DefaultSpringSecurityContextSource>, DisposableBean, ApplicationContextAware { |
||||||
|
|
||||||
|
private static final String UNBOUNDID_CLASSNAME = "com.unboundid.ldap.listener.InMemoryDirectoryServer"; |
||||||
|
|
||||||
|
private static final int DEFAULT_PORT = 33389; |
||||||
|
|
||||||
|
private static final int RANDOM_PORT = 0; |
||||||
|
|
||||||
|
private Integer port; |
||||||
|
|
||||||
|
private String ldif = "classpath*:*.ldif"; |
||||||
|
|
||||||
|
private String root = "dc=springframework,dc=org"; |
||||||
|
|
||||||
|
private ApplicationContext context; |
||||||
|
|
||||||
|
private String managerDn; |
||||||
|
|
||||||
|
private String managerPassword; |
||||||
|
|
||||||
|
private EmbeddedLdapServerContainer container; |
||||||
|
|
||||||
|
/** |
||||||
|
* Create an EmbeddedLdapServerContextSourceFactoryBean that will use an embedded LDAP |
||||||
|
* server to perform LDAP authentication. This requires a dependency on |
||||||
|
* `com.unboundid:unboundid-ldapsdk`. |
||||||
|
* @return the EmbeddedLdapServerContextSourceFactoryBean |
||||||
|
*/ |
||||||
|
public static EmbeddedLdapServerContextSourceFactoryBean fromEmbeddedLdapServer() { |
||||||
|
return new EmbeddedLdapServerContextSourceFactoryBean(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Specifies an LDIF to load at startup for an embedded LDAP server. The default is |
||||||
|
* "classpath*:*.ldif". |
||||||
|
* @param ldif the ldif to load at startup for an embedded LDAP server. |
||||||
|
*/ |
||||||
|
public void setLdif(String ldif) { |
||||||
|
this.ldif = ldif; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* The port to connect to LDAP to (the default is 33389 or random available port if |
||||||
|
* unavailable). Supplying 0 as the port indicates that a random available port should |
||||||
|
* be selected. |
||||||
|
* @param port the port to connect to |
||||||
|
*/ |
||||||
|
public void setPort(int port) { |
||||||
|
this.port = port; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Optional root suffix for the embedded LDAP server. Default is |
||||||
|
* "dc=springframework,dc=org". |
||||||
|
* @param root root suffix for the embedded LDAP server |
||||||
|
*/ |
||||||
|
public void setRoot(String root) { |
||||||
|
this.root = root; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Username (DN) of the "manager" user identity (i.e. "uid=admin,ou=system") which |
||||||
|
* will be used to authenticate to an LDAP server. If omitted, anonymous access will |
||||||
|
* be used. |
||||||
|
* @param managerDn the username (DN) of the "manager" user identity used to |
||||||
|
* authenticate to a LDAP server. |
||||||
|
*/ |
||||||
|
public void setManagerDn(String managerDn) { |
||||||
|
this.managerDn = managerDn; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* The password for the manager DN. This is required if the |
||||||
|
* {@link #setManagerDn(String)} is specified. |
||||||
|
* @param managerPassword password for the manager DN |
||||||
|
*/ |
||||||
|
public void setManagerPassword(String managerPassword) { |
||||||
|
this.managerPassword = managerPassword; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public DefaultSpringSecurityContextSource getObject() throws Exception { |
||||||
|
if (!ClassUtils.isPresent(UNBOUNDID_CLASSNAME, getClass().getClassLoader())) { |
||||||
|
throw new IllegalStateException("Embedded LDAP server is not provided"); |
||||||
|
} |
||||||
|
this.container = getContainer(); |
||||||
|
this.port = this.container.getPort(); |
||||||
|
DefaultSpringSecurityContextSource contextSourceFromProviderUrl = new DefaultSpringSecurityContextSource( |
||||||
|
"ldap://127.0.0.1:" + this.port + "/" + this.root); |
||||||
|
if (this.managerDn != null) { |
||||||
|
contextSourceFromProviderUrl.setUserDn(this.managerDn); |
||||||
|
if (this.managerPassword == null) { |
||||||
|
throw new IllegalStateException("managerPassword is required if managerDn is supplied"); |
||||||
|
} |
||||||
|
contextSourceFromProviderUrl.setPassword(this.managerPassword); |
||||||
|
} |
||||||
|
contextSourceFromProviderUrl.afterPropertiesSet(); |
||||||
|
return contextSourceFromProviderUrl; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Class<?> getObjectType() { |
||||||
|
return DefaultSpringSecurityContextSource.class; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void destroy() { |
||||||
|
if (this.container instanceof Lifecycle) { |
||||||
|
((Lifecycle) this.container).stop(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { |
||||||
|
this.context = applicationContext; |
||||||
|
} |
||||||
|
|
||||||
|
private EmbeddedLdapServerContainer getContainer() { |
||||||
|
if (!ClassUtils.isPresent(UNBOUNDID_CLASSNAME, getClass().getClassLoader())) { |
||||||
|
throw new IllegalStateException("Embedded LDAP server is not provided"); |
||||||
|
} |
||||||
|
UnboundIdContainer unboundIdContainer = new UnboundIdContainer(this.root, this.ldif); |
||||||
|
unboundIdContainer.setApplicationContext(this.context); |
||||||
|
unboundIdContainer.setPort(getEmbeddedServerPort()); |
||||||
|
unboundIdContainer.afterPropertiesSet(); |
||||||
|
return unboundIdContainer; |
||||||
|
} |
||||||
|
|
||||||
|
private int getEmbeddedServerPort() { |
||||||
|
if (this.port == null) { |
||||||
|
this.port = getDefaultEmbeddedServerPort(); |
||||||
|
} |
||||||
|
return this.port; |
||||||
|
} |
||||||
|
|
||||||
|
private int getDefaultEmbeddedServerPort() { |
||||||
|
try (ServerSocket serverSocket = new ServerSocket(DEFAULT_PORT)) { |
||||||
|
return serverSocket.getLocalPort(); |
||||||
|
} |
||||||
|
catch (IOException ex) { |
||||||
|
return RANDOM_PORT; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,41 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2002-2022 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 |
||||||
|
* |
||||||
|
* https://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.ldap; |
||||||
|
|
||||||
|
import org.springframework.ldap.core.support.BaseLdapPathContextSource; |
||||||
|
import org.springframework.security.authentication.AuthenticationManager; |
||||||
|
import org.springframework.security.ldap.authentication.BindAuthenticator; |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates an {@link AuthenticationManager} that can perform LDAP authentication using |
||||||
|
* bind authentication. |
||||||
|
* |
||||||
|
* @author Eleftheria Stein |
||||||
|
* @since 5.7 |
||||||
|
*/ |
||||||
|
public class LdapBindAuthenticationManagerFactory extends AbstractLdapAuthenticationManagerFactory<BindAuthenticator> { |
||||||
|
|
||||||
|
public LdapBindAuthenticationManagerFactory(BaseLdapPathContextSource contextSource) { |
||||||
|
super(contextSource); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected BindAuthenticator createDefaultLdapAuthenticator() { |
||||||
|
return new BindAuthenticator(getContextSource()); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,75 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2002-2022 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 |
||||||
|
* |
||||||
|
* https://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.ldap; |
||||||
|
|
||||||
|
import org.springframework.ldap.core.support.BaseLdapPathContextSource; |
||||||
|
import org.springframework.security.authentication.AuthenticationManager; |
||||||
|
import org.springframework.security.crypto.password.PasswordEncoder; |
||||||
|
import org.springframework.security.ldap.authentication.PasswordComparisonAuthenticator; |
||||||
|
import org.springframework.util.Assert; |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates an {@link AuthenticationManager} that can perform LDAP authentication using |
||||||
|
* password comparison. |
||||||
|
* |
||||||
|
* @author Eleftheria Stein |
||||||
|
* @since 5.7 |
||||||
|
*/ |
||||||
|
public class LdapPasswordComparisonAuthenticationManagerFactory |
||||||
|
extends AbstractLdapAuthenticationManagerFactory<PasswordComparisonAuthenticator> { |
||||||
|
|
||||||
|
private PasswordEncoder passwordEncoder; |
||||||
|
|
||||||
|
private String passwordAttribute; |
||||||
|
|
||||||
|
public LdapPasswordComparisonAuthenticationManagerFactory(BaseLdapPathContextSource contextSource, |
||||||
|
PasswordEncoder passwordEncoder) { |
||||||
|
super(contextSource); |
||||||
|
setPasswordEncoder(passwordEncoder); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Specifies the {@link PasswordEncoder} to be used when authenticating with password |
||||||
|
* comparison. |
||||||
|
* @param passwordEncoder the {@link PasswordEncoder} to use |
||||||
|
*/ |
||||||
|
public void setPasswordEncoder(PasswordEncoder passwordEncoder) { |
||||||
|
Assert.notNull(passwordEncoder, "passwordEncoder must not be null."); |
||||||
|
this.passwordEncoder = passwordEncoder; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* The attribute in the directory which contains the user password. Only used when |
||||||
|
* authenticating with password comparison. Defaults to "userPassword". |
||||||
|
* @param passwordAttribute the attribute in the directory which contains the user |
||||||
|
* password |
||||||
|
*/ |
||||||
|
public void setPasswordAttribute(String passwordAttribute) { |
||||||
|
this.passwordAttribute = passwordAttribute; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected PasswordComparisonAuthenticator createDefaultLdapAuthenticator() { |
||||||
|
PasswordComparisonAuthenticator ldapAuthenticator = new PasswordComparisonAuthenticator(getContextSource()); |
||||||
|
if (this.passwordAttribute != null) { |
||||||
|
ldapAuthenticator.setPasswordAttributeName(this.passwordAttribute); |
||||||
|
} |
||||||
|
ldapAuthenticator.setPasswordEncoder(this.passwordEncoder); |
||||||
|
return ldapAuthenticator; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
Loading…
Reference in new issue