Skip to content

Commit 7dedc0d

Browse files
committed
Password Modify Extended Operation Support
LdapUserDetailsManager can be configured to either use direct attribute modification or the LDAP Password Modify Extended Operation to change a user's password. Fixes: gh-3392
1 parent 7870786 commit 7dedc0d

File tree

3 files changed

+322
-34
lines changed

3 files changed

+322
-34
lines changed

ldap/spring-security-ldap.gradle

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,15 @@ dependencies {
2020
exclude(group: 'org.springframework.data', module: 'spring-data-commons')
2121
}
2222

23+
testCompile project(':spring-security-test')
2324
testCompile 'org.slf4j:jcl-over-slf4j'
2425
testCompile 'org.slf4j:slf4j-api'
2526
}
2627

2728
integrationTest {
28-
include('**/ApacheDSServerIntegrationTests.class', '**/ApacheDSEmbeddedLdifTests.class')
29+
include('**/ApacheDSServerIntegrationTests.class',
30+
'**/ApacheDSEmbeddedLdifTests.class',
31+
'**/LdapUserDetailsManagerModifyPasswordTests.class')
2932
// exclude('**/OpenLDAPIntegrationTestSuite.class')
3033
maxParallelForks = 1
3134
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
/*
2+
* Copyright 2002-2018 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.security.ldap.userdetails;
17+
18+
import javax.annotation.PreDestroy;
19+
20+
import org.junit.After;
21+
import org.junit.Before;
22+
import org.junit.Test;
23+
import org.junit.runner.RunWith;
24+
25+
import org.springframework.beans.factory.annotation.Autowired;
26+
import org.springframework.context.ConfigurableApplicationContext;
27+
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
28+
import org.springframework.context.annotation.Bean;
29+
import org.springframework.context.annotation.Configuration;
30+
import org.springframework.ldap.core.ContextSource;
31+
import org.springframework.security.authentication.BadCredentialsException;
32+
import org.springframework.security.ldap.DefaultLdapUsernameToDnMapper;
33+
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
34+
import org.springframework.security.ldap.SpringSecurityLdapTemplate;
35+
import org.springframework.security.ldap.server.UnboundIdContainer;
36+
import org.springframework.security.test.context.annotation.SecurityTestExecutionListeners;
37+
import org.springframework.security.test.context.support.WithMockUser;
38+
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
39+
40+
import static org.assertj.core.api.Assertions.assertThat;
41+
import static org.assertj.core.api.Assertions.assertThatCode;
42+
43+
/**
44+
* Tests for {@link LdapUserDetailsManager#changePassword}, specifically relating to the
45+
* use of the Modify Password Extended Operation.
46+
*
47+
* @author Josh Cummings
48+
*/
49+
@RunWith(SpringJUnit4ClassRunner.class)
50+
@SecurityTestExecutionListeners
51+
public class LdapUserDetailsManagerModifyPasswordTests {
52+
53+
ConfigurableApplicationContext context;
54+
55+
LdapUserDetailsManager userDetailsManager;
56+
ContextSource contextSource;
57+
58+
@Before
59+
public void setup() {
60+
this.context = new AnnotationConfigApplicationContext(ContainerConfiguration.class, LdapConfiguration.class);
61+
this.contextSource = this.context.getBean(ContextSource.class);
62+
63+
this.userDetailsManager = new LdapUserDetailsManager(this.contextSource);
64+
this.userDetailsManager.setUsePasswordModifyExtensionOperation(true);
65+
this.userDetailsManager.setUsernameMapper(new DefaultLdapUsernameToDnMapper("ou=people", "uid"));
66+
}
67+
68+
@After
69+
public void teardown() {
70+
this.context.close();
71+
}
72+
73+
@Test
74+
@WithMockUser(username="bob", password="bobspassword", authorities="ROLE_USER")
75+
public void changePasswordWhenOldPasswordIsIncorrectThenThrowsException() {
76+
assertThatCode(() ->
77+
this.userDetailsManager.changePassword("wrongoldpassword", "bobsnewpassword"))
78+
.isInstanceOf(BadCredentialsException.class);
79+
}
80+
81+
@Test
82+
@WithMockUser(username="bob", password="bobspassword", authorities="ROLE_USER")
83+
public void changePasswordWhenOldPasswordIsCorrectThenPasses() {
84+
SpringSecurityLdapTemplate template = new SpringSecurityLdapTemplate(this.contextSource);
85+
86+
this.userDetailsManager.changePassword("bobspassword",
87+
"bobsshinynewandformidablylongandnearlyimpossibletorememberthoughdemonstrablyhardtocrackduetoitshighlevelofentropypasswordofjustice");
88+
89+
assertThat(template.compare("uid=bob,ou=people", "userPassword",
90+
"bobsshinynewandformidablylongandnearlyimpossibletorememberthoughdemonstrablyhardtocrackduetoitshighlevelofentropypasswordofjustice")).isTrue();
91+
}
92+
93+
@Configuration
94+
static class LdapConfiguration {
95+
@Autowired UnboundIdContainer container;
96+
97+
@Bean
98+
ContextSource contextSource() throws Exception {
99+
return new DefaultSpringSecurityContextSource("ldap://127.0.0.1:"
100+
+ this.container.getPort() + "/dc=springframework,dc=org");
101+
}
102+
}
103+
104+
@Configuration
105+
static class ContainerConfiguration {
106+
UnboundIdContainer container = new UnboundIdContainer("dc=springframework,dc=org",
107+
"classpath:test-server.ldif");
108+
109+
@Bean
110+
UnboundIdContainer ldapContainer() {
111+
this.container.setPort(0);
112+
return this.container;
113+
}
114+
115+
@PreDestroy
116+
void shutdown() {
117+
this.container.stop();
118+
}
119+
}
120+
}

0 commit comments

Comments
 (0)