Reformat code using spring-javaformat
Run `./gradlew format` to reformat all java files. Issue gh-8945
This commit is contained in:
@@ -32,16 +32,15 @@ public class ApacheDsContainerConfig {
|
||||
|
||||
@Bean
|
||||
ApacheDSContainer ldapContainer() throws Exception {
|
||||
this.container = new ApacheDSContainer("dc=springframework,dc=org",
|
||||
"classpath:test-server.ldif");
|
||||
this.container = new ApacheDSContainer("dc=springframework,dc=org", "classpath:test-server.ldif");
|
||||
this.container.setPort(0);
|
||||
return this.container;
|
||||
}
|
||||
|
||||
@Bean
|
||||
ContextSource contextSource(ApacheDSContainer ldapContainer) throws Exception {
|
||||
return new DefaultSpringSecurityContextSource("ldap://127.0.0.1:"
|
||||
+ ldapContainer.getLocalPort() + "/dc=springframework,dc=org");
|
||||
return new DefaultSpringSecurityContextSource(
|
||||
"ldap://127.0.0.1:" + ldapContainer.getLocalPort() + "/dc=springframework,dc=org");
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
|
||||
@@ -53,8 +53,7 @@ public class DefaultSpringSecurityContextSourceTests {
|
||||
|
||||
@Test
|
||||
public void supportsSpacesInUrl() {
|
||||
new DefaultSpringSecurityContextSource(
|
||||
"ldap://myhost:10389/dc=spring%20framework,dc=org");
|
||||
new DefaultSpringSecurityContextSource("ldap://myhost:10389/dc=spring%20framework,dc=org");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -64,8 +63,8 @@ public class DefaultSpringSecurityContextSourceTests {
|
||||
ctxSrc.setUserDn("manager");
|
||||
ctxSrc.setPassword("password");
|
||||
ctxSrc.afterPropertiesSet();
|
||||
assertThat(ctxSrc.getAuthenticatedEnvForTest("manager", "password")).containsKey(
|
||||
AbstractContextSource.SUN_LDAP_POOLING_FLAG);
|
||||
assertThat(ctxSrc.getAuthenticatedEnvForTest("manager", "password"))
|
||||
.containsKey(AbstractContextSource.SUN_LDAP_POOLING_FLAG);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -75,18 +74,16 @@ public class DefaultSpringSecurityContextSourceTests {
|
||||
ctxSrc.setUserDn("manager");
|
||||
ctxSrc.setPassword("password");
|
||||
ctxSrc.afterPropertiesSet();
|
||||
assertThat(ctxSrc.getAuthenticatedEnvForTest("user", "password")).doesNotContainKey(
|
||||
AbstractContextSource.SUN_LDAP_POOLING_FLAG);
|
||||
assertThat(ctxSrc.getAuthenticatedEnvForTest("user", "password"))
|
||||
.doesNotContainKey(AbstractContextSource.SUN_LDAP_POOLING_FLAG);
|
||||
}
|
||||
|
||||
// SEC-1145. Confirms that there is no issue here with pooling.
|
||||
@Test(expected = AuthenticationException.class)
|
||||
public void cantBindWithWrongPasswordImmediatelyAfterSuccessfulBind()
|
||||
throws Exception {
|
||||
public void cantBindWithWrongPasswordImmediatelyAfterSuccessfulBind() throws Exception {
|
||||
DirContext ctx = null;
|
||||
try {
|
||||
ctx = this.contextSource.getContext(
|
||||
"uid=Bob,ou=people,dc=springframework,dc=org", "bobspassword");
|
||||
ctx = this.contextSource.getContext("uid=Bob,ou=people,dc=springframework,dc=org", "bobspassword");
|
||||
}
|
||||
catch (Exception e) {
|
||||
}
|
||||
@@ -95,27 +92,23 @@ public class DefaultSpringSecurityContextSourceTests {
|
||||
ctx.close();
|
||||
// com.sun.jndi.ldap.LdapPoolManager.showStats(System.out);
|
||||
// Now get it gain, with wrong password. Should fail.
|
||||
ctx = this.contextSource.getContext(
|
||||
"uid=Bob,ou=people,dc=springframework,dc=org", "wrongpassword");
|
||||
ctx = this.contextSource.getContext("uid=Bob,ou=people,dc=springframework,dc=org", "wrongpassword");
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serverUrlWithSpacesIsSupported() {
|
||||
DefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource(
|
||||
this.contextSource.getUrls()[0]
|
||||
+ "ou=space%20cadets,dc=springframework,dc=org");
|
||||
this.contextSource.getUrls()[0] + "ou=space%20cadets,dc=springframework,dc=org");
|
||||
contextSource.afterPropertiesSet();
|
||||
contextSource.getContext(
|
||||
"uid=space cadet,ou=space cadets,dc=springframework,dc=org",
|
||||
"spacecadetspassword");
|
||||
contextSource.getContext("uid=space cadet,ou=space cadets,dc=springframework,dc=org", "spacecadetspassword");
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void instantiationFailsWithEmptyServerList() {
|
||||
List<String> serverUrls = new ArrayList<>();
|
||||
DefaultSpringSecurityContextSource ctxSrc = new DefaultSpringSecurityContextSource(
|
||||
serverUrls, "dc=springframework,dc=org");
|
||||
DefaultSpringSecurityContextSource ctxSrc = new DefaultSpringSecurityContextSource(serverUrls,
|
||||
"dc=springframework,dc=org");
|
||||
ctxSrc.afterPropertiesSet();
|
||||
}
|
||||
|
||||
@@ -125,8 +118,8 @@ public class DefaultSpringSecurityContextSourceTests {
|
||||
serverUrls.add("ldap://foo:789");
|
||||
serverUrls.add("ldap://bar:389");
|
||||
serverUrls.add("ldaps://blah:636");
|
||||
DefaultSpringSecurityContextSource ctxSrc = new DefaultSpringSecurityContextSource(
|
||||
serverUrls, "dc=springframework,dc=org");
|
||||
DefaultSpringSecurityContextSource ctxSrc = new DefaultSpringSecurityContextSource(serverUrls,
|
||||
"dc=springframework,dc=org");
|
||||
|
||||
assertThat(ctxSrc.isAnonymousReadOnly()).isFalse();
|
||||
assertThat(ctxSrc.isPooled()).isTrue();
|
||||
@@ -140,8 +133,7 @@ public class DefaultSpringSecurityContextSourceTests {
|
||||
serverUrls.add("ldap://foo:789");
|
||||
serverUrls.add("ldap://bar:389");
|
||||
serverUrls.add("ldaps://blah:636");
|
||||
DefaultSpringSecurityContextSource ctxSrc = new DefaultSpringSecurityContextSource(
|
||||
serverUrls, baseDn);
|
||||
DefaultSpringSecurityContextSource ctxSrc = new DefaultSpringSecurityContextSource(serverUrls, baseDn);
|
||||
|
||||
assertThat(ctxSrc.isAnonymousReadOnly()).isFalse();
|
||||
assertThat(ctxSrc.isPooled()).isTrue();
|
||||
@@ -154,12 +146,12 @@ public class DefaultSpringSecurityContextSourceTests {
|
||||
serverUrls.add("ldaps://blah:636/");
|
||||
// this url should be rejected because the root DN goes into a separate parameter
|
||||
serverUrls.add("ldap://bar:389/dc=foobar,dc=org");
|
||||
DefaultSpringSecurityContextSource ctxSrc = new DefaultSpringSecurityContextSource(
|
||||
serverUrls, "dc=springframework,dc=org");
|
||||
DefaultSpringSecurityContextSource ctxSrc = new DefaultSpringSecurityContextSource(serverUrls,
|
||||
"dc=springframework,dc=org");
|
||||
}
|
||||
|
||||
static class EnvExposingDefaultSpringSecurityContextSource extends
|
||||
DefaultSpringSecurityContextSource {
|
||||
static class EnvExposingDefaultSpringSecurityContextSource extends DefaultSpringSecurityContextSource {
|
||||
|
||||
EnvExposingDefaultSpringSecurityContextSource(String providerUrl) {
|
||||
super(providerUrl);
|
||||
}
|
||||
@@ -168,5 +160,7 @@ public class DefaultSpringSecurityContextSourceTests {
|
||||
Hashtable getAuthenticatedEnvForTest(String userDn, String password) {
|
||||
return getAuthenticatedEnv(userDn, password);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -45,11 +45,13 @@ import org.springframework.test.context.junit4.SpringRunner;
|
||||
@RunWith(SpringRunner.class)
|
||||
@ContextConfiguration(classes = ApacheDsContainerConfig.class)
|
||||
public class SpringSecurityLdapTemplateITests {
|
||||
|
||||
// ~ Instance fields
|
||||
// ================================================================================================
|
||||
|
||||
@Autowired
|
||||
private DefaultSpringSecurityContextSource contextSource;
|
||||
|
||||
private SpringSecurityLdapTemplate template;
|
||||
|
||||
// ~ Methods
|
||||
@@ -67,14 +69,12 @@ public class SpringSecurityLdapTemplateITests {
|
||||
|
||||
@Test
|
||||
public void compareOfCorrectByteValueSucceeds() {
|
||||
assertThat(template.compare("uid=bob,ou=people", "userPassword",
|
||||
Utf8.encode("bobspassword"))).isTrue();
|
||||
assertThat(template.compare("uid=bob,ou=people", "userPassword", Utf8.encode("bobspassword"))).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compareOfWrongByteValueFails() {
|
||||
assertThat(template.compare("uid=bob,ou=people", "userPassword",
|
||||
Utf8.encode("wrongvalue"))).isFalse();
|
||||
assertThat(template.compare("uid=bob,ou=people", "userPassword", Utf8.encode("wrongvalue"))).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -108,8 +108,8 @@ public class SpringSecurityLdapTemplateITests {
|
||||
public void roleSearchReturnsCorrectNumberOfRoles() {
|
||||
String param = "uid=ben,ou=people,dc=springframework,dc=org";
|
||||
|
||||
Set<String> values = template.searchForSingleAttributeValues("ou=groups",
|
||||
"(member={0})", new String[] { param }, "ou");
|
||||
Set<String> values = template.searchForSingleAttributeValues("ou=groups", "(member={0})",
|
||||
new String[] { param }, "ou");
|
||||
|
||||
assertThat(values).as("Expected 3 results from search").hasSize(3);
|
||||
assertThat(values.contains("developer")).isTrue();
|
||||
@@ -119,14 +119,12 @@ public class SpringSecurityLdapTemplateITests {
|
||||
|
||||
@Test
|
||||
public void testMultiAttributeRetrievalWithNullAttributeNames() {
|
||||
Set<Map<String, List<String>>> values = template
|
||||
.searchForMultipleAttributeValues("ou=people", "(uid={0})",
|
||||
new String[] { "bob" }, null);
|
||||
Set<Map<String, List<String>>> values = template.searchForMultipleAttributeValues("ou=people", "(uid={0})",
|
||||
new String[] { "bob" }, null);
|
||||
assertThat(values).hasSize(1);
|
||||
Map<String, List<String>> record = values.iterator().next();
|
||||
assertAttributeValue(record, "uid", "bob");
|
||||
assertAttributeValue(record, "objectclass", "top", "person",
|
||||
"organizationalPerson", "inetOrgPerson");
|
||||
assertAttributeValue(record, "objectclass", "top", "person", "organizationalPerson", "inetOrgPerson");
|
||||
assertAttributeValue(record, "cn", "Bob Hamilton");
|
||||
assertAttributeValue(record, "sn", "Hamilton");
|
||||
assertThat(record.containsKey("userPassword")).isFalse();
|
||||
@@ -134,14 +132,12 @@ public class SpringSecurityLdapTemplateITests {
|
||||
|
||||
@Test
|
||||
public void testMultiAttributeRetrievalWithZeroLengthAttributeNames() {
|
||||
Set<Map<String, List<String>>> values = template
|
||||
.searchForMultipleAttributeValues("ou=people", "(uid={0})",
|
||||
new String[] { "bob" }, new String[0]);
|
||||
Set<Map<String, List<String>>> values = template.searchForMultipleAttributeValues("ou=people", "(uid={0})",
|
||||
new String[] { "bob" }, new String[0]);
|
||||
assertThat(values).hasSize(1);
|
||||
Map<String, List<String>> record = values.iterator().next();
|
||||
assertAttributeValue(record, "uid", "bob");
|
||||
assertAttributeValue(record, "objectclass", "top", "person",
|
||||
"organizationalPerson", "inetOrgPerson");
|
||||
assertAttributeValue(record, "objectclass", "top", "person", "organizationalPerson", "inetOrgPerson");
|
||||
assertAttributeValue(record, "cn", "Bob Hamilton");
|
||||
assertAttributeValue(record, "sn", "Hamilton");
|
||||
assertThat(record.containsKey("userPassword")).isFalse();
|
||||
@@ -149,9 +145,8 @@ public class SpringSecurityLdapTemplateITests {
|
||||
|
||||
@Test
|
||||
public void testMultiAttributeRetrievalWithSpecifiedAttributeNames() {
|
||||
Set<Map<String, List<String>>> values = template
|
||||
.searchForMultipleAttributeValues("ou=people", "(uid={0})",
|
||||
new String[] { "bob" }, new String[] { "uid", "cn", "sn" });
|
||||
Set<Map<String, List<String>>> values = template.searchForMultipleAttributeValues("ou=people", "(uid={0})",
|
||||
new String[] { "bob" }, new String[] { "uid", "cn", "sn" });
|
||||
assertThat(values).hasSize(1);
|
||||
Map<String, List<String>> record = values.iterator().next();
|
||||
assertAttributeValue(record, "uid", "bob");
|
||||
@@ -161,8 +156,7 @@ public class SpringSecurityLdapTemplateITests {
|
||||
assertThat(record.containsKey("objectclass")).isFalse();
|
||||
}
|
||||
|
||||
protected void assertAttributeValue(Map<String, List<String>> record,
|
||||
String attributeName, String... values) {
|
||||
protected void assertAttributeValue(Map<String, List<String>> record, String attributeName, String... values) {
|
||||
assertThat(record.containsKey(attributeName)).isTrue();
|
||||
assertThat(record.get(attributeName)).hasSize(values.length);
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
@@ -174,8 +168,8 @@ public class SpringSecurityLdapTemplateITests {
|
||||
public void testRoleSearchForMissingAttributeFailsGracefully() {
|
||||
String param = "uid=ben,ou=people,dc=springframework,dc=org";
|
||||
|
||||
Set<String> values = template.searchForSingleAttributeValues("ou=groups",
|
||||
"(member={0})", new String[] { param }, "mail");
|
||||
Set<String> values = template.searchForSingleAttributeValues("ou=groups", "(member={0})",
|
||||
new String[] { param }, "mail");
|
||||
|
||||
assertThat(values).isEmpty();
|
||||
}
|
||||
@@ -184,8 +178,8 @@ public class SpringSecurityLdapTemplateITests {
|
||||
public void roleSearchWithEscapedCharacterSucceeds() {
|
||||
String param = "cn=mouse\\, jerry,ou=people,dc=springframework,dc=org";
|
||||
|
||||
Set<String> values = template.searchForSingleAttributeValues("ou=groups",
|
||||
"(member={0})", new String[] { param }, "cn");
|
||||
Set<String> values = template.searchForSingleAttributeValues("ou=groups", "(member={0})",
|
||||
new String[] { param }, "cn");
|
||||
|
||||
assertThat(values).hasSize(1);
|
||||
}
|
||||
@@ -205,9 +199,8 @@ public class SpringSecurityLdapTemplateITests {
|
||||
controls.setReturningAttributes(null);
|
||||
String param = "cn=mouse\\, jerry,ou=people,dc=springframework,dc=org";
|
||||
|
||||
javax.naming.NamingEnumeration<SearchResult> results = ctx.search(
|
||||
"ou=groups,dc=springframework,dc=org", "(member={0})",
|
||||
new String[] { param }, controls);
|
||||
javax.naming.NamingEnumeration<SearchResult> results = ctx.search("ou=groups,dc=springframework,dc=org",
|
||||
"(member={0})", new String[] { param }, controls);
|
||||
|
||||
assertThat(results.hasMore()).as("Expected a result").isTrue();
|
||||
}
|
||||
|
||||
@@ -35,7 +35,6 @@ import org.springframework.test.context.junit4.SpringRunner;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.fail;
|
||||
|
||||
|
||||
/**
|
||||
* Tests for {@link BindAuthenticator}.
|
||||
*
|
||||
@@ -45,12 +44,15 @@ import static org.assertj.core.api.Assertions.fail;
|
||||
@RunWith(SpringRunner.class)
|
||||
@ContextConfiguration(classes = ApacheDsContainerConfig.class)
|
||||
public class BindAuthenticatorTests {
|
||||
|
||||
// ~ Instance fields
|
||||
// ================================================================================================
|
||||
|
||||
@Autowired
|
||||
private DefaultSpringSecurityContextSource contextSource;
|
||||
|
||||
private BindAuthenticator authenticator;
|
||||
|
||||
private Authentication bob;
|
||||
|
||||
// ~ Methods
|
||||
@@ -66,19 +68,16 @@ public class BindAuthenticatorTests {
|
||||
|
||||
@Test(expected = BadCredentialsException.class)
|
||||
public void emptyPasswordIsRejected() {
|
||||
this.authenticator
|
||||
.authenticate(new UsernamePasswordAuthenticationToken("jen", ""));
|
||||
this.authenticator.authenticate(new UsernamePasswordAuthenticationToken("jen", ""));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAuthenticationWithCorrectPasswordSucceeds() {
|
||||
this.authenticator.setUserDnPatterns(
|
||||
new String[] { "uid={0},ou=people", "cn={0},ou=people" });
|
||||
this.authenticator.setUserDnPatterns(new String[] { "uid={0},ou=people", "cn={0},ou=people" });
|
||||
|
||||
DirContextOperations user = this.authenticator.authenticate(this.bob);
|
||||
assertThat(user.getStringAttribute("uid")).isEqualTo("bob");
|
||||
this.authenticator.authenticate(new UsernamePasswordAuthenticationToken(
|
||||
"mouse, jerry", "jerryspassword"));
|
||||
this.authenticator.authenticate(new UsernamePasswordAuthenticationToken("mouse, jerry", "jerryspassword"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -86,8 +85,7 @@ public class BindAuthenticatorTests {
|
||||
this.authenticator.setUserDnPatterns(new String[] { "uid={0},ou=people" });
|
||||
|
||||
try {
|
||||
this.authenticator.authenticate(new UsernamePasswordAuthenticationToken(
|
||||
"nonexistentsuser", "password"));
|
||||
this.authenticator.authenticate(new UsernamePasswordAuthenticationToken("nonexistentsuser", "password"));
|
||||
fail("Shouldn't be able to bind with invalid username");
|
||||
}
|
||||
catch (BadCredentialsException expected) {
|
||||
@@ -98,28 +96,21 @@ public class BindAuthenticatorTests {
|
||||
public void testAuthenticationWithUserSearch() throws Exception {
|
||||
// DirContextAdapter ctx = new DirContextAdapter(new
|
||||
// DistinguishedName("uid=bob,ou=people"));
|
||||
this.authenticator.setUserSearch(new FilterBasedLdapUserSearch("ou=people",
|
||||
"(uid={0})", this.contextSource));
|
||||
this.authenticator.setUserSearch(new FilterBasedLdapUserSearch("ou=people", "(uid={0})", this.contextSource));
|
||||
this.authenticator.afterPropertiesSet();
|
||||
DirContextOperations result = this.authenticator.authenticate(this.bob);
|
||||
//ensure we are getting the same attributes back
|
||||
// ensure we are getting the same attributes back
|
||||
assertThat(result.getStringAttribute("cn")).isEqualTo("Bob Hamilton");
|
||||
// SEC-1444
|
||||
this.authenticator.setUserSearch(new FilterBasedLdapUserSearch("ou=people",
|
||||
"(cn={0})", this.contextSource));
|
||||
this.authenticator.authenticate(new UsernamePasswordAuthenticationToken(
|
||||
"mouse, jerry", "jerryspassword"));
|
||||
this.authenticator.authenticate(new UsernamePasswordAuthenticationToken(
|
||||
"slash/guy", "slashguyspassword"));
|
||||
this.authenticator.setUserSearch(new FilterBasedLdapUserSearch("ou=people", "(cn={0})", this.contextSource));
|
||||
this.authenticator.authenticate(new UsernamePasswordAuthenticationToken("mouse, jerry", "jerryspassword"));
|
||||
this.authenticator.authenticate(new UsernamePasswordAuthenticationToken("slash/guy", "slashguyspassword"));
|
||||
// SEC-1661
|
||||
this.authenticator.setUserSearch(new FilterBasedLdapUserSearch(
|
||||
"ou=\\\"quoted people\\\"", "(cn={0})", this.contextSource));
|
||||
this.authenticator.authenticate(new UsernamePasswordAuthenticationToken(
|
||||
"quote\"guy", "quoteguyspassword"));
|
||||
this.authenticator.setUserSearch(
|
||||
new FilterBasedLdapUserSearch("", "(cn={0})", this.contextSource));
|
||||
this.authenticator.authenticate(new UsernamePasswordAuthenticationToken(
|
||||
"quote\"guy", "quoteguyspassword"));
|
||||
new FilterBasedLdapUserSearch("ou=\\\"quoted people\\\"", "(cn={0})", this.contextSource));
|
||||
this.authenticator.authenticate(new UsernamePasswordAuthenticationToken("quote\"guy", "quoteguyspassword"));
|
||||
this.authenticator.setUserSearch(new FilterBasedLdapUserSearch("", "(cn={0})", this.contextSource));
|
||||
this.authenticator.authenticate(new UsernamePasswordAuthenticationToken("quote\"guy", "quoteguyspassword"));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -148,8 +139,7 @@ public class BindAuthenticatorTests {
|
||||
this.authenticator.setUserDnPatterns(new String[] { "uid={0},ou=people" });
|
||||
|
||||
try {
|
||||
this.authenticator.authenticate(
|
||||
new UsernamePasswordAuthenticationToken("bob", "wrongpassword"));
|
||||
this.authenticator.authenticate(new UsernamePasswordAuthenticationToken("bob", "wrongpassword"));
|
||||
fail("Shouldn't be able to bind with wrong password");
|
||||
}
|
||||
catch (BadCredentialsException expected) {
|
||||
@@ -159,7 +149,7 @@ public class BindAuthenticatorTests {
|
||||
@Test
|
||||
public void testUserDnPatternReturnsCorrectDn() {
|
||||
this.authenticator.setUserDnPatterns(new String[] { "cn={0},ou=people" });
|
||||
assertThat(this.authenticator.getUserDns("Joe").get(0))
|
||||
.isEqualTo("cn=Joe,ou=people");
|
||||
assertThat(this.authenticator.getUserDns("Joe").get(0)).isEqualTo("cn=Joe,ou=people");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -46,13 +46,17 @@ import static org.assertj.core.api.Assertions.*;
|
||||
@RunWith(SpringRunner.class)
|
||||
@ContextConfiguration(classes = ApacheDsContainerConfig.class)
|
||||
public class PasswordComparisonAuthenticatorTests {
|
||||
|
||||
// ~ Instance fields
|
||||
// ================================================================================================
|
||||
|
||||
@Autowired
|
||||
private DefaultSpringSecurityContextSource contextSource;
|
||||
|
||||
private PasswordComparisonAuthenticator authenticator;
|
||||
|
||||
private Authentication bob;
|
||||
|
||||
private Authentication ben;
|
||||
|
||||
// ~ Methods
|
||||
@@ -82,8 +86,7 @@ public class PasswordComparisonAuthenticatorTests {
|
||||
authenticator.afterPropertiesSet();
|
||||
|
||||
try {
|
||||
authenticator.authenticate(new UsernamePasswordAuthenticationToken("Joe",
|
||||
"pass"));
|
||||
authenticator.authenticate(new UsernamePasswordAuthenticationToken("Joe", "pass"));
|
||||
fail("Expected exception on failed user search");
|
||||
}
|
||||
catch (UsernameNotFoundException expected) {
|
||||
@@ -94,14 +97,12 @@ public class PasswordComparisonAuthenticatorTests {
|
||||
public void testLdapPasswordCompareFailsWithWrongPassword() {
|
||||
// Don't retrieve the password
|
||||
authenticator.setUserAttributes(new String[] { "uid", "cn", "sn" });
|
||||
authenticator.authenticate(new UsernamePasswordAuthenticationToken("bob",
|
||||
"wrongpass"));
|
||||
authenticator.authenticate(new UsernamePasswordAuthenticationToken("bob", "wrongpass"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleDnPatternsWorkOk() {
|
||||
authenticator.setUserDnPatterns(new String[] { "uid={0},ou=nonexistent",
|
||||
"uid={0},ou=people" });
|
||||
authenticator.setUserDnPatterns(new String[] { "uid={0},ou=nonexistent", "uid={0},ou=people" });
|
||||
authenticator.authenticate(bob);
|
||||
}
|
||||
|
||||
@@ -110,8 +111,7 @@ public class PasswordComparisonAuthenticatorTests {
|
||||
authenticator.setUserAttributes(new String[] { "uid", "userPassword" });
|
||||
|
||||
DirContextAdapter user = (DirContextAdapter) authenticator.authenticate(bob);
|
||||
assertThat(user
|
||||
.getAttributes().size()).withFailMessage("Should have retrieved 2 attribute (uid)").isEqualTo(2);
|
||||
assertThat(user.getAttributes().size()).withFailMessage("Should have retrieved 2 attribute (uid)").isEqualTo(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -145,8 +145,7 @@ public class PasswordComparisonAuthenticatorTests {
|
||||
public void testLdapCompareWithDifferentPasswordAttributeSucceeds() {
|
||||
authenticator.setUserAttributes(new String[] { "uid" });
|
||||
authenticator.setPasswordAttributeName("cn");
|
||||
authenticator.authenticate(new UsernamePasswordAuthenticationToken("ben",
|
||||
"Ben Alex"));
|
||||
authenticator.authenticate(new UsernamePasswordAuthenticationToken("ben", "Ben Alex"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -155,12 +154,11 @@ public class PasswordComparisonAuthenticatorTests {
|
||||
authenticator.setPasswordEncoder(NoOpPasswordEncoder.getInstance());
|
||||
assertThat(authenticator.getUserDns("Bob")).withFailMessage("User DN matches shouldn't be available").isEmpty();
|
||||
|
||||
DirContextAdapter ctx = new DirContextAdapter(new DistinguishedName(
|
||||
"uid=Bob,ou=people"));
|
||||
DirContextAdapter ctx = new DirContextAdapter(new DistinguishedName("uid=Bob,ou=people"));
|
||||
ctx.setAttributeValue("userPassword", "bobspassword");
|
||||
|
||||
authenticator.setUserSearch(new MockUserSearch(ctx));
|
||||
authenticator.authenticate(new UsernamePasswordAuthenticationToken(
|
||||
"shouldntbeused", "bobspassword"));
|
||||
authenticator.authenticate(new UsernamePasswordAuthenticationToken("shouldntbeused", "bobspassword"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -47,8 +47,7 @@ public class FilterBasedLdapUserSearchTests {
|
||||
|
||||
@Test
|
||||
public void basicSearchSucceeds() throws Exception {
|
||||
FilterBasedLdapUserSearch locator = new FilterBasedLdapUserSearch("ou=people",
|
||||
"(uid={0})", this.contextSource);
|
||||
FilterBasedLdapUserSearch locator = new FilterBasedLdapUserSearch("ou=people", "(uid={0})", this.contextSource);
|
||||
locator.setSearchSubtree(false);
|
||||
locator.setSearchTimeLimit(0);
|
||||
locator.setDerefLinkFlag(false);
|
||||
@@ -61,8 +60,7 @@ public class FilterBasedLdapUserSearchTests {
|
||||
|
||||
@Test
|
||||
public void searchForNameWithCommaSucceeds() throws Exception {
|
||||
FilterBasedLdapUserSearch locator = new FilterBasedLdapUserSearch("ou=people",
|
||||
"(uid={0})", this.contextSource);
|
||||
FilterBasedLdapUserSearch locator = new FilterBasedLdapUserSearch("ou=people", "(uid={0})", this.contextSource);
|
||||
locator.setSearchSubtree(false);
|
||||
|
||||
DirContextOperations jerry = locator.searchForUser("jerry");
|
||||
@@ -74,8 +72,7 @@ public class FilterBasedLdapUserSearchTests {
|
||||
// Try some funny business with filters.
|
||||
@Test
|
||||
public void extraFilterPartToExcludeBob() {
|
||||
FilterBasedLdapUserSearch locator = new FilterBasedLdapUserSearch(
|
||||
"ou=people",
|
||||
FilterBasedLdapUserSearch locator = new FilterBasedLdapUserSearch("ou=people",
|
||||
"(&(cn=*)(!(|(uid={0})(uid=rod)(uid=jerry)(uid=slashguy)(uid=javadude)(uid=groovydude)(uid=closuredude)(uid=scaladude))))",
|
||||
this.contextSource);
|
||||
|
||||
@@ -86,23 +83,20 @@ public class FilterBasedLdapUserSearchTests {
|
||||
|
||||
@Test(expected = IncorrectResultSizeDataAccessException.class)
|
||||
public void searchFailsOnMultipleMatches() {
|
||||
FilterBasedLdapUserSearch locator = new FilterBasedLdapUserSearch("ou=people",
|
||||
"(cn=*)", this.contextSource);
|
||||
FilterBasedLdapUserSearch locator = new FilterBasedLdapUserSearch("ou=people", "(cn=*)", this.contextSource);
|
||||
locator.searchForUser("Ignored");
|
||||
}
|
||||
|
||||
@Test(expected = UsernameNotFoundException.class)
|
||||
public void searchForInvalidUserFails() {
|
||||
FilterBasedLdapUserSearch locator = new FilterBasedLdapUserSearch("ou=people",
|
||||
"(uid={0})", this.contextSource);
|
||||
FilterBasedLdapUserSearch locator = new FilterBasedLdapUserSearch("ou=people", "(uid={0})", this.contextSource);
|
||||
locator.searchForUser("Joe");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void subTreeSearchSucceeds() throws Exception {
|
||||
// Don't set the searchBase, so search from the root.
|
||||
FilterBasedLdapUserSearch locator = new FilterBasedLdapUserSearch("", "(cn={0})",
|
||||
this.contextSource);
|
||||
FilterBasedLdapUserSearch locator = new FilterBasedLdapUserSearch("", "(cn={0})", this.contextSource);
|
||||
locator.setSearchSubtree(true);
|
||||
|
||||
DirContextOperations ben = locator.searchForUser("Ben Alex");
|
||||
@@ -113,8 +107,8 @@ public class FilterBasedLdapUserSearchTests {
|
||||
|
||||
@Test
|
||||
public void searchWithDifferentSearchBaseIsSuccessful() {
|
||||
FilterBasedLdapUserSearch locator = new FilterBasedLdapUserSearch(
|
||||
"ou=otherpeople", "(cn={0})", this.contextSource);
|
||||
FilterBasedLdapUserSearch locator = new FilterBasedLdapUserSearch("ou=otherpeople", "(cn={0})",
|
||||
this.contextSource);
|
||||
DirContextOperations joe = locator.searchForUser("Joe Smeth");
|
||||
assertThat(joe.getStringAttribute("cn")).isEqualTo("Joe Smeth");
|
||||
}
|
||||
|
||||
@@ -50,10 +50,8 @@ public class ApacheDSContainerTests {
|
||||
// SEC-2162
|
||||
@Test
|
||||
public void failsToStartThrowsException() throws Exception {
|
||||
ApacheDSContainer server1 = new ApacheDSContainer("dc=springframework,dc=org",
|
||||
"classpath:test-server.ldif");
|
||||
ApacheDSContainer server2 = new ApacheDSContainer("dc=springframework,dc=org",
|
||||
"classpath:missing.ldif");
|
||||
ApacheDSContainer server1 = new ApacheDSContainer("dc=springframework,dc=org", "classpath:test-server.ldif");
|
||||
ApacheDSContainer server2 = new ApacheDSContainer("dc=springframework,dc=org", "classpath:missing.ldif");
|
||||
List<Integer> ports = getDefaultPorts(1);
|
||||
server1.setPort(ports.get(0));
|
||||
server2.setPort(ports.get(0));
|
||||
@@ -83,10 +81,8 @@ public class ApacheDSContainerTests {
|
||||
// SEC-2161
|
||||
@Test
|
||||
public void multipleInstancesSimultanciously() throws Exception {
|
||||
ApacheDSContainer server1 = new ApacheDSContainer("dc=springframework,dc=org",
|
||||
"classpath:test-server.ldif");
|
||||
ApacheDSContainer server2 = new ApacheDSContainer("dc=springframework,dc=org",
|
||||
"classpath:test-server.ldif");
|
||||
ApacheDSContainer server1 = new ApacheDSContainer("dc=springframework,dc=org", "classpath:test-server.ldif");
|
||||
ApacheDSContainer server2 = new ApacheDSContainer("dc=springframework,dc=org", "classpath:test-server.ldif");
|
||||
List<Integer> ports = getDefaultPorts(2);
|
||||
server1.setPort(ports.get(0));
|
||||
server2.setPort(ports.get(1));
|
||||
@@ -110,8 +106,7 @@ public class ApacheDSContainerTests {
|
||||
|
||||
@Test
|
||||
public void startWithLdapOverSslWithoutCertificate() throws Exception {
|
||||
ApacheDSContainer server = new ApacheDSContainer("dc=springframework,dc=org",
|
||||
"classpath:test-server.ldif");
|
||||
ApacheDSContainer server = new ApacheDSContainer("dc=springframework,dc=org", "classpath:test-server.ldif");
|
||||
List<Integer> ports = getDefaultPorts(1);
|
||||
server.setPort(ports.get(0));
|
||||
server.setLdapOverSslEnabled(true);
|
||||
@@ -120,21 +115,21 @@ public class ApacheDSContainerTests {
|
||||
server.afterPropertiesSet();
|
||||
fail("Expected an IllegalArgumentException to be thrown.");
|
||||
}
|
||||
catch (IllegalArgumentException e){
|
||||
catch (IllegalArgumentException e) {
|
||||
assertThat(e).hasMessage("When LdapOverSsl is enabled, the keyStoreFile property must be set.");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void startWithLdapOverSslWithWrongPassword() throws Exception {
|
||||
final ClassPathResource keyStoreResource = new ClassPathResource("/org/springframework/security/ldap/server/spring.keystore");
|
||||
final ClassPathResource keyStoreResource = new ClassPathResource(
|
||||
"/org/springframework/security/ldap/server/spring.keystore");
|
||||
final File temporaryKeyStoreFile = new File(temporaryFolder.getRoot(), "spring.keystore");
|
||||
FileCopyUtils.copy(keyStoreResource.getInputStream(), new FileOutputStream(temporaryKeyStoreFile));
|
||||
|
||||
assertThat(temporaryKeyStoreFile).isFile();
|
||||
|
||||
ApacheDSContainer server = new ApacheDSContainer("dc=springframework,dc=org",
|
||||
"classpath:test-server.ldif");
|
||||
ApacheDSContainer server = new ApacheDSContainer("dc=springframework,dc=org", "classpath:test-server.ldif");
|
||||
|
||||
List<Integer> ports = getDefaultPorts(1);
|
||||
server.setPort(ports.get(0));
|
||||
@@ -147,15 +142,15 @@ public class ApacheDSContainerTests {
|
||||
server.afterPropertiesSet();
|
||||
fail("Expected a RuntimeException to be thrown.");
|
||||
}
|
||||
catch (RuntimeException e){
|
||||
catch (RuntimeException e) {
|
||||
assertThat(e).hasMessage("Server startup failed");
|
||||
assertThat(e).hasRootCauseInstanceOf(UnrecoverableKeyException.class);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This test starts an LDAP server using LDAPs (LDAP over SSL). A self-signed certificate is being used, which was
|
||||
* previously generated with:
|
||||
* This test starts an LDAP server using LDAPs (LDAP over SSL). A self-signed
|
||||
* certificate is being used, which was previously generated with:
|
||||
*
|
||||
* <pre>
|
||||
* {@code
|
||||
@@ -168,14 +163,14 @@ public class ApacheDSContainerTests {
|
||||
@Test
|
||||
public void startWithLdapOverSsl() throws Exception {
|
||||
|
||||
final ClassPathResource keyStoreResource = new ClassPathResource("/org/springframework/security/ldap/server/spring.keystore");
|
||||
final ClassPathResource keyStoreResource = new ClassPathResource(
|
||||
"/org/springframework/security/ldap/server/spring.keystore");
|
||||
final File temporaryKeyStoreFile = new File(temporaryFolder.getRoot(), "spring.keystore");
|
||||
FileCopyUtils.copy(keyStoreResource.getInputStream(), new FileOutputStream(temporaryKeyStoreFile));
|
||||
|
||||
assertThat(temporaryKeyStoreFile).isFile();
|
||||
|
||||
ApacheDSContainer server = new ApacheDSContainer("dc=springframework,dc=org",
|
||||
"classpath:test-server.ldif");
|
||||
ApacheDSContainer server = new ApacheDSContainer("dc=springframework,dc=org", "classpath:test-server.ldif");
|
||||
|
||||
List<Integer> ports = getDefaultPorts(1);
|
||||
server.setPort(ports.get(0));
|
||||
@@ -216,8 +211,7 @@ public class ApacheDSContainerTests {
|
||||
|
||||
@Test
|
||||
public void afterPropertiesSetWhenPortIsZeroThenRandomPortIsSelected() throws Exception {
|
||||
ApacheDSContainer server = new ApacheDSContainer("dc=springframework,dc=org",
|
||||
"classpath:test-server.ldif");
|
||||
ApacheDSContainer server = new ApacheDSContainer("dc=springframework,dc=org", "classpath:test-server.ldif");
|
||||
server.setPort(0);
|
||||
try {
|
||||
server.afterPropertiesSet();
|
||||
@@ -229,4 +223,5 @@ public class ApacheDSContainerTests {
|
||||
server.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -34,16 +34,17 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
public class ApacheDSEmbeddedLdifTests {
|
||||
|
||||
private static final String LDAP_ROOT = "ou=ssattributes,dc=springframework,dc=org";
|
||||
|
||||
private static final int LDAP_PORT = 52389;
|
||||
|
||||
private ApacheDSContainer server;
|
||||
|
||||
private SpringSecurityLdapTemplate ldapTemplate;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
// TODO: InMemoryXmlApplicationContext would be useful here, but it is not visible
|
||||
this.server = new ApacheDSContainer(LDAP_ROOT,
|
||||
"classpath:test-server-custom-attribute-types.ldif");
|
||||
this.server = new ApacheDSContainer(LDAP_ROOT, "classpath:test-server-custom-attribute-types.ldif");
|
||||
this.server.setPort(LDAP_PORT);
|
||||
this.server.afterPropertiesSet();
|
||||
|
||||
@@ -68,10 +69,10 @@ public class ApacheDSEmbeddedLdifTests {
|
||||
@Ignore // Not fixed yet
|
||||
@Test // SEC-2387
|
||||
public void customAttributeTypesShouldBeProperlyCreatedWhenLoadedFromLdif() {
|
||||
assertThat(this.ldapTemplate.compare("uid=objectWithCustomAttribute1", "uid",
|
||||
"objectWithCustomAttribute1")).isTrue();
|
||||
assertThat(this.ldapTemplate.compare("uid=objectWithCustomAttribute1",
|
||||
"customAttribute", "I am custom")).isTrue();
|
||||
assertThat(this.ldapTemplate.compare("uid=objectWithCustomAttribute1", "uid", "objectWithCustomAttribute1"))
|
||||
.isTrue();
|
||||
assertThat(this.ldapTemplate.compare("uid=objectWithCustomAttribute1", "customAttribute", "I am custom"))
|
||||
.isTrue();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -59,6 +59,7 @@ public class UnboundIdContainerLdifTests {
|
||||
|
||||
@Configuration
|
||||
static class CustomLdifConfig {
|
||||
|
||||
private UnboundIdContainer container = new UnboundIdContainer("dc=springframework,dc=org",
|
||||
"classpath:test-server.ldif");
|
||||
|
||||
@@ -70,14 +71,15 @@ public class UnboundIdContainerLdifTests {
|
||||
|
||||
@Bean
|
||||
ContextSource contextSource(UnboundIdContainer container) {
|
||||
return new DefaultSpringSecurityContextSource("ldap://127.0.0.1:"
|
||||
+ container.getPort() + "/dc=springframework,dc=org");
|
||||
return new DefaultSpringSecurityContextSource(
|
||||
"ldap://127.0.0.1:" + container.getPort() + "/dc=springframework,dc=org");
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
void shutdown() {
|
||||
this.container.stop();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -93,6 +95,7 @@ public class UnboundIdContainerLdifTests {
|
||||
|
||||
@Configuration
|
||||
static class WildcardLdifConfig {
|
||||
|
||||
private UnboundIdContainer container = new UnboundIdContainer("dc=springframework,dc=org",
|
||||
"classpath*:test-server.ldif");
|
||||
|
||||
@@ -104,14 +107,15 @@ public class UnboundIdContainerLdifTests {
|
||||
|
||||
@Bean
|
||||
ContextSource contextSource(UnboundIdContainer container) {
|
||||
return new DefaultSpringSecurityContextSource("ldap://127.0.0.1:"
|
||||
+ container.getPort() + "/dc=springframework,dc=org");
|
||||
return new DefaultSpringSecurityContextSource(
|
||||
"ldap://127.0.0.1:" + container.getPort() + "/dc=springframework,dc=org");
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
void shutdown() {
|
||||
this.container.stop();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -119,7 +123,8 @@ public class UnboundIdContainerLdifTests {
|
||||
try {
|
||||
appCtx = new AnnotationConfigApplicationContext(MalformedLdifConfig.class);
|
||||
failBecauseExceptionWasNotThrown(IllegalStateException.class);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
catch (Exception e) {
|
||||
assertThat(e.getCause()).isInstanceOf(IllegalStateException.class);
|
||||
assertThat(e.getMessage()).contains("Unable to load LDIF classpath:test-server-malformed.txt");
|
||||
}
|
||||
@@ -127,6 +132,7 @@ public class UnboundIdContainerLdifTests {
|
||||
|
||||
@Configuration
|
||||
static class MalformedLdifConfig {
|
||||
|
||||
private UnboundIdContainer container = new UnboundIdContainer("dc=springframework,dc=org",
|
||||
"classpath:test-server-malformed.txt");
|
||||
|
||||
@@ -140,6 +146,7 @@ public class UnboundIdContainerLdifTests {
|
||||
void shutdown() {
|
||||
this.container.stop();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -147,7 +154,8 @@ public class UnboundIdContainerLdifTests {
|
||||
try {
|
||||
appCtx = new AnnotationConfigApplicationContext(MissingLdifConfig.class);
|
||||
failBecauseExceptionWasNotThrown(IllegalStateException.class);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
catch (Exception e) {
|
||||
assertThat(e.getCause()).isInstanceOf(IllegalStateException.class);
|
||||
assertThat(e.getMessage()).contains("Unable to load LDIF classpath:does-not-exist.ldif");
|
||||
}
|
||||
@@ -155,6 +163,7 @@ public class UnboundIdContainerLdifTests {
|
||||
|
||||
@Configuration
|
||||
static class MissingLdifConfig {
|
||||
|
||||
private UnboundIdContainer container = new UnboundIdContainer("dc=springframework,dc=org",
|
||||
"classpath:does-not-exist.ldif");
|
||||
|
||||
@@ -168,6 +177,7 @@ public class UnboundIdContainerLdifTests {
|
||||
void shutdown() {
|
||||
this.container.stop();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -177,6 +187,7 @@ public class UnboundIdContainerLdifTests {
|
||||
|
||||
@Configuration
|
||||
static class WildcardNoLdifConfig {
|
||||
|
||||
private UnboundIdContainer container = new UnboundIdContainer("dc=springframework,dc=org",
|
||||
"classpath*:*.test.ldif");
|
||||
|
||||
@@ -190,5 +201,7 @@ public class UnboundIdContainerLdifTests {
|
||||
void shutdown() {
|
||||
this.container.stop();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -33,8 +33,7 @@ public class UnboundIdContainerTests {
|
||||
|
||||
@Test
|
||||
public void startLdapServer() throws Exception {
|
||||
UnboundIdContainer server = new UnboundIdContainer("dc=springframework,dc=org",
|
||||
"classpath:test-server.ldif");
|
||||
UnboundIdContainer server = new UnboundIdContainer("dc=springframework,dc=org", "classpath:test-server.ldif");
|
||||
server.setApplicationContext(new GenericApplicationContext());
|
||||
List<Integer> ports = getDefaultPorts(1);
|
||||
server.setPort(ports.get(0));
|
||||
@@ -42,7 +41,8 @@ public class UnboundIdContainerTests {
|
||||
try {
|
||||
server.afterPropertiesSet();
|
||||
assertThat(server.getPort()).isEqualTo(ports.get(0));
|
||||
} finally {
|
||||
}
|
||||
finally {
|
||||
server.destroy();
|
||||
}
|
||||
}
|
||||
@@ -55,7 +55,8 @@ public class UnboundIdContainerTests {
|
||||
try {
|
||||
server.afterPropertiesSet();
|
||||
assertThat(server.getPort()).isNotEqualTo(0);
|
||||
} finally {
|
||||
}
|
||||
finally {
|
||||
server.destroy();
|
||||
}
|
||||
}
|
||||
@@ -70,7 +71,8 @@ public class UnboundIdContainerTests {
|
||||
availablePorts.add(socket.getLocalPort());
|
||||
}
|
||||
return availablePorts;
|
||||
} finally {
|
||||
}
|
||||
finally {
|
||||
for (ServerSocket conn : connections) {
|
||||
conn.close();
|
||||
}
|
||||
|
||||
@@ -37,7 +37,6 @@ import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @author Eddú Meléndez
|
||||
*/
|
||||
@@ -48,6 +47,7 @@ public class DefaultLdapAuthoritiesPopulatorTests {
|
||||
|
||||
@Autowired
|
||||
private ContextSource contextSource;
|
||||
|
||||
private DefaultLdapAuthoritiesPopulator populator;
|
||||
|
||||
// ~ Methods
|
||||
@@ -64,11 +64,9 @@ public class DefaultLdapAuthoritiesPopulatorTests {
|
||||
populator.setDefaultRole("ROLE_USER");
|
||||
assertThat(populator.getContextSource()).isSameAs(this.contextSource);
|
||||
|
||||
DirContextAdapter ctx = new DirContextAdapter(
|
||||
new DistinguishedName("cn=notfound"));
|
||||
DirContextAdapter ctx = new DirContextAdapter(new DistinguishedName("cn=notfound"));
|
||||
|
||||
Collection<GrantedAuthority> authorities = populator.getGrantedAuthorities(ctx,
|
||||
"notfound");
|
||||
Collection<GrantedAuthority> authorities = populator.getGrantedAuthorities(ctx, "notfound");
|
||||
assertThat(authorities).hasSize(1);
|
||||
assertThat(AuthorityUtils.authorityListToSet(authorities).contains("ROLE_USER")).isTrue();
|
||||
}
|
||||
@@ -78,8 +76,8 @@ public class DefaultLdapAuthoritiesPopulatorTests {
|
||||
populator = new DefaultLdapAuthoritiesPopulator(this.contextSource, null);
|
||||
populator.setDefaultRole("ROLE_USER");
|
||||
|
||||
Collection<GrantedAuthority> authorities = populator.getGrantedAuthorities(
|
||||
new DirContextAdapter(new DistinguishedName("cn=notused")), "notused");
|
||||
Collection<GrantedAuthority> authorities = populator
|
||||
.getGrantedAuthorities(new DirContextAdapter(new DistinguishedName("cn=notused")), "notused");
|
||||
assertThat(authorities).hasSize(1);
|
||||
assertThat(AuthorityUtils.authorityListToSet(authorities).contains("ROLE_USER")).isTrue();
|
||||
}
|
||||
@@ -93,11 +91,10 @@ public class DefaultLdapAuthoritiesPopulatorTests {
|
||||
populator.setConvertToUpperCase(true);
|
||||
populator.setGroupSearchFilter("(member={0})");
|
||||
|
||||
DirContextAdapter ctx = new DirContextAdapter(new DistinguishedName(
|
||||
"uid=ben,ou=people,dc=springframework,dc=org"));
|
||||
DirContextAdapter ctx = new DirContextAdapter(
|
||||
new DistinguishedName("uid=ben,ou=people,dc=springframework,dc=org"));
|
||||
|
||||
Set<String> authorities = AuthorityUtils.authorityListToSet(populator
|
||||
.getGrantedAuthorities(ctx, "ben"));
|
||||
Set<String> authorities = AuthorityUtils.authorityListToSet(populator.getGrantedAuthorities(ctx, "ben"));
|
||||
|
||||
assertThat(authorities).as("Should have 2 roles").hasSize(2);
|
||||
|
||||
@@ -111,11 +108,10 @@ public class DefaultLdapAuthoritiesPopulatorTests {
|
||||
populator.setConvertToUpperCase(true);
|
||||
populator.setGroupSearchFilter("(ou={1})");
|
||||
|
||||
DirContextAdapter ctx = new DirContextAdapter(new DistinguishedName(
|
||||
"uid=ben,ou=people,dc=springframework,dc=org"));
|
||||
DirContextAdapter ctx = new DirContextAdapter(
|
||||
new DistinguishedName("uid=ben,ou=people,dc=springframework,dc=org"));
|
||||
|
||||
Set<String> authorities = AuthorityUtils.authorityListToSet(populator
|
||||
.getGrantedAuthorities(ctx, "manager"));
|
||||
Set<String> authorities = AuthorityUtils.authorityListToSet(populator.getGrantedAuthorities(ctx, "manager"));
|
||||
|
||||
assertThat(authorities).as("Should have 1 role").hasSize(1);
|
||||
assertThat(authorities.contains("ROLE_MANAGER")).isTrue();
|
||||
@@ -126,11 +122,10 @@ public class DefaultLdapAuthoritiesPopulatorTests {
|
||||
populator.setGroupRoleAttribute("ou");
|
||||
populator.setConvertToUpperCase(true);
|
||||
|
||||
DirContextAdapter ctx = new DirContextAdapter(new DistinguishedName(
|
||||
"uid=ben,ou=people,dc=springframework,dc=org"));
|
||||
DirContextAdapter ctx = new DirContextAdapter(
|
||||
new DistinguishedName("uid=ben,ou=people,dc=springframework,dc=org"));
|
||||
|
||||
Set<String> authorities = AuthorityUtils.authorityListToSet(populator
|
||||
.getGrantedAuthorities(ctx, "manager"));
|
||||
Set<String> authorities = AuthorityUtils.authorityListToSet(populator.getGrantedAuthorities(ctx, "manager"));
|
||||
|
||||
assertThat(authorities).as("Should have 2 roles").hasSize(2);
|
||||
assertThat(authorities.contains("ROLE_MANAGER")).isTrue();
|
||||
@@ -143,11 +138,10 @@ public class DefaultLdapAuthoritiesPopulatorTests {
|
||||
populator.setConvertToUpperCase(true);
|
||||
populator.setSearchSubtree(true);
|
||||
|
||||
DirContextAdapter ctx = new DirContextAdapter(new DistinguishedName(
|
||||
"uid=ben,ou=people,dc=springframework,dc=org"));
|
||||
DirContextAdapter ctx = new DirContextAdapter(
|
||||
new DistinguishedName("uid=ben,ou=people,dc=springframework,dc=org"));
|
||||
|
||||
Set<String> authorities = AuthorityUtils.authorityListToSet(populator
|
||||
.getGrantedAuthorities(ctx, "manager"));
|
||||
Set<String> authorities = AuthorityUtils.authorityListToSet(populator.getGrantedAuthorities(ctx, "manager"));
|
||||
|
||||
assertThat(authorities).as("Should have 3 roles").hasSize(3);
|
||||
assertThat(authorities.contains("ROLE_MANAGER")).isTrue();
|
||||
@@ -159,15 +153,13 @@ public class DefaultLdapAuthoritiesPopulatorTests {
|
||||
public void extraRolesAreAdded() {
|
||||
populator = new DefaultLdapAuthoritiesPopulator(this.contextSource, null) {
|
||||
@Override
|
||||
protected Set<GrantedAuthority> getAdditionalRoles(DirContextOperations user,
|
||||
String username) {
|
||||
return new HashSet<>(
|
||||
AuthorityUtils.createAuthorityList("ROLE_EXTRA"));
|
||||
protected Set<GrantedAuthority> getAdditionalRoles(DirContextOperations user, String username) {
|
||||
return new HashSet<>(AuthorityUtils.createAuthorityList("ROLE_EXTRA"));
|
||||
}
|
||||
};
|
||||
|
||||
Collection<GrantedAuthority> authorities = populator.getGrantedAuthorities(
|
||||
new DirContextAdapter(new DistinguishedName("cn=notused")), "notused");
|
||||
Collection<GrantedAuthority> authorities = populator
|
||||
.getGrantedAuthorities(new DirContextAdapter(new DistinguishedName("cn=notused")), "notused");
|
||||
assertThat(authorities).hasSize(1);
|
||||
assertThat(AuthorityUtils.authorityListToSet(authorities).contains("ROLE_EXTRA")).isTrue();
|
||||
}
|
||||
@@ -178,11 +170,10 @@ public class DefaultLdapAuthoritiesPopulatorTests {
|
||||
populator.setConvertToUpperCase(true);
|
||||
populator.setGroupSearchFilter("(member={0})");
|
||||
|
||||
DirContextAdapter ctx = new DirContextAdapter(new DistinguishedName(
|
||||
"cn=mouse\\, jerry,ou=people,dc=springframework,dc=org"));
|
||||
DirContextAdapter ctx = new DirContextAdapter(
|
||||
new DistinguishedName("cn=mouse\\, jerry,ou=people,dc=springframework,dc=org"));
|
||||
|
||||
Set<String> authorities = AuthorityUtils.authorityListToSet(populator
|
||||
.getGrantedAuthorities(ctx, "notused"));
|
||||
Set<String> authorities = AuthorityUtils.authorityListToSet(populator.getGrantedAuthorities(ctx, "notused"));
|
||||
|
||||
assertThat(authorities).as("Should have 1 role").hasSize(1);
|
||||
assertThat(authorities.contains("ROLE_MANAGER")).isTrue();
|
||||
@@ -196,8 +187,8 @@ public class DefaultLdapAuthoritiesPopulatorTests {
|
||||
return new LdapAuthority(role, dn);
|
||||
});
|
||||
|
||||
DirContextAdapter ctx = new DirContextAdapter(new DistinguishedName(
|
||||
"cn=mouse\\, jerry,ou=people,dc=springframework,dc=org"));
|
||||
DirContextAdapter ctx = new DirContextAdapter(
|
||||
new DistinguishedName("cn=mouse\\, jerry,ou=people,dc=springframework,dc=org"));
|
||||
|
||||
Collection<GrantedAuthority> authorities = populator.getGrantedAuthorities(ctx, "notused");
|
||||
|
||||
@@ -208,4 +199,5 @@ public class DefaultLdapAuthoritiesPopulatorTests {
|
||||
public void customAuthoritiesMappingFunctionThrowsIfNull() {
|
||||
populator.setAuthorityMapper(null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ import static org.assertj.core.api.Assertions.assertThatCode;
|
||||
* @author Josh Cummings
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@ContextConfiguration(classes=LdapUserDetailsManagerModifyPasswordTests.UnboundIdContainerConfiguration.class)
|
||||
@ContextConfiguration(classes = LdapUserDetailsManagerModifyPasswordTests.UnboundIdContainerConfiguration.class)
|
||||
public class LdapUserDetailsManagerModifyPasswordTests {
|
||||
|
||||
LdapUserDetailsManager userDetailsManager;
|
||||
@@ -60,15 +60,14 @@ public class LdapUserDetailsManagerModifyPasswordTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser(username="bob", password="bobspassword", authorities="ROLE_USER")
|
||||
@WithMockUser(username = "bob", password = "bobspassword", authorities = "ROLE_USER")
|
||||
public void changePasswordWhenOldPasswordIsIncorrectThenThrowsException() {
|
||||
assertThatCode(() ->
|
||||
this.userDetailsManager.changePassword("wrongoldpassword", "bobsnewpassword"))
|
||||
assertThatCode(() -> this.userDetailsManager.changePassword("wrongoldpassword", "bobsnewpassword"))
|
||||
.isInstanceOf(BadCredentialsException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser(username="bob", password="bobspassword", authorities="ROLE_USER")
|
||||
@WithMockUser(username = "bob", password = "bobspassword", authorities = "ROLE_USER")
|
||||
public void changePasswordWhenOldPasswordIsCorrectThenPasses() {
|
||||
SpringSecurityLdapTemplate template = new SpringSecurityLdapTemplate(this.contextSource);
|
||||
|
||||
@@ -76,11 +75,13 @@ public class LdapUserDetailsManagerModifyPasswordTests {
|
||||
"bobsshinynewandformidablylongandnearlyimpossibletorememberthoughdemonstrablyhardtocrackduetoitshighlevelofentropypasswordofjustice");
|
||||
|
||||
assertThat(template.compare("uid=bob,ou=people", "userPassword",
|
||||
"bobsshinynewandformidablylongandnearlyimpossibletorememberthoughdemonstrablyhardtocrackduetoitshighlevelofentropypasswordofjustice")).isTrue();
|
||||
"bobsshinynewandformidablylongandnearlyimpossibletorememberthoughdemonstrablyhardtocrackduetoitshighlevelofentropypasswordofjustice"))
|
||||
.isTrue();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class UnboundIdContainerConfiguration {
|
||||
|
||||
private UnboundIdContainer container = new UnboundIdContainer("dc=springframework,dc=org",
|
||||
"classpath:test-server.ldif");
|
||||
|
||||
@@ -92,13 +93,15 @@ public class LdapUserDetailsManagerModifyPasswordTests {
|
||||
|
||||
@Bean
|
||||
ContextSource contextSource(UnboundIdContainer container) {
|
||||
return new DefaultSpringSecurityContextSource("ldap://127.0.0.1:"
|
||||
+ container.getPort() + "/dc=springframework,dc=org");
|
||||
return new DefaultSpringSecurityContextSource(
|
||||
"ldap://127.0.0.1:" + container.getPort() + "/dc=springframework,dc=org");
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
void shutdown() {
|
||||
this.container.stop();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -52,8 +52,8 @@ public class LdapUserDetailsManagerTests {
|
||||
@Autowired
|
||||
private ContextSource contextSource;
|
||||
|
||||
private static final List<GrantedAuthority> TEST_AUTHORITIES = AuthorityUtils.createAuthorityList(
|
||||
"ROLE_CLOWNS", "ROLE_ACROBATS");
|
||||
private static final List<GrantedAuthority> TEST_AUTHORITIES = AuthorityUtils.createAuthorityList("ROLE_CLOWNS",
|
||||
"ROLE_ACROBATS");
|
||||
|
||||
private LdapUserDetailsManager mgr;
|
||||
|
||||
@@ -76,8 +76,7 @@ public class LdapUserDetailsManagerTests {
|
||||
|
||||
group.setAttributeValue("objectclass", "groupOfNames");
|
||||
group.setAttributeValue("cn", "clowns");
|
||||
group.setAttributeValue("member",
|
||||
"cn=nobody,ou=test people,dc=springframework,dc=org");
|
||||
group.setAttributeValue("member", "cn=nobody,ou=test people,dc=springframework,dc=org");
|
||||
template.bind("cn=clowns,ou=testgroups", group, null);
|
||||
|
||||
group.setAttributeValue("cn", "acrobats");
|
||||
@@ -185,9 +184,7 @@ public class LdapUserDetailsManagerTests {
|
||||
}
|
||||
|
||||
// Check that no authorities are left
|
||||
assertThat(
|
||||
mgr.getUserAuthorities(mgr.usernameMapper.buildDn("don"), "don")).hasSize(
|
||||
0);
|
||||
assertThat(mgr.getUserAuthorities(mgr.usernameMapper.buildDn("don"), "don")).hasSize(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -203,13 +200,12 @@ public class LdapUserDetailsManagerTests {
|
||||
mgr.createUser(p.createUserDetails());
|
||||
|
||||
SecurityContextHolder.getContext().setAuthentication(
|
||||
new UsernamePasswordAuthenticationToken("johnyossarian",
|
||||
"yossarianspassword", TEST_AUTHORITIES));
|
||||
new UsernamePasswordAuthenticationToken("johnyossarian", "yossarianspassword", TEST_AUTHORITIES));
|
||||
|
||||
mgr.changePassword("yossarianspassword", "yossariansnewpassword");
|
||||
|
||||
assertThat(template.compare("uid=johnyossarian,ou=test people", "userPassword",
|
||||
"yossariansnewpassword")).isTrue();
|
||||
assertThat(template.compare("uid=johnyossarian,ou=test people", "userPassword", "yossariansnewpassword"))
|
||||
.isTrue();
|
||||
}
|
||||
|
||||
@Test(expected = BadCredentialsException.class)
|
||||
@@ -225,9 +221,9 @@ public class LdapUserDetailsManagerTests {
|
||||
mgr.createUser(p.createUserDetails());
|
||||
|
||||
SecurityContextHolder.getContext().setAuthentication(
|
||||
new UsernamePasswordAuthenticationToken("johnyossarian",
|
||||
"yossarianspassword", TEST_AUTHORITIES));
|
||||
new UsernamePasswordAuthenticationToken("johnyossarian", "yossarianspassword", TEST_AUTHORITIES));
|
||||
|
||||
mgr.changePassword("wrongpassword", "yossariansnewpassword");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -43,12 +43,19 @@ public class NestedLdapAuthoritiesPopulatorTests {
|
||||
|
||||
@Autowired
|
||||
private ContextSource contextSource;
|
||||
|
||||
private NestedLdapAuthoritiesPopulator populator;
|
||||
|
||||
private LdapAuthority javaDevelopers;
|
||||
|
||||
private LdapAuthority groovyDevelopers;
|
||||
|
||||
private LdapAuthority scalaDevelopers;
|
||||
|
||||
private LdapAuthority closureDevelopers;
|
||||
|
||||
private LdapAuthority jDevelopers;
|
||||
|
||||
private LdapAuthority circularJavaDevelopers;
|
||||
|
||||
// ~ Methods
|
||||
@@ -56,15 +63,13 @@ public class NestedLdapAuthoritiesPopulatorTests {
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
populator = new NestedLdapAuthoritiesPopulator(this.contextSource,
|
||||
"ou=jdeveloper");
|
||||
populator = new NestedLdapAuthoritiesPopulator(this.contextSource, "ou=jdeveloper");
|
||||
populator.setGroupSearchFilter("(member={0})");
|
||||
populator.setIgnorePartialResultException(false);
|
||||
populator.setRolePrefix("");
|
||||
populator.setSearchSubtree(true);
|
||||
populator.setConvertToUpperCase(false);
|
||||
jDevelopers = new LdapAuthority("j-developers",
|
||||
"cn=j-developers,ou=jdeveloper,dc=springframework,dc=org");
|
||||
jDevelopers = new LdapAuthority("j-developers", "cn=j-developers,ou=jdeveloper,dc=springframework,dc=org");
|
||||
javaDevelopers = new LdapAuthority("java-developers",
|
||||
"cn=java-developers,ou=jdeveloper,dc=springframework,dc=org");
|
||||
groovyDevelopers = new LdapAuthority("groovy-developers",
|
||||
@@ -79,21 +84,17 @@ public class NestedLdapAuthoritiesPopulatorTests {
|
||||
|
||||
@Test
|
||||
public void testScalaDudeJDevelopersAuthorities() {
|
||||
DirContextAdapter ctx = new DirContextAdapter(
|
||||
"uid=scaladude,ou=people,dc=springframework,dc=org");
|
||||
Collection<GrantedAuthority> authorities = populator.getGrantedAuthorities(ctx,
|
||||
"scaladude");
|
||||
DirContextAdapter ctx = new DirContextAdapter("uid=scaladude,ou=people,dc=springframework,dc=org");
|
||||
Collection<GrantedAuthority> authorities = populator.getGrantedAuthorities(ctx, "scaladude");
|
||||
assertThat(authorities).hasSize(5);
|
||||
assertThat(authorities).isEqualTo(Arrays.asList(javaDevelopers, circularJavaDevelopers,
|
||||
scalaDevelopers, groovyDevelopers, jDevelopers));
|
||||
assertThat(authorities).isEqualTo(
|
||||
Arrays.asList(javaDevelopers, circularJavaDevelopers, scalaDevelopers, groovyDevelopers, jDevelopers));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJavaDudeJDevelopersAuthorities() {
|
||||
DirContextAdapter ctx = new DirContextAdapter(
|
||||
"uid=javadude,ou=people,dc=springframework,dc=org");
|
||||
Collection<GrantedAuthority> authorities = populator.getGrantedAuthorities(ctx,
|
||||
"javadude");
|
||||
DirContextAdapter ctx = new DirContextAdapter("uid=javadude,ou=people,dc=springframework,dc=org");
|
||||
Collection<GrantedAuthority> authorities = populator.getGrantedAuthorities(ctx, "javadude");
|
||||
assertThat(authorities).hasSize(4);
|
||||
assertThat(authorities).contains(javaDevelopers);
|
||||
}
|
||||
@@ -101,36 +102,30 @@ public class NestedLdapAuthoritiesPopulatorTests {
|
||||
@Test
|
||||
public void testScalaDudeJDevelopersAuthoritiesWithSearchLimit() {
|
||||
populator.setMaxSearchDepth(1);
|
||||
DirContextAdapter ctx = new DirContextAdapter(
|
||||
"uid=scaladude,ou=people,dc=springframework,dc=org");
|
||||
Collection<GrantedAuthority> authorities = populator.getGrantedAuthorities(ctx,
|
||||
"scaladude");
|
||||
DirContextAdapter ctx = new DirContextAdapter("uid=scaladude,ou=people,dc=springframework,dc=org");
|
||||
Collection<GrantedAuthority> authorities = populator.getGrantedAuthorities(ctx, "scaladude");
|
||||
assertThat(authorities).hasSize(1);
|
||||
assertThat(authorities).isEqualTo(Arrays.asList(scalaDevelopers));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGroovyDudeJDevelopersAuthorities() {
|
||||
DirContextAdapter ctx = new DirContextAdapter(
|
||||
"uid=groovydude,ou=people,dc=springframework,dc=org");
|
||||
Collection<GrantedAuthority> authorities = populator.getGrantedAuthorities(ctx,
|
||||
"groovydude");
|
||||
DirContextAdapter ctx = new DirContextAdapter("uid=groovydude,ou=people,dc=springframework,dc=org");
|
||||
Collection<GrantedAuthority> authorities = populator.getGrantedAuthorities(ctx, "groovydude");
|
||||
assertThat(authorities).hasSize(4);
|
||||
assertThat(authorities).isEqualTo(Arrays.asList(javaDevelopers, circularJavaDevelopers, groovyDevelopers,
|
||||
jDevelopers));
|
||||
assertThat(authorities)
|
||||
.isEqualTo(Arrays.asList(javaDevelopers, circularJavaDevelopers, groovyDevelopers, jDevelopers));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClosureDudeJDevelopersWithMembershipAsAttributeValues() {
|
||||
populator.setAttributeNames(new HashSet(Arrays.asList("member")));
|
||||
|
||||
DirContextAdapter ctx = new DirContextAdapter(
|
||||
"uid=closuredude,ou=people,dc=springframework,dc=org");
|
||||
Collection<GrantedAuthority> authorities = populator.getGrantedAuthorities(ctx,
|
||||
"closuredude");
|
||||
DirContextAdapter ctx = new DirContextAdapter("uid=closuredude,ou=people,dc=springframework,dc=org");
|
||||
Collection<GrantedAuthority> authorities = populator.getGrantedAuthorities(ctx, "closuredude");
|
||||
assertThat(authorities).hasSize(5);
|
||||
assertThat(authorities).isEqualTo(Arrays.asList(javaDevelopers, circularJavaDevelopers,
|
||||
closureDevelopers, groovyDevelopers, jDevelopers));
|
||||
assertThat(authorities).isEqualTo(Arrays.asList(javaDevelopers, circularJavaDevelopers, closureDevelopers,
|
||||
groovyDevelopers, jDevelopers));
|
||||
|
||||
LdapAuthority[] ldapAuthorities = authorities.toArray(new LdapAuthority[0]);
|
||||
assertThat(ldapAuthorities).hasSize(5);
|
||||
@@ -138,15 +133,16 @@ public class NestedLdapAuthoritiesPopulatorTests {
|
||||
assertThat(ldapAuthorities[0].getAttributes().containsKey("member")).isTrue();
|
||||
assertThat(ldapAuthorities[0].getAttributes().get("member")).isNotNull();
|
||||
assertThat(ldapAuthorities[0].getAttributes().get("member")).hasSize(3);
|
||||
assertThat(ldapAuthorities[0].getFirstAttributeValue("member")).isEqualTo("cn=groovy-developers,ou=jdeveloper,dc=springframework,dc=org");
|
||||
assertThat(ldapAuthorities[0].getFirstAttributeValue("member"))
|
||||
.isEqualTo("cn=groovy-developers,ou=jdeveloper,dc=springframework,dc=org");
|
||||
|
||||
// java group
|
||||
assertThat(ldapAuthorities[1].getAttributes().containsKey("member")).isTrue();
|
||||
assertThat(ldapAuthorities[1].getAttributes().get("member")).isNotNull();
|
||||
assertThat(ldapAuthorities[1].getAttributes().get("member")).hasSize(3);
|
||||
assertThat(groovyDevelopers.getDn()).isEqualTo(ldapAuthorities[1].getFirstAttributeValue("member"));
|
||||
assertThat(ldapAuthorities[2]
|
||||
.getAttributes().get("member")).contains("uid=closuredude,ou=people,dc=springframework,dc=org");
|
||||
assertThat(ldapAuthorities[2].getAttributes().get("member"))
|
||||
.contains("uid=closuredude,ou=people,dc=springframework,dc=org");
|
||||
|
||||
// test non existent attribute
|
||||
assertThat(ldapAuthorities[2].getFirstAttributeValue("test")).isNull();
|
||||
@@ -155,4 +151,5 @@ public class NestedLdapAuthoritiesPopulatorTests {
|
||||
// test role name
|
||||
assertThat(ldapAuthorities[3].getAuthority()).isEqualTo(groovyDevelopers.getAuthority());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -26,7 +26,9 @@ import org.springframework.ldap.core.DistinguishedName;
|
||||
* @author Luke Taylor
|
||||
*/
|
||||
public class DefaultLdapUsernameToDnMapper implements LdapUsernameToDnMapper {
|
||||
|
||||
private final String userDnBase;
|
||||
|
||||
private final String usernameAttribute;
|
||||
|
||||
/**
|
||||
@@ -48,4 +50,5 @@ public class DefaultLdapUsernameToDnMapper implements LdapUsernameToDnMapper {
|
||||
|
||||
return dn;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@ import org.springframework.util.Assert;
|
||||
* @since 2.0
|
||||
*/
|
||||
public class DefaultSpringSecurityContextSource extends LdapContextSource {
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private String rootDn;
|
||||
@@ -55,7 +56,6 @@ public class DefaultSpringSecurityContextSource extends LdapContextSource {
|
||||
* Create and initialize an instance which will connect to the supplied LDAP URL. If
|
||||
* you want to use more than one server for fail-over, rather use the
|
||||
* {@link #DefaultSpringSecurityContextSource(List, String)} constructor.
|
||||
*
|
||||
* @param providerUrl an LDAP URL of the form
|
||||
* <code>ldap://localhost:389/base_dn</code>
|
||||
*/
|
||||
@@ -79,8 +79,7 @@ public class DefaultSpringSecurityContextSource extends LdapContextSource {
|
||||
this.rootDn = urlRootDn;
|
||||
}
|
||||
else if (!this.rootDn.equals(urlRootDn)) {
|
||||
throw new IllegalArgumentException(
|
||||
"Root DNs must be the same when using multiple URLs");
|
||||
throw new IllegalArgumentException("Root DNs must be the same when using multiple URLs");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,8 +95,7 @@ public class DefaultSpringSecurityContextSource extends LdapContextSource {
|
||||
// user.
|
||||
if (!DefaultSpringSecurityContextSource.this.userDn.equals(dn)
|
||||
&& env.containsKey(SUN_LDAP_POOLING_FLAG)) {
|
||||
DefaultSpringSecurityContextSource.this.logger
|
||||
.debug("Removing pooling flag for user " + dn);
|
||||
DefaultSpringSecurityContextSource.this.logger.debug("Removing pooling flag for user " + dn);
|
||||
env.remove(SUN_LDAP_POOLING_FLAG);
|
||||
}
|
||||
}
|
||||
@@ -107,7 +105,6 @@ public class DefaultSpringSecurityContextSource extends LdapContextSource {
|
||||
/**
|
||||
* Create and initialize an instance which will connect of the LDAP Spring Security
|
||||
* Context Source. It will connect to any of the provided LDAP server URLs.
|
||||
*
|
||||
* @param urls A list of string values which are LDAP server URLs. An example would be
|
||||
* <code>ldap://ldap.company.com:389</code>. LDAPS URLs (SSL-secured) may be used as
|
||||
* well, given that Spring Security is able to connect to the server. Note that these
|
||||
@@ -128,7 +125,6 @@ public class DefaultSpringSecurityContextSource extends LdapContextSource {
|
||||
* Builds a Spring LDAP-compliant Provider URL string, i.e. a space-separated list of
|
||||
* LDAP servers with their base DNs. As the base DN must be identical for all servers,
|
||||
* it needs to be supplied only once.
|
||||
*
|
||||
* @param urls A list of string values which are LDAP server URLs. An example would be
|
||||
*
|
||||
* <pre>
|
||||
|
||||
@@ -1,245 +1,241 @@
|
||||
/*
|
||||
* Copyright 2005-2010 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.ldap;
|
||||
|
||||
import org.springframework.ldap.BadLdapGrammarException;
|
||||
|
||||
/**
|
||||
* Helper class to encode and decode ldap names and values.
|
||||
*
|
||||
* <p>
|
||||
* NOTE: This is a copy from Spring LDAP so that both Spring LDAP 1.x and 2.x can be
|
||||
* supported without reflection.
|
||||
* </p>
|
||||
*
|
||||
* @author Adam Skogman
|
||||
* @author Mattias Hellborg Arthursson
|
||||
*/
|
||||
final class LdapEncoder {
|
||||
|
||||
private static final int HEX = 16;
|
||||
private static String[] NAME_ESCAPE_TABLE = new String[96];
|
||||
|
||||
private static String[] FILTER_ESCAPE_TABLE = new String['\\' + 1];
|
||||
|
||||
static {
|
||||
|
||||
// Name encoding table -------------------------------------
|
||||
|
||||
// all below 0x20 (control chars)
|
||||
for (char c = 0; c < ' '; c++) {
|
||||
NAME_ESCAPE_TABLE[c] = "\\" + toTwoCharHex(c);
|
||||
}
|
||||
|
||||
NAME_ESCAPE_TABLE['#'] = "\\#";
|
||||
NAME_ESCAPE_TABLE[','] = "\\,";
|
||||
NAME_ESCAPE_TABLE[';'] = "\\;";
|
||||
NAME_ESCAPE_TABLE['='] = "\\=";
|
||||
NAME_ESCAPE_TABLE['+'] = "\\+";
|
||||
NAME_ESCAPE_TABLE['<'] = "\\<";
|
||||
NAME_ESCAPE_TABLE['>'] = "\\>";
|
||||
NAME_ESCAPE_TABLE['\"'] = "\\\"";
|
||||
NAME_ESCAPE_TABLE['\\'] = "\\\\";
|
||||
|
||||
// Filter encoding table -------------------------------------
|
||||
|
||||
// fill with char itself
|
||||
for (char c = 0; c < FILTER_ESCAPE_TABLE.length; c++) {
|
||||
FILTER_ESCAPE_TABLE[c] = String.valueOf(c);
|
||||
}
|
||||
|
||||
// escapes (RFC2254)
|
||||
FILTER_ESCAPE_TABLE['*'] = "\\2a";
|
||||
FILTER_ESCAPE_TABLE['('] = "\\28";
|
||||
FILTER_ESCAPE_TABLE[')'] = "\\29";
|
||||
FILTER_ESCAPE_TABLE['\\'] = "\\5c";
|
||||
FILTER_ESCAPE_TABLE[0] = "\\00";
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* All static methods - not to be instantiated.
|
||||
*/
|
||||
private LdapEncoder() {
|
||||
}
|
||||
|
||||
protected static String toTwoCharHex(char c) {
|
||||
|
||||
String raw = Integer.toHexString(c).toUpperCase();
|
||||
|
||||
if (raw.length() > 1) {
|
||||
return raw;
|
||||
}
|
||||
else {
|
||||
return "0" + raw;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape a value for use in a filter.
|
||||
*
|
||||
* @param value the value to escape.
|
||||
* @return a properly escaped representation of the supplied value.
|
||||
*/
|
||||
public static String filterEncode(String value) {
|
||||
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// make buffer roomy
|
||||
StringBuilder encodedValue = new StringBuilder(value.length() * 2);
|
||||
|
||||
int length = value.length();
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
|
||||
char c = value.charAt(i);
|
||||
|
||||
if (c < FILTER_ESCAPE_TABLE.length) {
|
||||
encodedValue.append(FILTER_ESCAPE_TABLE[c]);
|
||||
}
|
||||
else {
|
||||
// default: add the char
|
||||
encodedValue.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
return encodedValue.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* LDAP Encodes a value for use with a DN. Escapes for LDAP, not JNDI!
|
||||
*
|
||||
* <br/>
|
||||
* Escapes:<br/>
|
||||
* ' ' [space] - "\ " [if first or last] <br/>
|
||||
* '#' [hash] - "\#" <br/>
|
||||
* ',' [comma] - "\," <br/>
|
||||
* ';' [semicolon] - "\;" <br/>
|
||||
* '= [equals] - "\=" <br/>
|
||||
* '+' [plus] - "\+" <br/>
|
||||
* '<' [less than] - "\<" <br/>
|
||||
* '>' [greater than] - "\>" <br/>
|
||||
* '"' [double quote] - "\"" <br/>
|
||||
* '\' [backslash] - "\\" <br/>
|
||||
*
|
||||
* @param value the value to escape.
|
||||
* @return The escaped value.
|
||||
*/
|
||||
public static String nameEncode(String value) {
|
||||
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// make buffer roomy
|
||||
StringBuilder encodedValue = new StringBuilder(value.length() * 2);
|
||||
|
||||
int length = value.length();
|
||||
int last = length - 1;
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
|
||||
char c = value.charAt(i);
|
||||
|
||||
// space first or last
|
||||
if (c == ' ' && (i == 0 || i == last)) {
|
||||
encodedValue.append("\\ ");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c < NAME_ESCAPE_TABLE.length) {
|
||||
// check in table for escapes
|
||||
String esc = NAME_ESCAPE_TABLE[c];
|
||||
|
||||
if (esc != null) {
|
||||
encodedValue.append(esc);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// default: add the char
|
||||
encodedValue.append(c);
|
||||
}
|
||||
|
||||
return encodedValue.toString();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a value. Converts escaped chars to ordinary chars.
|
||||
*
|
||||
* @param value Trimmed value, so no leading an trailing blanks, except an escaped
|
||||
* space last.
|
||||
* @return The decoded value as a string.
|
||||
* @throws BadLdapGrammarException
|
||||
*/
|
||||
static public String nameDecode(String value) throws BadLdapGrammarException {
|
||||
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// make buffer same size
|
||||
StringBuilder decoded = new StringBuilder(value.length());
|
||||
|
||||
int i = 0;
|
||||
while (i < value.length()) {
|
||||
char currentChar = value.charAt(i);
|
||||
if (currentChar == '\\') {
|
||||
if (value.length() <= i + 1) {
|
||||
// Ending with a single backslash is not allowed
|
||||
throw new BadLdapGrammarException(
|
||||
"Unexpected end of value " + "unterminated '\\'");
|
||||
}
|
||||
else {
|
||||
char nextChar = value.charAt(i + 1);
|
||||
if (nextChar == ',' || nextChar == '=' || nextChar == '+'
|
||||
|| nextChar == '<' || nextChar == '>' || nextChar == '#'
|
||||
|| nextChar == ';' || nextChar == '\\' || nextChar == '\"'
|
||||
|| nextChar == ' ') {
|
||||
// Normal backslash escape
|
||||
decoded.append(nextChar);
|
||||
i += 2;
|
||||
}
|
||||
else {
|
||||
if (value.length() <= i + 2) {
|
||||
throw new BadLdapGrammarException("Unexpected end of value "
|
||||
+ "expected special or hex, found '" + nextChar
|
||||
+ "'");
|
||||
}
|
||||
else {
|
||||
// This should be a hex value
|
||||
String hexString = "" + nextChar + value.charAt(i + 2);
|
||||
decoded.append((char) Integer.parseInt(hexString, HEX));
|
||||
i += 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// This character wasn't escaped - just append it
|
||||
decoded.append(currentChar);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
return decoded.toString();
|
||||
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright 2005-2010 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.ldap;
|
||||
|
||||
import org.springframework.ldap.BadLdapGrammarException;
|
||||
|
||||
/**
|
||||
* Helper class to encode and decode ldap names and values.
|
||||
*
|
||||
* <p>
|
||||
* NOTE: This is a copy from Spring LDAP so that both Spring LDAP 1.x and 2.x can be
|
||||
* supported without reflection.
|
||||
* </p>
|
||||
*
|
||||
* @author Adam Skogman
|
||||
* @author Mattias Hellborg Arthursson
|
||||
*/
|
||||
final class LdapEncoder {
|
||||
|
||||
private static final int HEX = 16;
|
||||
|
||||
private static String[] NAME_ESCAPE_TABLE = new String[96];
|
||||
|
||||
private static String[] FILTER_ESCAPE_TABLE = new String['\\' + 1];
|
||||
|
||||
static {
|
||||
|
||||
// Name encoding table -------------------------------------
|
||||
|
||||
// all below 0x20 (control chars)
|
||||
for (char c = 0; c < ' '; c++) {
|
||||
NAME_ESCAPE_TABLE[c] = "\\" + toTwoCharHex(c);
|
||||
}
|
||||
|
||||
NAME_ESCAPE_TABLE['#'] = "\\#";
|
||||
NAME_ESCAPE_TABLE[','] = "\\,";
|
||||
NAME_ESCAPE_TABLE[';'] = "\\;";
|
||||
NAME_ESCAPE_TABLE['='] = "\\=";
|
||||
NAME_ESCAPE_TABLE['+'] = "\\+";
|
||||
NAME_ESCAPE_TABLE['<'] = "\\<";
|
||||
NAME_ESCAPE_TABLE['>'] = "\\>";
|
||||
NAME_ESCAPE_TABLE['\"'] = "\\\"";
|
||||
NAME_ESCAPE_TABLE['\\'] = "\\\\";
|
||||
|
||||
// Filter encoding table -------------------------------------
|
||||
|
||||
// fill with char itself
|
||||
for (char c = 0; c < FILTER_ESCAPE_TABLE.length; c++) {
|
||||
FILTER_ESCAPE_TABLE[c] = String.valueOf(c);
|
||||
}
|
||||
|
||||
// escapes (RFC2254)
|
||||
FILTER_ESCAPE_TABLE['*'] = "\\2a";
|
||||
FILTER_ESCAPE_TABLE['('] = "\\28";
|
||||
FILTER_ESCAPE_TABLE[')'] = "\\29";
|
||||
FILTER_ESCAPE_TABLE['\\'] = "\\5c";
|
||||
FILTER_ESCAPE_TABLE[0] = "\\00";
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* All static methods - not to be instantiated.
|
||||
*/
|
||||
private LdapEncoder() {
|
||||
}
|
||||
|
||||
protected static String toTwoCharHex(char c) {
|
||||
|
||||
String raw = Integer.toHexString(c).toUpperCase();
|
||||
|
||||
if (raw.length() > 1) {
|
||||
return raw;
|
||||
}
|
||||
else {
|
||||
return "0" + raw;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape a value for use in a filter.
|
||||
* @param value the value to escape.
|
||||
* @return a properly escaped representation of the supplied value.
|
||||
*/
|
||||
public static String filterEncode(String value) {
|
||||
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// make buffer roomy
|
||||
StringBuilder encodedValue = new StringBuilder(value.length() * 2);
|
||||
|
||||
int length = value.length();
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
|
||||
char c = value.charAt(i);
|
||||
|
||||
if (c < FILTER_ESCAPE_TABLE.length) {
|
||||
encodedValue.append(FILTER_ESCAPE_TABLE[c]);
|
||||
}
|
||||
else {
|
||||
// default: add the char
|
||||
encodedValue.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
return encodedValue.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* LDAP Encodes a value for use with a DN. Escapes for LDAP, not JNDI!
|
||||
*
|
||||
* <br/>
|
||||
* Escapes:<br/>
|
||||
* ' ' [space] - "\ " [if first or last] <br/>
|
||||
* '#' [hash] - "\#" <br/>
|
||||
* ',' [comma] - "\," <br/>
|
||||
* ';' [semicolon] - "\;" <br/>
|
||||
* '= [equals] - "\=" <br/>
|
||||
* '+' [plus] - "\+" <br/>
|
||||
* '<' [less than] - "\<" <br/>
|
||||
* '>' [greater than] - "\>" <br/>
|
||||
* '"' [double quote] - "\"" <br/>
|
||||
* '\' [backslash] - "\\" <br/>
|
||||
* @param value the value to escape.
|
||||
* @return The escaped value.
|
||||
*/
|
||||
public static String nameEncode(String value) {
|
||||
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// make buffer roomy
|
||||
StringBuilder encodedValue = new StringBuilder(value.length() * 2);
|
||||
|
||||
int length = value.length();
|
||||
int last = length - 1;
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
|
||||
char c = value.charAt(i);
|
||||
|
||||
// space first or last
|
||||
if (c == ' ' && (i == 0 || i == last)) {
|
||||
encodedValue.append("\\ ");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c < NAME_ESCAPE_TABLE.length) {
|
||||
// check in table for escapes
|
||||
String esc = NAME_ESCAPE_TABLE[c];
|
||||
|
||||
if (esc != null) {
|
||||
encodedValue.append(esc);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// default: add the char
|
||||
encodedValue.append(c);
|
||||
}
|
||||
|
||||
return encodedValue.toString();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a value. Converts escaped chars to ordinary chars.
|
||||
* @param value Trimmed value, so no leading an trailing blanks, except an escaped
|
||||
* space last.
|
||||
* @return The decoded value as a string.
|
||||
* @throws BadLdapGrammarException
|
||||
*/
|
||||
static public String nameDecode(String value) throws BadLdapGrammarException {
|
||||
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// make buffer same size
|
||||
StringBuilder decoded = new StringBuilder(value.length());
|
||||
|
||||
int i = 0;
|
||||
while (i < value.length()) {
|
||||
char currentChar = value.charAt(i);
|
||||
if (currentChar == '\\') {
|
||||
if (value.length() <= i + 1) {
|
||||
// Ending with a single backslash is not allowed
|
||||
throw new BadLdapGrammarException("Unexpected end of value " + "unterminated '\\'");
|
||||
}
|
||||
else {
|
||||
char nextChar = value.charAt(i + 1);
|
||||
if (nextChar == ',' || nextChar == '=' || nextChar == '+' || nextChar == '<' || nextChar == '>'
|
||||
|| nextChar == '#' || nextChar == ';' || nextChar == '\\' || nextChar == '\"'
|
||||
|| nextChar == ' ') {
|
||||
// Normal backslash escape
|
||||
decoded.append(nextChar);
|
||||
i += 2;
|
||||
}
|
||||
else {
|
||||
if (value.length() <= i + 2) {
|
||||
throw new BadLdapGrammarException(
|
||||
"Unexpected end of value " + "expected special or hex, found '" + nextChar + "'");
|
||||
}
|
||||
else {
|
||||
// This should be a hex value
|
||||
String hexString = "" + nextChar + value.charAt(i + 2);
|
||||
decoded.append((char) Integer.parseInt(hexString, HEX));
|
||||
i += 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// This character wasn't escaped - just append it
|
||||
decoded.append(currentChar);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
return decoded.toString();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -23,5 +23,7 @@ import org.springframework.ldap.core.DistinguishedName;
|
||||
* @author Luke Taylor
|
||||
*/
|
||||
public interface LdapUsernameToDnMapper {
|
||||
|
||||
DistinguishedName buildDn(String username);
|
||||
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ import java.net.URISyntaxException;
|
||||
* @author Luke Taylor
|
||||
*/
|
||||
public final class LdapUtils {
|
||||
|
||||
// ~ Static fields/initializers
|
||||
// =====================================================================================
|
||||
|
||||
@@ -82,16 +83,12 @@ public final class LdapUtils {
|
||||
* If the DN is "cn=bob,ou=people,dc=springframework,dc=org" and the base context name
|
||||
* is "ou=people,dc=springframework,dc=org" it would return "cn=bob".
|
||||
* </p>
|
||||
*
|
||||
* @param fullDn the DN
|
||||
* @param baseCtx the context to work out the name relative to.
|
||||
*
|
||||
* @return the
|
||||
*
|
||||
* @throws NamingException any exceptions thrown by the context are propagated.
|
||||
*/
|
||||
public static String getRelativeName(String fullDn, Context baseCtx)
|
||||
throws NamingException {
|
||||
public static String getRelativeName(String fullDn, Context baseCtx) throws NamingException {
|
||||
|
||||
String baseDn = baseCtx.getNameInNamespace();
|
||||
|
||||
@@ -117,8 +114,7 @@ public final class LdapUtils {
|
||||
* Gets the full dn of a name by prepending the name of the context it is relative to.
|
||||
* If the name already contains the base name, it is returned unaltered.
|
||||
*/
|
||||
public static DistinguishedName getFullDn(DistinguishedName dn, Context baseCtx)
|
||||
throws NamingException {
|
||||
public static DistinguishedName getFullDn(DistinguishedName dn, Context baseCtx) throws NamingException {
|
||||
DistinguishedName baseDn = new DistinguishedName(baseCtx.getNameInNamespace());
|
||||
|
||||
if (dn.contains(baseDn)) {
|
||||
@@ -140,8 +136,7 @@ public final class LdapUtils {
|
||||
return (String) passObj;
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException(
|
||||
"Password object was not a String or byte array.");
|
||||
throw new IllegalArgumentException("Password object was not a String or byte array.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,9 +146,7 @@ public final class LdapUtils {
|
||||
* For example, the URL <tt>ldap://monkeymachine:11389/dc=springframework,dc=org</tt>
|
||||
* has the root DN "dc=springframework,dc=org".
|
||||
* </p>
|
||||
*
|
||||
* @param url the LDAP URL
|
||||
*
|
||||
* @return the root DN
|
||||
*/
|
||||
public static String parseRootDnFromUrl(String url) {
|
||||
@@ -193,10 +186,10 @@ public final class LdapUtils {
|
||||
return new URI(url);
|
||||
}
|
||||
catch (URISyntaxException e) {
|
||||
IllegalArgumentException iae = new IllegalArgumentException(
|
||||
"Unable to parse url: " + url);
|
||||
IllegalArgumentException iae = new IllegalArgumentException("Unable to parse url: " + url);
|
||||
iae.initCause(e);
|
||||
throw iae;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@ import java.util.Set;
|
||||
* @since 2.0
|
||||
*/
|
||||
public class SpringSecurityLdapTemplate extends LdapTemplate {
|
||||
|
||||
// ~ Static fields/initializers
|
||||
// =====================================================================================
|
||||
private static final Log logger = LogFactory.getLog(SpringSecurityLdapTemplate.class);
|
||||
@@ -90,11 +91,9 @@ public class SpringSecurityLdapTemplate extends LdapTemplate {
|
||||
/**
|
||||
* Performs an LDAP compare operation of the value of an attribute for a particular
|
||||
* directory entry.
|
||||
*
|
||||
* @param dn the entry who's attribute is to be used
|
||||
* @param attributeName the attribute who's value we want to compare
|
||||
* @param value the value to be checked against the directory value
|
||||
*
|
||||
* @return true if the supplied value matches that in the directory
|
||||
*/
|
||||
public boolean compare(final String dn, final String attributeName, final Object value) {
|
||||
@@ -107,14 +106,15 @@ public class SpringSecurityLdapTemplate extends LdapTemplate {
|
||||
ctls.setReturningAttributes(NO_ATTRS);
|
||||
ctls.setSearchScope(SearchControls.OBJECT_SCOPE);
|
||||
|
||||
NamingEnumeration<SearchResult> results = ctx.search(dn,
|
||||
comparisonFilter, new Object[] { value }, ctls);
|
||||
NamingEnumeration<SearchResult> results = ctx.search(dn, comparisonFilter, new Object[] { value },
|
||||
ctls);
|
||||
|
||||
Boolean match = results.hasMore();
|
||||
LdapUtils.closeEnumeration(results);
|
||||
|
||||
return match;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Boolean matches = (Boolean) executeReadOnly(new LdapCompareCallback());
|
||||
@@ -124,15 +124,12 @@ public class SpringSecurityLdapTemplate extends LdapTemplate {
|
||||
|
||||
/**
|
||||
* Composes an object from the attributes of the given DN.
|
||||
*
|
||||
* @param dn the directory entry which will be read
|
||||
* @param attributesToRetrieve the named attributes which will be retrieved from the
|
||||
* directory entry.
|
||||
*
|
||||
* @return the object created by the mapper
|
||||
*/
|
||||
public DirContextOperations retrieveEntry(final String dn,
|
||||
final String[] attributesToRetrieve) {
|
||||
public DirContextOperations retrieveEntry(final String dn, final String[] attributesToRetrieve) {
|
||||
|
||||
return (DirContextOperations) executeReadOnly((ContextExecutor) ctx -> {
|
||||
Attributes attrs = ctx.getAttributes(dn, attributesToRetrieve);
|
||||
@@ -149,20 +146,18 @@ public class SpringSecurityLdapTemplate extends LdapTemplate {
|
||||
* the named attribute found in all entries matched by the search. Note that one
|
||||
* directory entry may have several values for the attribute. Intended for role
|
||||
* searches and similar scenarios.
|
||||
*
|
||||
* @param base the DN to search in
|
||||
* @param filter search filter to use
|
||||
* @param params the parameters to substitute in the search filter
|
||||
* @param attributeName the attribute who's values are to be retrieved.
|
||||
*
|
||||
* @return the set of String values for the attribute as a union of the values found
|
||||
* in all the matching entries.
|
||||
*/
|
||||
public Set<String> searchForSingleAttributeValues(final String base,
|
||||
final String filter, final Object[] params, final String attributeName) {
|
||||
public Set<String> searchForSingleAttributeValues(final String base, final String filter, final Object[] params,
|
||||
final String attributeName) {
|
||||
String[] attributeNames = new String[] { attributeName };
|
||||
Set<Map<String, List<String>>> multipleAttributeValues = searchForMultipleAttributeValues(
|
||||
base, filter, params, attributeNames);
|
||||
Set<Map<String, List<String>>> multipleAttributeValues = searchForMultipleAttributeValues(base, filter, params,
|
||||
attributeNames);
|
||||
Set<String> result = new HashSet<>();
|
||||
for (Map<String, List<String>> map : multipleAttributeValues) {
|
||||
List<String> values = map.get(attributeName);
|
||||
@@ -178,19 +173,16 @@ public class SpringSecurityLdapTemplate extends LdapTemplate {
|
||||
* attribute found in all entries matched by the search. Note that one directory entry
|
||||
* may have several values for the attribute. Intended for role searches and similar
|
||||
* scenarios.
|
||||
*
|
||||
* @param base the DN to search in
|
||||
* @param filter search filter to use
|
||||
* @param params the parameters to substitute in the search filter
|
||||
* @param attributeNames the attributes' values that are to be retrieved.
|
||||
*
|
||||
* @return the set of String values for each attribute found in all the matching
|
||||
* entries. The attribute name is the key for each set of values. In addition each map
|
||||
* contains the DN as a String with the key predefined key {@link #DN_KEY}.
|
||||
*/
|
||||
public Set<Map<String, List<String>>> searchForMultipleAttributeValues(
|
||||
final String base, final String filter, final Object[] params,
|
||||
final String[] attributeNames) {
|
||||
public Set<Map<String, List<String>>> searchForMultipleAttributeValues(final String base, final String filter,
|
||||
final Object[] params, final String[] attributeNames) {
|
||||
// Escape the params acording to RFC2254
|
||||
Object[] encodedParams = new String[params.length];
|
||||
|
||||
@@ -208,15 +200,13 @@ public class SpringSecurityLdapTemplate extends LdapTemplate {
|
||||
Map<String, List<String>> record = new HashMap<>();
|
||||
if (attributeNames == null || attributeNames.length == 0) {
|
||||
try {
|
||||
for (NamingEnumeration ae = adapter.getAttributes().getAll(); ae
|
||||
.hasMore();) {
|
||||
for (NamingEnumeration ae = adapter.getAttributes().getAll(); ae.hasMore();) {
|
||||
Attribute attr = (Attribute) ae.next();
|
||||
extractStringAttributeValues(adapter, record, attr.getID());
|
||||
}
|
||||
}
|
||||
catch (NamingException x) {
|
||||
org.springframework.ldap.support.LdapUtils
|
||||
.convertLdapException(x);
|
||||
org.springframework.ldap.support.LdapUtils.convertLdapException(x);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -231,8 +221,7 @@ public class SpringSecurityLdapTemplate extends LdapTemplate {
|
||||
|
||||
SearchControls ctls = new SearchControls();
|
||||
ctls.setSearchScope(searchControls.getSearchScope());
|
||||
ctls.setReturningAttributes(attributeNames != null && attributeNames.length > 0 ? attributeNames
|
||||
: null);
|
||||
ctls.setReturningAttributes(attributeNames != null && attributeNames.length > 0 ? attributeNames : null);
|
||||
|
||||
search(base, formattedFilter, ctls, roleMapper);
|
||||
|
||||
@@ -256,13 +245,12 @@ public class SpringSecurityLdapTemplate extends LdapTemplate {
|
||||
* Extracts String values for a specified attribute name and places them in the map
|
||||
* representing the ldap record If a value is not of type String, it will derive it's
|
||||
* value from the {@link Object#toString()}
|
||||
*
|
||||
* @param adapter - the adapter that contains the values
|
||||
* @param record - the map holding the attribute names and values
|
||||
* @param attributeName - the name for which to fetch the values from
|
||||
*/
|
||||
private void extractStringAttributeValues(DirContextAdapter adapter,
|
||||
Map<String, List<String>> record, String attributeName) {
|
||||
private void extractStringAttributeValues(DirContextAdapter adapter, Map<String, List<String>> record,
|
||||
String attributeName) {
|
||||
Object[] values = adapter.getObjectAttributes(attributeName);
|
||||
if (values == null || values.length == 0) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
@@ -278,9 +266,8 @@ public class SpringSecurityLdapTemplate extends LdapTemplate {
|
||||
}
|
||||
else {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Attribute:" + attributeName
|
||||
+ " contains a non string value of type[" + o.getClass()
|
||||
+ "]");
|
||||
logger.debug("Attribute:" + attributeName + " contains a non string value of type["
|
||||
+ o.getClass() + "]");
|
||||
}
|
||||
svalues.add(o.toString());
|
||||
}
|
||||
@@ -295,39 +282,33 @@ public class SpringSecurityLdapTemplate extends LdapTemplate {
|
||||
* <p>
|
||||
* Ignores <tt>PartialResultException</tt> if thrown, for compatibility with Active
|
||||
* Directory (see {@link LdapTemplate#setIgnorePartialResultException(boolean)}).
|
||||
*
|
||||
* @param base the search base, relative to the base context supplied by the context
|
||||
* source.
|
||||
* @param filter the LDAP search filter
|
||||
* @param params parameters to be substituted in the search.
|
||||
*
|
||||
* @return a DirContextOperations instance created from the matching entry.
|
||||
*
|
||||
* @throws IncorrectResultSizeDataAccessException if no results are found or the
|
||||
* search returns more than one result.
|
||||
*/
|
||||
public DirContextOperations searchForSingleEntry(final String base,
|
||||
final String filter, final Object[] params) {
|
||||
public DirContextOperations searchForSingleEntry(final String base, final String filter, final Object[] params) {
|
||||
|
||||
return (DirContextOperations) executeReadOnly((ContextExecutor) ctx -> searchForSingleEntryInternal(ctx, searchControls, base, filter,
|
||||
params));
|
||||
return (DirContextOperations) executeReadOnly(
|
||||
(ContextExecutor) ctx -> searchForSingleEntryInternal(ctx, searchControls, base, filter, params));
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method extracted to avoid code duplication in AD search.
|
||||
*/
|
||||
public static DirContextOperations searchForSingleEntryInternal(DirContext ctx,
|
||||
SearchControls searchControls, String base, String filter, Object[] params)
|
||||
throws NamingException {
|
||||
final DistinguishedName ctxBaseDn = new DistinguishedName(
|
||||
ctx.getNameInNamespace());
|
||||
public static DirContextOperations searchForSingleEntryInternal(DirContext ctx, SearchControls searchControls,
|
||||
String base, String filter, Object[] params) throws NamingException {
|
||||
final DistinguishedName ctxBaseDn = new DistinguishedName(ctx.getNameInNamespace());
|
||||
final DistinguishedName searchBaseDn = new DistinguishedName(base);
|
||||
final NamingEnumeration<SearchResult> resultsEnum = ctx.search(searchBaseDn,
|
||||
filter, params, buildControls(searchControls));
|
||||
final NamingEnumeration<SearchResult> resultsEnum = ctx.search(searchBaseDn, filter, params,
|
||||
buildControls(searchControls));
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Searching for entry under DN '" + ctxBaseDn + "', base = '"
|
||||
+ searchBaseDn + "', filter = '" + filter + "'");
|
||||
logger.debug("Searching for entry under DN '" + ctxBaseDn + "', base = '" + searchBaseDn + "', filter = '"
|
||||
+ filter + "'");
|
||||
}
|
||||
|
||||
Set<DirContextOperations> results = new HashSet<>();
|
||||
@@ -335,8 +316,7 @@ public class SpringSecurityLdapTemplate extends LdapTemplate {
|
||||
while (resultsEnum.hasMore()) {
|
||||
SearchResult searchResult = resultsEnum.next();
|
||||
DirContextAdapter dca = (DirContextAdapter) searchResult.getObject();
|
||||
Assert.notNull(dca,
|
||||
"No object returned by search, DirContext is not correctly configured");
|
||||
Assert.notNull(dca, "No object returned by search, DirContext is not correctly configured");
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Found DN: " + dca.getDn());
|
||||
@@ -367,19 +347,18 @@ public class SpringSecurityLdapTemplate extends LdapTemplate {
|
||||
* @return
|
||||
*/
|
||||
private static SearchControls buildControls(SearchControls originalControls) {
|
||||
return new SearchControls(originalControls.getSearchScope(),
|
||||
originalControls.getCountLimit(), originalControls.getTimeLimit(),
|
||||
originalControls.getReturningAttributes(), RETURN_OBJECT,
|
||||
return new SearchControls(originalControls.getSearchScope(), originalControls.getCountLimit(),
|
||||
originalControls.getTimeLimit(), originalControls.getReturningAttributes(), RETURN_OBJECT,
|
||||
originalControls.getDerefLinkFlag());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the search controls which will be used for search operations by the template.
|
||||
*
|
||||
* @param searchControls the SearchControls instance which will be cached in the
|
||||
* template.
|
||||
*/
|
||||
public void setSearchControls(SearchControls searchControls) {
|
||||
this.searchControls = searchControls;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -46,16 +46,19 @@ import org.springframework.util.StringUtils;
|
||||
* @author Luke Taylor
|
||||
* @since 3.1
|
||||
*/
|
||||
public abstract class AbstractLdapAuthenticationProvider
|
||||
implements AuthenticationProvider, MessageSourceAware {
|
||||
public abstract class AbstractLdapAuthenticationProvider implements AuthenticationProvider, MessageSourceAware {
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
|
||||
|
||||
private boolean useAuthenticationRequestCredentials = true;
|
||||
|
||||
private GrantedAuthoritiesMapper authoritiesMapper = new NullAuthoritiesMapper();
|
||||
|
||||
protected UserDetailsContextMapper userDetailsContextMapper = new LdapUserDetailsMapper();
|
||||
|
||||
public Authentication authenticate(Authentication authentication)
|
||||
throws AuthenticationException {
|
||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||
Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication,
|
||||
() -> this.messages.getMessage("LdapAuthenticationProvider.onlySupports",
|
||||
"Only UsernamePasswordAuthenticationToken is supported"));
|
||||
@@ -70,50 +73,44 @@ public abstract class AbstractLdapAuthenticationProvider
|
||||
}
|
||||
|
||||
if (!StringUtils.hasLength(username)) {
|
||||
throw new BadCredentialsException(this.messages.getMessage(
|
||||
"LdapAuthenticationProvider.emptyUsername", "Empty Username"));
|
||||
throw new BadCredentialsException(
|
||||
this.messages.getMessage("LdapAuthenticationProvider.emptyUsername", "Empty Username"));
|
||||
}
|
||||
|
||||
if (!StringUtils.hasLength(password)) {
|
||||
throw new BadCredentialsException(this.messages.getMessage(
|
||||
"AbstractLdapAuthenticationProvider.emptyPassword",
|
||||
"Empty Password"));
|
||||
throw new BadCredentialsException(
|
||||
this.messages.getMessage("AbstractLdapAuthenticationProvider.emptyPassword", "Empty Password"));
|
||||
}
|
||||
|
||||
Assert.notNull(password, "Null password was supplied in authentication token");
|
||||
|
||||
DirContextOperations userData = doAuthentication(userToken);
|
||||
|
||||
UserDetails user = this.userDetailsContextMapper.mapUserFromContext(userData,
|
||||
authentication.getName(),
|
||||
loadUserAuthorities(userData, authentication.getName(),
|
||||
(String) authentication.getCredentials()));
|
||||
UserDetails user = this.userDetailsContextMapper.mapUserFromContext(userData, authentication.getName(),
|
||||
loadUserAuthorities(userData, authentication.getName(), (String) authentication.getCredentials()));
|
||||
|
||||
return createSuccessfulAuthentication(userToken, user);
|
||||
}
|
||||
|
||||
protected abstract DirContextOperations doAuthentication(
|
||||
UsernamePasswordAuthenticationToken auth);
|
||||
protected abstract DirContextOperations doAuthentication(UsernamePasswordAuthenticationToken auth);
|
||||
|
||||
protected abstract Collection<? extends GrantedAuthority> loadUserAuthorities(
|
||||
DirContextOperations userData, String username, String password);
|
||||
protected abstract Collection<? extends GrantedAuthority> loadUserAuthorities(DirContextOperations userData,
|
||||
String username, String password);
|
||||
|
||||
/**
|
||||
* Creates the final {@code Authentication} object which will be returned from the
|
||||
* {@code authenticate} method.
|
||||
*
|
||||
* @param authentication the original authentication request token
|
||||
* @param user the <tt>UserDetails</tt> instance returned by the configured
|
||||
* <tt>UserDetailsContextMapper</tt>.
|
||||
* @return the Authentication object for the fully authenticated user.
|
||||
*/
|
||||
protected Authentication createSuccessfulAuthentication(
|
||||
UsernamePasswordAuthenticationToken authentication, UserDetails user) {
|
||||
Object password = this.useAuthenticationRequestCredentials
|
||||
? authentication.getCredentials() : user.getPassword();
|
||||
protected Authentication createSuccessfulAuthentication(UsernamePasswordAuthenticationToken authentication,
|
||||
UserDetails user) {
|
||||
Object password = this.useAuthenticationRequestCredentials ? authentication.getCredentials()
|
||||
: user.getPassword();
|
||||
|
||||
UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(
|
||||
user, password,
|
||||
UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(user, password,
|
||||
this.authoritiesMapper.mapAuthorities(user.getAuthorities()));
|
||||
result.setDetails(authentication.getDetails());
|
||||
|
||||
@@ -130,11 +127,9 @@ public abstract class AbstractLdapAuthenticationProvider
|
||||
* obtained from the UserDetails object created by the configured
|
||||
* {@code UserDetailsContextMapper}. Often it will not be possible to read the
|
||||
* password from the directory, so defaults to true.
|
||||
*
|
||||
* @param useAuthenticationRequestCredentials
|
||||
*/
|
||||
public void setUseAuthenticationRequestCredentials(
|
||||
boolean useAuthenticationRequestCredentials) {
|
||||
public void setUseAuthenticationRequestCredentials(boolean useAuthenticationRequestCredentials) {
|
||||
this.useAuthenticationRequestCredentials = useAuthenticationRequestCredentials;
|
||||
}
|
||||
|
||||
@@ -151,14 +146,11 @@ public abstract class AbstractLdapAuthenticationProvider
|
||||
* will be stored as the principal in the <tt>Authentication</tt> returned by the
|
||||
* {@link #createSuccessfulAuthentication(org.springframework.security.authentication.UsernamePasswordAuthenticationToken, org.springframework.security.core.userdetails.UserDetails)}
|
||||
* method.
|
||||
*
|
||||
* @param userDetailsContextMapper the strategy instance. If not set, defaults to a
|
||||
* simple <tt>LdapUserDetailsMapper</tt>.
|
||||
*/
|
||||
public void setUserDetailsContextMapper(
|
||||
UserDetailsContextMapper userDetailsContextMapper) {
|
||||
Assert.notNull(userDetailsContextMapper,
|
||||
"UserDetailsContextMapper must not be null");
|
||||
public void setUserDetailsContextMapper(UserDetailsContextMapper userDetailsContextMapper) {
|
||||
Assert.notNull(userDetailsContextMapper, "UserDetailsContextMapper must not be null");
|
||||
this.userDetailsContextMapper = userDetailsContextMapper;
|
||||
}
|
||||
|
||||
@@ -169,4 +161,5 @@ public abstract class AbstractLdapAuthenticationProvider
|
||||
protected UserDetailsContextMapper getUserDetailsContextMapper() {
|
||||
return this.userDetailsContextMapper;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -35,8 +35,8 @@ import java.util.List;
|
||||
*
|
||||
* @author Luke Taylor
|
||||
*/
|
||||
public abstract class AbstractLdapAuthenticator implements LdapAuthenticator,
|
||||
InitializingBean, MessageSourceAware {
|
||||
public abstract class AbstractLdapAuthenticator implements LdapAuthenticator, InitializingBean, MessageSourceAware {
|
||||
|
||||
// ~ Instance fields
|
||||
// ================================================================================================
|
||||
|
||||
@@ -47,6 +47,7 @@ public abstract class AbstractLdapAuthenticator implements LdapAuthenticator,
|
||||
* isn't sufficient
|
||||
*/
|
||||
private LdapUserSearch userSearch;
|
||||
|
||||
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
|
||||
|
||||
/**
|
||||
@@ -64,7 +65,6 @@ public abstract class AbstractLdapAuthenticator implements LdapAuthenticator,
|
||||
|
||||
/**
|
||||
* Create an initialized instance with the {@link ContextSource} provided.
|
||||
*
|
||||
* @param contextSource
|
||||
*/
|
||||
public AbstractLdapAuthenticator(ContextSource contextSource) {
|
||||
@@ -91,9 +91,7 @@ public abstract class AbstractLdapAuthenticator implements LdapAuthenticator,
|
||||
/**
|
||||
* Builds list of possible DNs for the user, worked out from the
|
||||
* <tt>userDnPatterns</tt> property.
|
||||
*
|
||||
* @param username the user's login name
|
||||
*
|
||||
* @return the list of possible DN matches, empty if <tt>userDnPatterns</tt> wasn't
|
||||
* set.
|
||||
*/
|
||||
@@ -125,12 +123,10 @@ public abstract class AbstractLdapAuthenticator implements LdapAuthenticator,
|
||||
|
||||
/**
|
||||
* Sets the user attributes which will be retrieved from the directory.
|
||||
*
|
||||
* @param userAttributes
|
||||
*/
|
||||
public void setUserAttributes(String[] userAttributes) {
|
||||
Assert.notNull(userAttributes,
|
||||
"The userAttributes property cannot be set to null");
|
||||
Assert.notNull(userAttributes, "The userAttributes property cannot be set to null");
|
||||
this.userAttributes = userAttributes;
|
||||
}
|
||||
|
||||
@@ -138,7 +134,6 @@ public abstract class AbstractLdapAuthenticator implements LdapAuthenticator,
|
||||
* Sets the pattern which will be used to supply a DN for the user. The pattern should
|
||||
* be the name relative to the root DN. The pattern argument {0} will contain the
|
||||
* username. An example would be "cn={0},ou=people".
|
||||
*
|
||||
* @param dnPattern the array of patterns which will be tried when converting a
|
||||
* username to a DN.
|
||||
*/
|
||||
@@ -156,4 +151,5 @@ public abstract class AbstractLdapAuthenticator implements LdapAuthenticator,
|
||||
Assert.notNull(userSearch, "The userSearch cannot be set to null");
|
||||
this.userSearch = userSearch;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -39,10 +39,10 @@ import javax.naming.directory.DirContext;
|
||||
* An authenticator which binds as a user.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
*
|
||||
* @see AbstractLdapAuthenticator
|
||||
*/
|
||||
public class BindAuthenticator extends AbstractLdapAuthenticator {
|
||||
|
||||
// ~ Static fields/initializers
|
||||
// =====================================================================================
|
||||
|
||||
@@ -54,7 +54,6 @@ public class BindAuthenticator extends AbstractLdapAuthenticator {
|
||||
/**
|
||||
* Create an initialized instance using the {@link BaseLdapPathContextSource}
|
||||
* provided.
|
||||
*
|
||||
* @param contextSource the BaseLdapPathContextSource instance against which bind
|
||||
* operations will be performed.
|
||||
*
|
||||
@@ -76,8 +75,7 @@ public class BindAuthenticator extends AbstractLdapAuthenticator {
|
||||
|
||||
if (!StringUtils.hasLength(password)) {
|
||||
logger.debug("Rejecting empty password for user " + username);
|
||||
throw new BadCredentialsException(messages.getMessage(
|
||||
"BindAuthenticator.emptyPassword", "Empty Password"));
|
||||
throw new BadCredentialsException(messages.getMessage("BindAuthenticator.emptyPassword", "Empty Password"));
|
||||
}
|
||||
|
||||
// If DN patterns are configured, try authenticating with them directly
|
||||
@@ -93,25 +91,22 @@ public class BindAuthenticator extends AbstractLdapAuthenticator {
|
||||
// with the returned DN.
|
||||
if (user == null && getUserSearch() != null) {
|
||||
DirContextOperations userFromSearch = getUserSearch().searchForUser(username);
|
||||
user = bindWithDn(userFromSearch.getDn().toString(), username, password,
|
||||
userFromSearch.getAttributes());
|
||||
user = bindWithDn(userFromSearch.getDn().toString(), username, password, userFromSearch.getAttributes());
|
||||
}
|
||||
|
||||
if (user == null) {
|
||||
throw new BadCredentialsException(messages.getMessage(
|
||||
"BindAuthenticator.badCredentials", "Bad credentials"));
|
||||
throw new BadCredentialsException(
|
||||
messages.getMessage("BindAuthenticator.badCredentials", "Bad credentials"));
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
private DirContextOperations bindWithDn(String userDnStr, String username,
|
||||
String password) {
|
||||
private DirContextOperations bindWithDn(String userDnStr, String username, String password) {
|
||||
return bindWithDn(userDnStr, username, password, null);
|
||||
}
|
||||
|
||||
private DirContextOperations bindWithDn(String userDnStr, String username,
|
||||
String password, Attributes attrs) {
|
||||
private DirContextOperations bindWithDn(String userDnStr, String username, String password, Attributes attrs) {
|
||||
BaseLdapPathContextSource ctxSource = (BaseLdapPathContextSource) getContextSource();
|
||||
DistinguishedName userDn = new DistinguishedName(userDnStr);
|
||||
DistinguishedName fullDn = new DistinguishedName(userDn);
|
||||
@@ -123,16 +118,14 @@ public class BindAuthenticator extends AbstractLdapAuthenticator {
|
||||
try {
|
||||
ctx = getContextSource().getContext(fullDn.toString(), password);
|
||||
// Check for password policy control
|
||||
PasswordPolicyControl ppolicy = PasswordPolicyControlExtractor
|
||||
.extractControl(ctx);
|
||||
PasswordPolicyControl ppolicy = PasswordPolicyControlExtractor.extractControl(ctx);
|
||||
|
||||
logger.debug("Retrieving attributes...");
|
||||
if (attrs == null || attrs.size()==0) {
|
||||
if (attrs == null || attrs.size() == 0) {
|
||||
attrs = ctx.getAttributes(userDn, getUserAttributes());
|
||||
}
|
||||
|
||||
DirContextAdapter result = new DirContextAdapter(attrs, userDn,
|
||||
ctxSource.getBaseLdapPath());
|
||||
DirContextAdapter result = new DirContextAdapter(attrs, userDn, ctxSource.getBaseLdapPath());
|
||||
|
||||
if (ppolicy != null) {
|
||||
result.setAttributeValue(ppolicy.getID(), ppolicy);
|
||||
@@ -172,4 +165,5 @@ public class BindAuthenticator extends AbstractLdapAuthenticator {
|
||||
logger.debug("Failed to bind as " + userDn + ": " + cause);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -108,21 +108,22 @@ import org.springframework.util.Assert;
|
||||
* this means that if the LDAP directory is configured to allow unauthenticated access, it
|
||||
* might be possible to authenticate as <i>any</i> user just by supplying an empty
|
||||
* password. More information on the misuse of unauthenticated access can be found in
|
||||
* <a href="https://www.ietf.org/internet-drafts/draft-ietf-ldapbis-authmeth-19.txt"> draft
|
||||
* -ietf-ldapbis-authmeth-19.txt</a>.
|
||||
*
|
||||
* <a href="https://www.ietf.org/internet-drafts/draft-ietf-ldapbis-authmeth-19.txt">
|
||||
* draft -ietf-ldapbis-authmeth-19.txt</a>.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
*
|
||||
* @see BindAuthenticator
|
||||
* @see DefaultLdapAuthoritiesPopulator
|
||||
*/
|
||||
public class LdapAuthenticationProvider extends AbstractLdapAuthenticationProvider {
|
||||
|
||||
// ~ Instance fields
|
||||
// ================================================================================================
|
||||
|
||||
private LdapAuthenticator authenticator;
|
||||
|
||||
private LdapAuthoritiesPopulator authoritiesPopulator;
|
||||
|
||||
private boolean hideUserNotFoundExceptions = true;
|
||||
|
||||
// ~ Constructors
|
||||
@@ -131,14 +132,12 @@ public class LdapAuthenticationProvider extends AbstractLdapAuthenticationProvid
|
||||
/**
|
||||
* Create an instance with the supplied authenticator and authorities populator
|
||||
* implementations.
|
||||
*
|
||||
* @param authenticator the authentication strategy (bind, password comparison, etc)
|
||||
* to be used by this provider for authenticating users.
|
||||
* @param authoritiesPopulator the strategy for obtaining the authorities for a given
|
||||
* user after they've been authenticated.
|
||||
*/
|
||||
public LdapAuthenticationProvider(LdapAuthenticator authenticator,
|
||||
LdapAuthoritiesPopulator authoritiesPopulator) {
|
||||
public LdapAuthenticationProvider(LdapAuthenticator authenticator, LdapAuthoritiesPopulator authoritiesPopulator) {
|
||||
this.setAuthenticator(authenticator);
|
||||
this.setAuthoritiesPopulator(authoritiesPopulator);
|
||||
}
|
||||
@@ -146,7 +145,6 @@ public class LdapAuthenticationProvider extends AbstractLdapAuthenticationProvid
|
||||
/**
|
||||
* Creates an instance with the supplied authenticator and a null authorities
|
||||
* populator. In this case, the authorities must be mapped from the user context.
|
||||
*
|
||||
* @param authenticator the authenticator strategy.
|
||||
*/
|
||||
public LdapAuthenticationProvider(LdapAuthenticator authenticator) {
|
||||
@@ -167,8 +165,7 @@ public class LdapAuthenticationProvider extends AbstractLdapAuthenticationProvid
|
||||
}
|
||||
|
||||
private void setAuthoritiesPopulator(LdapAuthoritiesPopulator authoritiesPopulator) {
|
||||
Assert.notNull(authoritiesPopulator,
|
||||
"An LdapAuthoritiesPopulator must be supplied");
|
||||
Assert.notNull(authoritiesPopulator, "An LdapAuthoritiesPopulator must be supplied");
|
||||
this.authoritiesPopulator = authoritiesPopulator;
|
||||
}
|
||||
|
||||
@@ -181,35 +178,33 @@ public class LdapAuthenticationProvider extends AbstractLdapAuthenticationProvid
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DirContextOperations doAuthentication(
|
||||
UsernamePasswordAuthenticationToken authentication) {
|
||||
protected DirContextOperations doAuthentication(UsernamePasswordAuthenticationToken authentication) {
|
||||
try {
|
||||
return getAuthenticator().authenticate(authentication);
|
||||
}
|
||||
catch (PasswordPolicyException ppe) {
|
||||
// The only reason a ppolicy exception can occur during a bind is that the
|
||||
// account is locked.
|
||||
throw new LockedException(this.messages.getMessage(
|
||||
ppe.getStatus().getErrorCode(), ppe.getStatus().getDefaultMessage()));
|
||||
throw new LockedException(
|
||||
this.messages.getMessage(ppe.getStatus().getErrorCode(), ppe.getStatus().getDefaultMessage()));
|
||||
}
|
||||
catch (UsernameNotFoundException notFound) {
|
||||
if (this.hideUserNotFoundExceptions) {
|
||||
throw new BadCredentialsException(this.messages.getMessage(
|
||||
"LdapAuthenticationProvider.badCredentials", "Bad credentials"));
|
||||
throw new BadCredentialsException(
|
||||
this.messages.getMessage("LdapAuthenticationProvider.badCredentials", "Bad credentials"));
|
||||
}
|
||||
else {
|
||||
throw notFound;
|
||||
}
|
||||
}
|
||||
catch (NamingException ldapAccessFailure) {
|
||||
throw new InternalAuthenticationServiceException(
|
||||
ldapAccessFailure.getMessage(), ldapAccessFailure);
|
||||
throw new InternalAuthenticationServiceException(ldapAccessFailure.getMessage(), ldapAccessFailure);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<? extends GrantedAuthority> loadUserAuthorities(
|
||||
DirContextOperations userData, String username, String password) {
|
||||
protected Collection<? extends GrantedAuthority> loadUserAuthorities(DirContextOperations userData, String username,
|
||||
String password) {
|
||||
return getAuthoritiesPopulator().getGrantedAuthorities(userData, username);
|
||||
}
|
||||
|
||||
|
||||
@@ -26,19 +26,19 @@ import org.springframework.ldap.core.DirContextOperations;
|
||||
* the information for that user from the directory.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
*
|
||||
* @see org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator
|
||||
* @see org.springframework.security.ldap.authentication.UserDetailsServiceLdapAuthoritiesPopulator
|
||||
*/
|
||||
public interface LdapAuthenticator {
|
||||
|
||||
// ~ Methods
|
||||
// ========================================================================================================
|
||||
|
||||
/**
|
||||
* Authenticates as a user and obtains additional user information from the directory.
|
||||
*
|
||||
* @param authentication
|
||||
* @return the details of the successfully authenticated user.
|
||||
*/
|
||||
DirContextOperations authenticate(Authentication authentication);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,245 +1,241 @@
|
||||
/*
|
||||
* Copyright 2005-2010 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.ldap.authentication;
|
||||
|
||||
import org.springframework.ldap.BadLdapGrammarException;
|
||||
|
||||
/**
|
||||
* Helper class to encode and decode ldap names and values.
|
||||
*
|
||||
* <p>
|
||||
* NOTE: This is a copy from Spring LDAP so that both Spring LDAP 1.x and 2.x can be
|
||||
* supported without reflection.
|
||||
* </p>
|
||||
*
|
||||
* @author Adam Skogman
|
||||
* @author Mattias Hellborg Arthursson
|
||||
*/
|
||||
final class LdapEncoder {
|
||||
|
||||
private static final int HEX = 16;
|
||||
private static String[] NAME_ESCAPE_TABLE = new String[96];
|
||||
|
||||
private static String[] FILTER_ESCAPE_TABLE = new String['\\' + 1];
|
||||
|
||||
static {
|
||||
|
||||
// Name encoding table -------------------------------------
|
||||
|
||||
// all below 0x20 (control chars)
|
||||
for (char c = 0; c < ' '; c++) {
|
||||
NAME_ESCAPE_TABLE[c] = "\\" + toTwoCharHex(c);
|
||||
}
|
||||
|
||||
NAME_ESCAPE_TABLE['#'] = "\\#";
|
||||
NAME_ESCAPE_TABLE[','] = "\\,";
|
||||
NAME_ESCAPE_TABLE[';'] = "\\;";
|
||||
NAME_ESCAPE_TABLE['='] = "\\=";
|
||||
NAME_ESCAPE_TABLE['+'] = "\\+";
|
||||
NAME_ESCAPE_TABLE['<'] = "\\<";
|
||||
NAME_ESCAPE_TABLE['>'] = "\\>";
|
||||
NAME_ESCAPE_TABLE['\"'] = "\\\"";
|
||||
NAME_ESCAPE_TABLE['\\'] = "\\\\";
|
||||
|
||||
// Filter encoding table -------------------------------------
|
||||
|
||||
// fill with char itself
|
||||
for (char c = 0; c < FILTER_ESCAPE_TABLE.length; c++) {
|
||||
FILTER_ESCAPE_TABLE[c] = String.valueOf(c);
|
||||
}
|
||||
|
||||
// escapes (RFC2254)
|
||||
FILTER_ESCAPE_TABLE['*'] = "\\2a";
|
||||
FILTER_ESCAPE_TABLE['('] = "\\28";
|
||||
FILTER_ESCAPE_TABLE[')'] = "\\29";
|
||||
FILTER_ESCAPE_TABLE['\\'] = "\\5c";
|
||||
FILTER_ESCAPE_TABLE[0] = "\\00";
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* All static methods - not to be instantiated.
|
||||
*/
|
||||
private LdapEncoder() {
|
||||
}
|
||||
|
||||
protected static String toTwoCharHex(char c) {
|
||||
|
||||
String raw = Integer.toHexString(c).toUpperCase();
|
||||
|
||||
if (raw.length() > 1) {
|
||||
return raw;
|
||||
}
|
||||
else {
|
||||
return "0" + raw;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape a value for use in a filter.
|
||||
*
|
||||
* @param value the value to escape.
|
||||
* @return a properly escaped representation of the supplied value.
|
||||
*/
|
||||
public static String filterEncode(String value) {
|
||||
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// make buffer roomy
|
||||
StringBuilder encodedValue = new StringBuilder(value.length() * 2);
|
||||
|
||||
int length = value.length();
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
|
||||
char c = value.charAt(i);
|
||||
|
||||
if (c < FILTER_ESCAPE_TABLE.length) {
|
||||
encodedValue.append(FILTER_ESCAPE_TABLE[c]);
|
||||
}
|
||||
else {
|
||||
// default: add the char
|
||||
encodedValue.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
return encodedValue.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* LDAP Encodes a value for use with a DN. Escapes for LDAP, not JNDI!
|
||||
*
|
||||
* <br/>
|
||||
* Escapes:<br/>
|
||||
* ' ' [space] - "\ " [if first or last] <br/>
|
||||
* '#' [hash] - "\#" <br/>
|
||||
* ',' [comma] - "\," <br/>
|
||||
* ';' [semicolon] - "\;" <br/>
|
||||
* '= [equals] - "\=" <br/>
|
||||
* '+' [plus] - "\+" <br/>
|
||||
* '<' [less than] - "\<" <br/>
|
||||
* '>' [greater than] - "\>" <br/>
|
||||
* '"' [double quote] - "\"" <br/>
|
||||
* '\' [backslash] - "\\" <br/>
|
||||
*
|
||||
* @param value the value to escape.
|
||||
* @return The escaped value.
|
||||
*/
|
||||
public static String nameEncode(String value) {
|
||||
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// make buffer roomy
|
||||
StringBuilder encodedValue = new StringBuilder(value.length() * 2);
|
||||
|
||||
int length = value.length();
|
||||
int last = length - 1;
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
|
||||
char c = value.charAt(i);
|
||||
|
||||
// space first or last
|
||||
if (c == ' ' && (i == 0 || i == last)) {
|
||||
encodedValue.append("\\ ");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c < NAME_ESCAPE_TABLE.length) {
|
||||
// check in table for escapes
|
||||
String esc = NAME_ESCAPE_TABLE[c];
|
||||
|
||||
if (esc != null) {
|
||||
encodedValue.append(esc);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// default: add the char
|
||||
encodedValue.append(c);
|
||||
}
|
||||
|
||||
return encodedValue.toString();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a value. Converts escaped chars to ordinary chars.
|
||||
*
|
||||
* @param value Trimmed value, so no leading an trailing blanks, except an escaped
|
||||
* space last.
|
||||
* @return The decoded value as a string.
|
||||
* @throws BadLdapGrammarException
|
||||
*/
|
||||
static public String nameDecode(String value) throws BadLdapGrammarException {
|
||||
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// make buffer same size
|
||||
StringBuilder decoded = new StringBuilder(value.length());
|
||||
|
||||
int i = 0;
|
||||
while (i < value.length()) {
|
||||
char currentChar = value.charAt(i);
|
||||
if (currentChar == '\\') {
|
||||
if (value.length() <= i + 1) {
|
||||
// Ending with a single backslash is not allowed
|
||||
throw new BadLdapGrammarException(
|
||||
"Unexpected end of value " + "unterminated '\\'");
|
||||
}
|
||||
else {
|
||||
char nextChar = value.charAt(i + 1);
|
||||
if (nextChar == ',' || nextChar == '=' || nextChar == '+'
|
||||
|| nextChar == '<' || nextChar == '>' || nextChar == '#'
|
||||
|| nextChar == ';' || nextChar == '\\' || nextChar == '\"'
|
||||
|| nextChar == ' ') {
|
||||
// Normal backslash escape
|
||||
decoded.append(nextChar);
|
||||
i += 2;
|
||||
}
|
||||
else {
|
||||
if (value.length() <= i + 2) {
|
||||
throw new BadLdapGrammarException("Unexpected end of value "
|
||||
+ "expected special or hex, found '" + nextChar
|
||||
+ "'");
|
||||
}
|
||||
else {
|
||||
// This should be a hex value
|
||||
String hexString = "" + nextChar + value.charAt(i + 2);
|
||||
decoded.append((char) Integer.parseInt(hexString, HEX));
|
||||
i += 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// This character wasn't escaped - just append it
|
||||
decoded.append(currentChar);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
return decoded.toString();
|
||||
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright 2005-2010 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.ldap.authentication;
|
||||
|
||||
import org.springframework.ldap.BadLdapGrammarException;
|
||||
|
||||
/**
|
||||
* Helper class to encode and decode ldap names and values.
|
||||
*
|
||||
* <p>
|
||||
* NOTE: This is a copy from Spring LDAP so that both Spring LDAP 1.x and 2.x can be
|
||||
* supported without reflection.
|
||||
* </p>
|
||||
*
|
||||
* @author Adam Skogman
|
||||
* @author Mattias Hellborg Arthursson
|
||||
*/
|
||||
final class LdapEncoder {
|
||||
|
||||
private static final int HEX = 16;
|
||||
|
||||
private static String[] NAME_ESCAPE_TABLE = new String[96];
|
||||
|
||||
private static String[] FILTER_ESCAPE_TABLE = new String['\\' + 1];
|
||||
|
||||
static {
|
||||
|
||||
// Name encoding table -------------------------------------
|
||||
|
||||
// all below 0x20 (control chars)
|
||||
for (char c = 0; c < ' '; c++) {
|
||||
NAME_ESCAPE_TABLE[c] = "\\" + toTwoCharHex(c);
|
||||
}
|
||||
|
||||
NAME_ESCAPE_TABLE['#'] = "\\#";
|
||||
NAME_ESCAPE_TABLE[','] = "\\,";
|
||||
NAME_ESCAPE_TABLE[';'] = "\\;";
|
||||
NAME_ESCAPE_TABLE['='] = "\\=";
|
||||
NAME_ESCAPE_TABLE['+'] = "\\+";
|
||||
NAME_ESCAPE_TABLE['<'] = "\\<";
|
||||
NAME_ESCAPE_TABLE['>'] = "\\>";
|
||||
NAME_ESCAPE_TABLE['\"'] = "\\\"";
|
||||
NAME_ESCAPE_TABLE['\\'] = "\\\\";
|
||||
|
||||
// Filter encoding table -------------------------------------
|
||||
|
||||
// fill with char itself
|
||||
for (char c = 0; c < FILTER_ESCAPE_TABLE.length; c++) {
|
||||
FILTER_ESCAPE_TABLE[c] = String.valueOf(c);
|
||||
}
|
||||
|
||||
// escapes (RFC2254)
|
||||
FILTER_ESCAPE_TABLE['*'] = "\\2a";
|
||||
FILTER_ESCAPE_TABLE['('] = "\\28";
|
||||
FILTER_ESCAPE_TABLE[')'] = "\\29";
|
||||
FILTER_ESCAPE_TABLE['\\'] = "\\5c";
|
||||
FILTER_ESCAPE_TABLE[0] = "\\00";
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* All static methods - not to be instantiated.
|
||||
*/
|
||||
private LdapEncoder() {
|
||||
}
|
||||
|
||||
protected static String toTwoCharHex(char c) {
|
||||
|
||||
String raw = Integer.toHexString(c).toUpperCase();
|
||||
|
||||
if (raw.length() > 1) {
|
||||
return raw;
|
||||
}
|
||||
else {
|
||||
return "0" + raw;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape a value for use in a filter.
|
||||
* @param value the value to escape.
|
||||
* @return a properly escaped representation of the supplied value.
|
||||
*/
|
||||
public static String filterEncode(String value) {
|
||||
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// make buffer roomy
|
||||
StringBuilder encodedValue = new StringBuilder(value.length() * 2);
|
||||
|
||||
int length = value.length();
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
|
||||
char c = value.charAt(i);
|
||||
|
||||
if (c < FILTER_ESCAPE_TABLE.length) {
|
||||
encodedValue.append(FILTER_ESCAPE_TABLE[c]);
|
||||
}
|
||||
else {
|
||||
// default: add the char
|
||||
encodedValue.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
return encodedValue.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* LDAP Encodes a value for use with a DN. Escapes for LDAP, not JNDI!
|
||||
*
|
||||
* <br/>
|
||||
* Escapes:<br/>
|
||||
* ' ' [space] - "\ " [if first or last] <br/>
|
||||
* '#' [hash] - "\#" <br/>
|
||||
* ',' [comma] - "\," <br/>
|
||||
* ';' [semicolon] - "\;" <br/>
|
||||
* '= [equals] - "\=" <br/>
|
||||
* '+' [plus] - "\+" <br/>
|
||||
* '<' [less than] - "\<" <br/>
|
||||
* '>' [greater than] - "\>" <br/>
|
||||
* '"' [double quote] - "\"" <br/>
|
||||
* '\' [backslash] - "\\" <br/>
|
||||
* @param value the value to escape.
|
||||
* @return The escaped value.
|
||||
*/
|
||||
public static String nameEncode(String value) {
|
||||
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// make buffer roomy
|
||||
StringBuilder encodedValue = new StringBuilder(value.length() * 2);
|
||||
|
||||
int length = value.length();
|
||||
int last = length - 1;
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
|
||||
char c = value.charAt(i);
|
||||
|
||||
// space first or last
|
||||
if (c == ' ' && (i == 0 || i == last)) {
|
||||
encodedValue.append("\\ ");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c < NAME_ESCAPE_TABLE.length) {
|
||||
// check in table for escapes
|
||||
String esc = NAME_ESCAPE_TABLE[c];
|
||||
|
||||
if (esc != null) {
|
||||
encodedValue.append(esc);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// default: add the char
|
||||
encodedValue.append(c);
|
||||
}
|
||||
|
||||
return encodedValue.toString();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a value. Converts escaped chars to ordinary chars.
|
||||
* @param value Trimmed value, so no leading an trailing blanks, except an escaped
|
||||
* space last.
|
||||
* @return The decoded value as a string.
|
||||
* @throws BadLdapGrammarException
|
||||
*/
|
||||
static public String nameDecode(String value) throws BadLdapGrammarException {
|
||||
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// make buffer same size
|
||||
StringBuilder decoded = new StringBuilder(value.length());
|
||||
|
||||
int i = 0;
|
||||
while (i < value.length()) {
|
||||
char currentChar = value.charAt(i);
|
||||
if (currentChar == '\\') {
|
||||
if (value.length() <= i + 1) {
|
||||
// Ending with a single backslash is not allowed
|
||||
throw new BadLdapGrammarException("Unexpected end of value " + "unterminated '\\'");
|
||||
}
|
||||
else {
|
||||
char nextChar = value.charAt(i + 1);
|
||||
if (nextChar == ',' || nextChar == '=' || nextChar == '+' || nextChar == '<' || nextChar == '>'
|
||||
|| nextChar == '#' || nextChar == ';' || nextChar == '\\' || nextChar == '\"'
|
||||
|| nextChar == ' ') {
|
||||
// Normal backslash escape
|
||||
decoded.append(nextChar);
|
||||
i += 2;
|
||||
}
|
||||
else {
|
||||
if (value.length() <= i + 2) {
|
||||
throw new BadLdapGrammarException(
|
||||
"Unexpected end of value " + "expected special or hex, found '" + nextChar + "'");
|
||||
}
|
||||
else {
|
||||
// This should be a hex value
|
||||
String hexString = "" + nextChar + value.charAt(i + 2);
|
||||
decoded.append((char) Integer.parseInt(hexString, HEX));
|
||||
i += 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// This character wasn't escaped - just append it
|
||||
decoded.append(currentChar);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
return decoded.toString();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -23,13 +23,13 @@ import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @since 3.0
|
||||
*/
|
||||
public final class NullLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator {
|
||||
public Collection<GrantedAuthority> getGrantedAuthorities(
|
||||
DirContextOperations userDetails, String username) {
|
||||
|
||||
public Collection<GrantedAuthority> getGrantedAuthorities(DirContextOperations userDetails, String username) {
|
||||
return AuthorityUtils.NO_AUTHORITIES;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -47,17 +47,19 @@ import org.springframework.util.Assert;
|
||||
* @author Luke Taylor
|
||||
*/
|
||||
public final class PasswordComparisonAuthenticator extends AbstractLdapAuthenticator {
|
||||
|
||||
// ~ Static fields/initializers
|
||||
// =====================================================================================
|
||||
|
||||
private static final Log logger = LogFactory
|
||||
.getLog(PasswordComparisonAuthenticator.class);
|
||||
private static final Log logger = LogFactory.getLog(PasswordComparisonAuthenticator.class);
|
||||
|
||||
// ~ Instance fields
|
||||
// ================================================================================================
|
||||
|
||||
private PasswordEncoder passwordEncoder = new LdapShaPasswordEncoder(KeyGenerators.shared(0));
|
||||
|
||||
private String passwordAttributeName = "userPassword";
|
||||
|
||||
private boolean usePasswordAttrCompare = false;
|
||||
|
||||
// ~ Constructors
|
||||
@@ -79,8 +81,7 @@ public final class PasswordComparisonAuthenticator extends AbstractLdapAuthentic
|
||||
String username = authentication.getName();
|
||||
String password = (String) authentication.getCredentials();
|
||||
|
||||
SpringSecurityLdapTemplate ldapTemplate = new SpringSecurityLdapTemplate(
|
||||
getContextSource());
|
||||
SpringSecurityLdapTemplate ldapTemplate = new SpringSecurityLdapTemplate(getContextSource());
|
||||
|
||||
for (String userDn : getUserDns(username)) {
|
||||
try {
|
||||
@@ -102,8 +103,8 @@ public final class PasswordComparisonAuthenticator extends AbstractLdapAuthentic
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Performing LDAP compare of password attribute '"
|
||||
+ passwordAttributeName + "' for user '" + user.getDn() + "'");
|
||||
logger.debug("Performing LDAP compare of password attribute '" + passwordAttributeName + "' for user '"
|
||||
+ user.getDn() + "'");
|
||||
}
|
||||
|
||||
if (usePasswordAttrCompare && isPasswordAttrCompare(user, password)) {
|
||||
@@ -112,8 +113,8 @@ public final class PasswordComparisonAuthenticator extends AbstractLdapAuthentic
|
||||
else if (isLdapPasswordCompare(user, ldapTemplate, password)) {
|
||||
return user;
|
||||
}
|
||||
throw new BadCredentialsException(messages.getMessage(
|
||||
"PasswordComparisonAuthenticator.badCredentials", "Bad credentials"));
|
||||
throw new BadCredentialsException(
|
||||
messages.getMessage("PasswordComparisonAuthenticator.badCredentials", "Bad credentials"));
|
||||
}
|
||||
|
||||
private boolean isPasswordAttrCompare(DirContextOperations user, String password) {
|
||||
@@ -132,17 +133,15 @@ public final class PasswordComparisonAuthenticator extends AbstractLdapAuthentic
|
||||
return String.valueOf(passwordAttrValue);
|
||||
}
|
||||
|
||||
private boolean isLdapPasswordCompare(DirContextOperations user,
|
||||
SpringSecurityLdapTemplate ldapTemplate, String password) {
|
||||
private boolean isLdapPasswordCompare(DirContextOperations user, SpringSecurityLdapTemplate ldapTemplate,
|
||||
String password) {
|
||||
String encodedPassword = passwordEncoder.encode(password);
|
||||
byte[] passwordBytes = Utf8.encode(encodedPassword);
|
||||
return ldapTemplate.compare(user.getDn().toString(), passwordAttributeName,
|
||||
passwordBytes);
|
||||
return ldapTemplate.compare(user.getDn().toString(), passwordAttributeName, passwordBytes);
|
||||
}
|
||||
|
||||
public void setPasswordAttributeName(String passwordAttribute) {
|
||||
Assert.hasLength(passwordAttribute,
|
||||
"passwordAttributeName must not be empty or null");
|
||||
Assert.hasLength(passwordAttribute, "passwordAttributeName must not be empty or null");
|
||||
this.passwordAttributeName = passwordAttribute;
|
||||
}
|
||||
|
||||
@@ -155,4 +154,5 @@ public final class PasswordComparisonAuthenticator extends AbstractLdapAuthentic
|
||||
this.passwordEncoder = passwordEncoder;
|
||||
setUsePasswordAttrCompare(true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -36,17 +36,15 @@ import org.apache.commons.logging.LogFactory;
|
||||
* @since 2.0
|
||||
*/
|
||||
public class SpringSecurityAuthenticationSource implements AuthenticationSource {
|
||||
private static final Log log = LogFactory
|
||||
.getLog(SpringSecurityAuthenticationSource.class);
|
||||
|
||||
private static final Log log = LogFactory.getLog(SpringSecurityAuthenticationSource.class);
|
||||
|
||||
/**
|
||||
* Get the principals of the logged in user, in this case the distinguished name.
|
||||
*
|
||||
* @return the distinguished name of the logged in user.
|
||||
*/
|
||||
public String getPrincipal() {
|
||||
Authentication authentication = SecurityContextHolder.getContext()
|
||||
.getAuthentication();
|
||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||
|
||||
if (authentication == null) {
|
||||
log.warn("No Authentication object set in SecurityContext - returning empty String as Principal");
|
||||
@@ -67,8 +65,7 @@ public class SpringSecurityAuthenticationSource implements AuthenticationSource
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException(
|
||||
"The principal property of the authentication object"
|
||||
+ "needs to be an LdapUserDetails.");
|
||||
"The principal property of the authentication object" + "needs to be an LdapUserDetails.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,8 +73,7 @@ public class SpringSecurityAuthenticationSource implements AuthenticationSource
|
||||
* @see org.springframework.ldap.core.AuthenticationSource#getCredentials()
|
||||
*/
|
||||
public String getCredentials() {
|
||||
Authentication authentication = SecurityContextHolder.getContext()
|
||||
.getAuthentication();
|
||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||
|
||||
if (authentication == null) {
|
||||
log.warn("No Authentication object set in SecurityContext - returning empty String as Credentials");
|
||||
@@ -86,4 +82,5 @@ public class SpringSecurityAuthenticationSource implements AuthenticationSource
|
||||
|
||||
return (String) authentication.getCredentials();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -27,12 +27,11 @@ import org.springframework.util.Assert;
|
||||
* Simple LdapAuthoritiesPopulator which delegates to a UserDetailsService, using the name
|
||||
* which was supplied at login as the username.
|
||||
*
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @since 2.0
|
||||
*/
|
||||
public class UserDetailsServiceLdapAuthoritiesPopulator implements
|
||||
LdapAuthoritiesPopulator {
|
||||
public class UserDetailsServiceLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator {
|
||||
|
||||
private final UserDetailsService userDetailsService;
|
||||
|
||||
public UserDetailsServiceLdapAuthoritiesPopulator(UserDetailsService userService) {
|
||||
@@ -40,8 +39,9 @@ public class UserDetailsServiceLdapAuthoritiesPopulator implements
|
||||
this.userDetailsService = userService;
|
||||
}
|
||||
|
||||
public Collection<? extends GrantedAuthority> getGrantedAuthorities(
|
||||
DirContextOperations userData, String username) {
|
||||
public Collection<? extends GrantedAuthority> getGrantedAuthorities(DirContextOperations userData,
|
||||
String username) {
|
||||
return userDetailsService.loadUserByUsername(username).getAuthorities();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -41,10 +41,10 @@ import org.springframework.security.core.AuthenticationException;
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public final class ActiveDirectoryAuthenticationException extends AuthenticationException {
|
||||
|
||||
private final String dataCode;
|
||||
|
||||
ActiveDirectoryAuthenticationException(String dataCode, String message,
|
||||
Throwable cause) {
|
||||
ActiveDirectoryAuthenticationException(String dataCode, String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
this.dataCode = dataCode;
|
||||
}
|
||||
@@ -52,4 +52,5 @@ public final class ActiveDirectoryAuthenticationException extends Authentication
|
||||
public String getDataCode() {
|
||||
return dataCode;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -53,8 +53,8 @@ import java.util.regex.Pattern;
|
||||
* Specialized LDAP authentication provider which uses Active Directory configuration
|
||||
* conventions.
|
||||
* <p>
|
||||
* It will authenticate using the Active Directory <a
|
||||
* href="https://msdn.microsoft.com/en-us/library/ms680857%28VS.85%29.aspx">
|
||||
* It will authenticate using the Active Directory
|
||||
* <a href="https://msdn.microsoft.com/en-us/library/ms680857%28VS.85%29.aspx">
|
||||
* {@code userPrincipalName}</a> or a custom {@link #setSearchFilter(String) searchFilter}
|
||||
* in the form {@code username@domain}. If the username does not already end with the
|
||||
* domain name, the {@code userPrincipalName} will be built by appending the configured
|
||||
@@ -90,26 +90,37 @@ import java.util.regex.Pattern;
|
||||
* @author Rob Winch
|
||||
* @since 3.1
|
||||
*/
|
||||
public final class ActiveDirectoryLdapAuthenticationProvider extends
|
||||
AbstractLdapAuthenticationProvider {
|
||||
private static final Pattern SUB_ERROR_CODE = Pattern
|
||||
.compile(".*data\\s([0-9a-f]{3,4}).*");
|
||||
public final class ActiveDirectoryLdapAuthenticationProvider extends AbstractLdapAuthenticationProvider {
|
||||
|
||||
private static final Pattern SUB_ERROR_CODE = Pattern.compile(".*data\\s([0-9a-f]{3,4}).*");
|
||||
|
||||
// Error codes
|
||||
private static final int USERNAME_NOT_FOUND = 0x525;
|
||||
|
||||
private static final int INVALID_PASSWORD = 0x52e;
|
||||
|
||||
private static final int NOT_PERMITTED = 0x530;
|
||||
|
||||
private static final int PASSWORD_EXPIRED = 0x532;
|
||||
|
||||
private static final int ACCOUNT_DISABLED = 0x533;
|
||||
|
||||
private static final int ACCOUNT_EXPIRED = 0x701;
|
||||
|
||||
private static final int PASSWORD_NEEDS_RESET = 0x773;
|
||||
|
||||
private static final int ACCOUNT_LOCKED = 0x775;
|
||||
|
||||
private final String domain;
|
||||
|
||||
private final String rootDn;
|
||||
|
||||
private final String url;
|
||||
|
||||
private boolean convertSubErrorCodesToExceptions;
|
||||
|
||||
private String searchFilter = "(&(objectClass=user)(userPrincipalName={0}))";
|
||||
|
||||
private Map<String, Object> contextEnvironmentProperties = new HashMap<>();
|
||||
|
||||
// Only used to allow tests to substitute a mock LdapContext
|
||||
@@ -120,8 +131,7 @@ public final class ActiveDirectoryLdapAuthenticationProvider extends
|
||||
* @param url an LDAP url (or multiple URLs)
|
||||
* @param rootDn the root DN (may be null or empty)
|
||||
*/
|
||||
public ActiveDirectoryLdapAuthenticationProvider(String domain, String url,
|
||||
String rootDn) {
|
||||
public ActiveDirectoryLdapAuthenticationProvider(String domain, String url, String rootDn) {
|
||||
Assert.isTrue(StringUtils.hasText(url), "Url cannot be empty");
|
||||
this.domain = StringUtils.hasText(domain) ? domain.toLowerCase() : null;
|
||||
this.url = url;
|
||||
@@ -140,8 +150,7 @@ public final class ActiveDirectoryLdapAuthenticationProvider extends
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DirContextOperations doAuthentication(
|
||||
UsernamePasswordAuthenticationToken auth) {
|
||||
protected DirContextOperations doAuthentication(UsernamePasswordAuthenticationToken auth) {
|
||||
String username = auth.getName();
|
||||
String password = (String) auth.getCredentials();
|
||||
DirContext ctx = null;
|
||||
@@ -154,8 +163,7 @@ public final class ActiveDirectoryLdapAuthenticationProvider extends
|
||||
throw badLdapConnection(e);
|
||||
}
|
||||
catch (NamingException e) {
|
||||
logger.error("Failed to locate directory entry for authenticated user: "
|
||||
+ username, e);
|
||||
logger.error("Failed to locate directory entry for authenticated user: " + username, e);
|
||||
throw badCredentials(e);
|
||||
}
|
||||
finally {
|
||||
@@ -168,8 +176,8 @@ public final class ActiveDirectoryLdapAuthenticationProvider extends
|
||||
* obtained from the user's Active Directory entry.
|
||||
*/
|
||||
@Override
|
||||
protected Collection<? extends GrantedAuthority> loadUserAuthorities(
|
||||
DirContextOperations userData, String username, String password) {
|
||||
protected Collection<? extends GrantedAuthority> loadUserAuthorities(DirContextOperations userData, String username,
|
||||
String password) {
|
||||
String[] groups = userData.getStringAttributes("memberOf");
|
||||
|
||||
if (groups == null) {
|
||||
@@ -182,12 +190,10 @@ public final class ActiveDirectoryLdapAuthenticationProvider extends
|
||||
logger.debug("'memberOf' attribute values: " + Arrays.asList(groups));
|
||||
}
|
||||
|
||||
ArrayList<GrantedAuthority> authorities = new ArrayList<>(
|
||||
groups.length);
|
||||
ArrayList<GrantedAuthority> authorities = new ArrayList<>(groups.length);
|
||||
|
||||
for (String group : groups) {
|
||||
authorities.add(new SimpleGrantedAuthority(new DistinguishedName(group)
|
||||
.removeLast().getValue()));
|
||||
authorities.add(new SimpleGrantedAuthority(new DistinguishedName(group).removeLast().getValue()));
|
||||
}
|
||||
|
||||
return authorities;
|
||||
@@ -211,11 +217,11 @@ public final class ActiveDirectoryLdapAuthenticationProvider extends
|
||||
return contextFactory.createContext(env);
|
||||
}
|
||||
catch (NamingException e) {
|
||||
if ((e instanceof AuthenticationException)
|
||||
|| (e instanceof OperationNotSupportedException)) {
|
||||
if ((e instanceof AuthenticationException) || (e instanceof OperationNotSupportedException)) {
|
||||
handleBindException(bindPrincipal, e);
|
||||
throw badCredentials(e);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
throw LdapUtils.convertLdapException(e);
|
||||
}
|
||||
}
|
||||
@@ -235,8 +241,7 @@ public final class ActiveDirectoryLdapAuthenticationProvider extends
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info("Active Directory authentication failed: "
|
||||
+ subCodeToLogMessage(subErrorCode));
|
||||
logger.info("Active Directory authentication failed: " + subCodeToLogMessage(subErrorCode));
|
||||
|
||||
if (convertSubErrorCodesToExceptions) {
|
||||
raiseExceptionForErrorCode(subErrorCode, exception);
|
||||
@@ -263,23 +268,20 @@ public final class ActiveDirectoryLdapAuthenticationProvider extends
|
||||
|
||||
private void raiseExceptionForErrorCode(int code, NamingException exception) {
|
||||
String hexString = Integer.toHexString(code);
|
||||
Throwable cause = new ActiveDirectoryAuthenticationException(hexString,
|
||||
exception.getMessage(), exception);
|
||||
Throwable cause = new ActiveDirectoryAuthenticationException(hexString, exception.getMessage(), exception);
|
||||
switch (code) {
|
||||
case PASSWORD_EXPIRED:
|
||||
throw new CredentialsExpiredException(messages.getMessage(
|
||||
"LdapAuthenticationProvider.credentialsExpired",
|
||||
throw new CredentialsExpiredException(messages.getMessage("LdapAuthenticationProvider.credentialsExpired",
|
||||
"User credentials have expired"), cause);
|
||||
case ACCOUNT_DISABLED:
|
||||
throw new DisabledException(messages.getMessage(
|
||||
"LdapAuthenticationProvider.disabled", "User is disabled"), cause);
|
||||
case ACCOUNT_EXPIRED:
|
||||
throw new AccountExpiredException(messages.getMessage(
|
||||
"LdapAuthenticationProvider.expired", "User account has expired"),
|
||||
throw new DisabledException(messages.getMessage("LdapAuthenticationProvider.disabled", "User is disabled"),
|
||||
cause);
|
||||
case ACCOUNT_EXPIRED:
|
||||
throw new AccountExpiredException(
|
||||
messages.getMessage("LdapAuthenticationProvider.expired", "User account has expired"), cause);
|
||||
case ACCOUNT_LOCKED:
|
||||
throw new LockedException(messages.getMessage(
|
||||
"LdapAuthenticationProvider.locked", "User account is locked"), cause);
|
||||
throw new LockedException(
|
||||
messages.getMessage("LdapAuthenticationProvider.locked", "User account is locked"), cause);
|
||||
default:
|
||||
throw badCredentials(cause);
|
||||
}
|
||||
@@ -309,8 +311,8 @@ public final class ActiveDirectoryLdapAuthenticationProvider extends
|
||||
}
|
||||
|
||||
private BadCredentialsException badCredentials() {
|
||||
return new BadCredentialsException(messages.getMessage(
|
||||
"LdapAuthenticationProvider.badCredentials", "Bad credentials"));
|
||||
return new BadCredentialsException(
|
||||
messages.getMessage("LdapAuthenticationProvider.badCredentials", "Bad credentials"));
|
||||
}
|
||||
|
||||
private BadCredentialsException badCredentials(Throwable cause) {
|
||||
@@ -319,23 +321,19 @@ public final class ActiveDirectoryLdapAuthenticationProvider extends
|
||||
|
||||
private InternalAuthenticationServiceException badLdapConnection(Throwable cause) {
|
||||
return new InternalAuthenticationServiceException(messages.getMessage(
|
||||
"LdapAuthenticationProvider.badLdapConnection",
|
||||
"Connection to LDAP server failed."), cause);
|
||||
"LdapAuthenticationProvider.badLdapConnection", "Connection to LDAP server failed."), cause);
|
||||
}
|
||||
|
||||
private DirContextOperations searchForUser(DirContext context, String username)
|
||||
throws NamingException {
|
||||
private DirContextOperations searchForUser(DirContext context, String username) throws NamingException {
|
||||
SearchControls searchControls = new SearchControls();
|
||||
searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
|
||||
|
||||
String bindPrincipal = createBindPrincipal(username);
|
||||
String searchRoot = rootDn != null ? rootDn
|
||||
: searchRootFromPrincipal(bindPrincipal);
|
||||
String searchRoot = rootDn != null ? rootDn : searchRootFromPrincipal(bindPrincipal);
|
||||
|
||||
try {
|
||||
return SpringSecurityLdapTemplate.searchForSingleEntryInternal(context,
|
||||
searchControls, searchRoot, searchFilter,
|
||||
new Object[] { bindPrincipal, username });
|
||||
return SpringSecurityLdapTemplate.searchForSingleEntryInternal(context, searchControls, searchRoot,
|
||||
searchFilter, new Object[] { bindPrincipal, username });
|
||||
}
|
||||
catch (CommunicationException ldapCommunicationException) {
|
||||
throw badLdapConnection(ldapCommunicationException);
|
||||
@@ -362,8 +360,7 @@ public final class ActiveDirectoryLdapAuthenticationProvider extends
|
||||
throw badCredentials();
|
||||
}
|
||||
|
||||
return rootDnFromDomain(bindPrincipal.substring(atChar + 1,
|
||||
bindPrincipal.length()));
|
||||
return rootDnFromDomain(bindPrincipal.substring(atChar + 1, bindPrincipal.length()));
|
||||
}
|
||||
|
||||
private String rootDnFromDomain(String domain) {
|
||||
@@ -398,12 +395,10 @@ public final class ActiveDirectoryLdapAuthenticationProvider extends
|
||||
* {@link AccountExpiredException} or {@link LockedException} will be thrown for the
|
||||
* corresponding codes. All other codes will result in the default
|
||||
* {@code BadCredentialsException}.
|
||||
*
|
||||
* @param convertSubErrorCodesToExceptions {@code true} to raise an exception based on
|
||||
* the AD error code.
|
||||
*/
|
||||
public void setConvertSubErrorCodesToExceptions(
|
||||
boolean convertSubErrorCodesToExceptions) {
|
||||
public void setConvertSubErrorCodesToExceptions(boolean convertSubErrorCodesToExceptions) {
|
||||
this.convertSubErrorCodesToExceptions = convertSubErrorCodesToExceptions;
|
||||
}
|
||||
|
||||
@@ -414,7 +409,6 @@ public final class ActiveDirectoryLdapAuthenticationProvider extends
|
||||
* <p>
|
||||
* Defaults to: {@code (&(objectClass=user)(userPrincipalName={0}))}
|
||||
* </p>
|
||||
*
|
||||
* @param searchFilter the filter string
|
||||
*
|
||||
* @since 3.2.6
|
||||
@@ -426,8 +420,8 @@ public final class ActiveDirectoryLdapAuthenticationProvider extends
|
||||
|
||||
/**
|
||||
* Allows a custom environment properties to be used to create initial LDAP context.
|
||||
*
|
||||
* @param environment the additional environment parameters to use when creating the LDAP Context
|
||||
* @param environment the additional environment parameters to use when creating the
|
||||
* LDAP Context
|
||||
*/
|
||||
public void setContextEnvironmentProperties(Map<String, Object> environment) {
|
||||
Assert.notEmpty(environment, "environment must not be empty");
|
||||
@@ -435,8 +429,11 @@ public final class ActiveDirectoryLdapAuthenticationProvider extends
|
||||
}
|
||||
|
||||
static class ContextFactory {
|
||||
|
||||
DirContext createContext(Hashtable<?, ?> env) throws NamingException {
|
||||
return new InitialLdapContext(env, null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -14,13 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
/**
|
||||
* The LDAP authentication provider package. Interfaces are provided for
|
||||
* both authentication and retrieval of user roles from an LDAP server.
|
||||
* The LDAP authentication provider package. Interfaces are provided for both
|
||||
* authentication and retrieval of user roles from an LDAP server.
|
||||
* <p>
|
||||
* The main provider class is <tt>LdapAuthenticationProvider</tt>.
|
||||
* This is configured with an <tt>LdapAuthenticator</tt> instance and
|
||||
* an <tt>LdapAuthoritiesPopulator</tt>. The latter is used to obtain the
|
||||
* list of roles for the user.
|
||||
* The main provider class is <tt>LdapAuthenticationProvider</tt>. This is configured with
|
||||
* an <tt>LdapAuthenticator</tt> instance and an <tt>LdapAuthoritiesPopulator</tt>. The
|
||||
* latter is used to obtain the list of roles for the user.
|
||||
*/
|
||||
package org.springframework.security.ldap.authentication;
|
||||
|
||||
|
||||
@@ -17,4 +17,3 @@
|
||||
* Spring Security's LDAP module.
|
||||
*/
|
||||
package org.springframework.security.ldap;
|
||||
|
||||
|
||||
@@ -44,8 +44,7 @@ public class PasswordPolicyAwareContextSource extends DefaultSpringSecurityConte
|
||||
}
|
||||
|
||||
@Override
|
||||
public DirContext getContext(String principal, String credentials)
|
||||
throws PasswordPolicyException {
|
||||
public DirContext getContext(String principal, String credentials) throws PasswordPolicyException {
|
||||
if (principal.equals(userDn)) {
|
||||
return super.getContext(principal, credentials);
|
||||
}
|
||||
@@ -53,8 +52,7 @@ public class PasswordPolicyAwareContextSource extends DefaultSpringSecurityConte
|
||||
final boolean debug = logger.isDebugEnabled();
|
||||
|
||||
if (debug) {
|
||||
logger.debug("Binding as '" + userDn + "', prior to reconnect as user '"
|
||||
+ principal + "'");
|
||||
logger.debug("Binding as '" + userDn + "', prior to reconnect as user '" + principal + "'");
|
||||
}
|
||||
|
||||
// First bind as manager user before rebinding as the specific principal.
|
||||
@@ -68,8 +66,7 @@ public class PasswordPolicyAwareContextSource extends DefaultSpringSecurityConte
|
||||
ctx.reconnect(rctls);
|
||||
}
|
||||
catch (javax.naming.NamingException ne) {
|
||||
PasswordPolicyResponseControl ctrl = PasswordPolicyControlExtractor
|
||||
.extractControl(ctx);
|
||||
PasswordPolicyResponseControl ctrl = PasswordPolicyControlExtractor.extractControl(ctx);
|
||||
if (debug) {
|
||||
logger.debug("Failed to obtain context", ne);
|
||||
logger.debug("Password policy response: " + ctrl);
|
||||
@@ -87,8 +84,7 @@ public class PasswordPolicyAwareContextSource extends DefaultSpringSecurityConte
|
||||
}
|
||||
|
||||
if (debug) {
|
||||
logger.debug("PPolicy control returned: "
|
||||
+ PasswordPolicyControlExtractor.extractControl(ctx));
|
||||
logger.debug("PPolicy control returned: " + PasswordPolicyControlExtractor.extractControl(ctx));
|
||||
}
|
||||
|
||||
return ctx;
|
||||
@@ -99,9 +95,9 @@ public class PasswordPolicyAwareContextSource extends DefaultSpringSecurityConte
|
||||
protected Hashtable getAuthenticatedEnv(String principal, String credentials) {
|
||||
Hashtable env = super.getAuthenticatedEnv(principal, credentials);
|
||||
|
||||
env.put(LdapContext.CONTROL_FACTORIES,
|
||||
PasswordPolicyControlFactory.class.getName());
|
||||
env.put(LdapContext.CONTROL_FACTORIES, PasswordPolicyControlFactory.class.getName());
|
||||
|
||||
return env;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -28,10 +28,10 @@ import javax.naming.ldap.Control;
|
||||
*
|
||||
* @author Stefan Zoerner
|
||||
* @author Luke Taylor
|
||||
*
|
||||
* @see PasswordPolicyResponseControl
|
||||
*/
|
||||
public class PasswordPolicyControl implements Control {
|
||||
|
||||
// ~ Static fields/initializers
|
||||
// =====================================================================================
|
||||
|
||||
@@ -55,7 +55,6 @@ public class PasswordPolicyControl implements Control {
|
||||
|
||||
/**
|
||||
* Creates a (request) control.
|
||||
*
|
||||
* @param critical indicates whether the control is critical for the client
|
||||
*/
|
||||
public PasswordPolicyControl(boolean critical) {
|
||||
@@ -68,7 +67,6 @@ public class PasswordPolicyControl implements Control {
|
||||
/**
|
||||
* Retrieves the ASN.1 BER encoded value of the LDAP control. The request value for
|
||||
* this control is always empty.
|
||||
*
|
||||
* @return always null
|
||||
*/
|
||||
public byte[] getEncodedValue() {
|
||||
@@ -88,4 +86,5 @@ public class PasswordPolicyControl implements Control {
|
||||
public boolean isCritical() {
|
||||
return critical;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -29,8 +29,8 @@ import org.apache.commons.logging.LogFactory;
|
||||
* @since 3.0
|
||||
*/
|
||||
public class PasswordPolicyControlExtractor {
|
||||
private static final Log logger = LogFactory
|
||||
.getLog(PasswordPolicyControlExtractor.class);
|
||||
|
||||
private static final Log logger = LogFactory.getLog(PasswordPolicyControlExtractor.class);
|
||||
|
||||
public static PasswordPolicyResponseControl extractControl(DirContext dirCtx) {
|
||||
LdapContext ctx = (LdapContext) dirCtx;
|
||||
|
||||
@@ -26,6 +26,7 @@ import javax.naming.ldap.ControlFactory;
|
||||
* @author Luke Taylor
|
||||
*/
|
||||
public class PasswordPolicyControlFactory extends ControlFactory {
|
||||
|
||||
// ~ Methods
|
||||
// ========================================================================================================
|
||||
|
||||
@@ -33,9 +34,7 @@ public class PasswordPolicyControlFactory extends ControlFactory {
|
||||
* Creates an instance of PasswordPolicyResponseControl if the passed control is a
|
||||
* response control of this type. Attributes of the result are filled with the correct
|
||||
* values (e.g. error code).
|
||||
*
|
||||
* @param ctl the control the check
|
||||
*
|
||||
* @return a response control of type PasswordPolicyResponseControl, or null
|
||||
*/
|
||||
public Control getControlInstance(Control ctl) {
|
||||
@@ -45,4 +44,5 @@ public class PasswordPolicyControlFactory extends ControlFactory {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,7 +20,9 @@ package org.springframework.security.ldap.ppolicy;
|
||||
* @since 3.0
|
||||
*/
|
||||
public interface PasswordPolicyData {
|
||||
|
||||
int getTimeBeforeExpiration();
|
||||
|
||||
int getGraceLoginsRemaining();
|
||||
|
||||
}
|
||||
|
||||
@@ -39,20 +39,24 @@ package org.springframework.security.ldap.ppolicy;
|
||||
* @since 3.0
|
||||
*/
|
||||
public enum PasswordPolicyErrorStatus {
|
||||
PASSWORD_EXPIRED("ppolicy.expired", "Your password has expired"), ACCOUNT_LOCKED(
|
||||
"ppolicy.locked", "Account is locked"), CHANGE_AFTER_RESET(
|
||||
"ppolicy.change.after.reset",
|
||||
"Your password must be changed after being reset"), PASSWORD_MOD_NOT_ALLOWED(
|
||||
"ppolicy.mod.not.allowed", "Password cannot be changed"), MUST_SUPPLY_OLD_PASSWORD(
|
||||
"ppolicy.must.supply.old.password", "The old password must be supplied"), INSUFFICIENT_PASSWORD_QUALITY(
|
||||
"ppolicy.insufficient.password.quality",
|
||||
"The supplied password is of insufficient quality"), PASSWORD_TOO_SHORT(
|
||||
"ppolicy.password.too.short", "The supplied password is too short"), PASSWORD_TOO_YOUNG(
|
||||
"ppolicy.password.too.young",
|
||||
"Your password was changed too recently to be changed again"), PASSWORD_IN_HISTORY(
|
||||
"ppolicy.password.in.history", "The supplied password has already been used");
|
||||
|
||||
PASSWORD_EXPIRED("ppolicy.expired", "Your password has expired"), ACCOUNT_LOCKED("ppolicy.locked",
|
||||
"Account is locked"), CHANGE_AFTER_RESET("ppolicy.change.after.reset",
|
||||
"Your password must be changed after being reset"), PASSWORD_MOD_NOT_ALLOWED(
|
||||
"ppolicy.mod.not.allowed",
|
||||
"Password cannot be changed"), MUST_SUPPLY_OLD_PASSWORD("ppolicy.must.supply.old.password",
|
||||
"The old password must be supplied"), INSUFFICIENT_PASSWORD_QUALITY(
|
||||
"ppolicy.insufficient.password.quality",
|
||||
"The supplied password is of insufficient quality"), PASSWORD_TOO_SHORT(
|
||||
"ppolicy.password.too.short",
|
||||
"The supplied password is too short"), PASSWORD_TOO_YOUNG(
|
||||
"ppolicy.password.too.young",
|
||||
"Your password was changed too recently to be changed again"), PASSWORD_IN_HISTORY(
|
||||
"ppolicy.password.in.history",
|
||||
"The supplied password has already been used");
|
||||
|
||||
private final String errorCode;
|
||||
|
||||
private final String defaultMessage;
|
||||
|
||||
PasswordPolicyErrorStatus(String errorCode, String defaultMessage) {
|
||||
@@ -67,4 +71,5 @@ public enum PasswordPolicyErrorStatus {
|
||||
public String getDefaultMessage() {
|
||||
return defaultMessage;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ package org.springframework.security.ldap.ppolicy;
|
||||
* @since 3.0
|
||||
*/
|
||||
public class PasswordPolicyException extends RuntimeException {
|
||||
|
||||
private final PasswordPolicyErrorStatus status;
|
||||
|
||||
public PasswordPolicyException(PasswordPolicyErrorStatus status) {
|
||||
@@ -35,4 +36,5 @@ public class PasswordPolicyException extends RuntimeException {
|
||||
public PasswordPolicyErrorStatus getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -41,20 +41,19 @@ import org.springframework.dao.DataRetrievalFailureException;
|
||||
* <tt>graceLoginsRemaining</tt>.
|
||||
* <p>
|
||||
*
|
||||
*
|
||||
* @author Stefan Zoerner
|
||||
* @author Luke Taylor
|
||||
*
|
||||
* @see org.springframework.security.ldap.ppolicy.PasswordPolicyControl
|
||||
* @see <a href="https://www.ibm.com/developerworks/tivoli/library/t-ldap-controls/">Stefan
|
||||
* Zoerner's IBM developerworks article on LDAP controls.</a>
|
||||
* @see <a href=
|
||||
* "https://www.ibm.com/developerworks/tivoli/library/t-ldap-controls/">Stefan Zoerner's
|
||||
* IBM developerworks article on LDAP controls.</a>
|
||||
*/
|
||||
public class PasswordPolicyResponseControl extends PasswordPolicyControl {
|
||||
|
||||
// ~ Static fields/initializers
|
||||
// =====================================================================================
|
||||
|
||||
private static final Log logger = LogFactory
|
||||
.getLog(PasswordPolicyResponseControl.class);
|
||||
private static final Log logger = LogFactory.getLog(PasswordPolicyResponseControl.class);
|
||||
|
||||
// ~ Instance fields
|
||||
// ================================================================================================
|
||||
@@ -64,6 +63,7 @@ public class PasswordPolicyResponseControl extends PasswordPolicyControl {
|
||||
private PasswordPolicyErrorStatus errorStatus;
|
||||
|
||||
private int graceLoginsRemaining = Integer.MAX_VALUE;
|
||||
|
||||
private int timeBeforeExpiration = Integer.MAX_VALUE;
|
||||
|
||||
// ~ Constructors
|
||||
@@ -116,7 +116,6 @@ public class PasswordPolicyResponseControl extends PasswordPolicyControl {
|
||||
|
||||
/**
|
||||
* Returns the graceLoginsRemaining.
|
||||
*
|
||||
* @return Returns the graceLoginsRemaining.
|
||||
*/
|
||||
public int getGraceLoginsRemaining() {
|
||||
@@ -125,7 +124,6 @@ public class PasswordPolicyResponseControl extends PasswordPolicyControl {
|
||||
|
||||
/**
|
||||
* Returns the timeBeforeExpiration.
|
||||
*
|
||||
* @return Returns the time before expiration in seconds
|
||||
*/
|
||||
public int getTimeBeforeExpiration() {
|
||||
@@ -134,7 +132,6 @@ public class PasswordPolicyResponseControl extends PasswordPolicyControl {
|
||||
|
||||
/**
|
||||
* Checks whether an error is present.
|
||||
*
|
||||
* @return true, if an error is present
|
||||
*/
|
||||
public boolean hasError() {
|
||||
@@ -143,12 +140,10 @@ public class PasswordPolicyResponseControl extends PasswordPolicyControl {
|
||||
|
||||
/**
|
||||
* Checks whether a warning is present.
|
||||
*
|
||||
* @return true, if a warning is present
|
||||
*/
|
||||
public boolean hasWarning() {
|
||||
return (this.graceLoginsRemaining != Integer.MAX_VALUE)
|
||||
|| (this.timeBeforeExpiration != Integer.MAX_VALUE);
|
||||
return (this.graceLoginsRemaining != Integer.MAX_VALUE) || (this.timeBeforeExpiration != Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
public boolean isExpired() {
|
||||
@@ -165,7 +160,6 @@ public class PasswordPolicyResponseControl extends PasswordPolicyControl {
|
||||
|
||||
/**
|
||||
* Determines whether an account locked error has been returned.
|
||||
*
|
||||
* @return true if the account is locked.
|
||||
*/
|
||||
public boolean isLocked() {
|
||||
@@ -175,7 +169,6 @@ public class PasswordPolicyResponseControl extends PasswordPolicyControl {
|
||||
/**
|
||||
* Create a textual representation containing error and warning messages, if any are
|
||||
* present.
|
||||
*
|
||||
* @return error and warning messages
|
||||
*/
|
||||
@Override
|
||||
@@ -187,13 +180,11 @@ public class PasswordPolicyResponseControl extends PasswordPolicyControl {
|
||||
}
|
||||
|
||||
if (this.graceLoginsRemaining != Integer.MAX_VALUE) {
|
||||
sb.append(", warning: ").append(this.graceLoginsRemaining)
|
||||
.append(" grace logins remain");
|
||||
sb.append(", warning: ").append(this.graceLoginsRemaining).append(" grace logins remain");
|
||||
}
|
||||
|
||||
if (this.timeBeforeExpiration != Integer.MAX_VALUE) {
|
||||
sb.append(", warning: time before expiration is ")
|
||||
.append(this.timeBeforeExpiration);
|
||||
sb.append(", warning: time before expiration is ").append(this.timeBeforeExpiration);
|
||||
}
|
||||
|
||||
if (!hasError() && !hasWarning()) {
|
||||
@@ -207,7 +198,9 @@ public class PasswordPolicyResponseControl extends PasswordPolicyControl {
|
||||
// ===============================================================================================
|
||||
|
||||
private interface PPolicyDecoder {
|
||||
|
||||
void decode() throws IOException;
|
||||
|
||||
}
|
||||
|
||||
// ~ Inner Classes
|
||||
@@ -217,19 +210,16 @@ public class PasswordPolicyResponseControl extends PasswordPolicyControl {
|
||||
* Decoder based on Netscape ldapsdk library
|
||||
*/
|
||||
private class NetscapeDecoder implements PPolicyDecoder {
|
||||
|
||||
public void decode() throws IOException {
|
||||
int[] bread = { 0 };
|
||||
BERSequence seq = (BERSequence) BERElement
|
||||
.getElement(new SpecificTagDecoder(),
|
||||
new ByteArrayInputStream(
|
||||
PasswordPolicyResponseControl.this.encodedValue),
|
||||
bread);
|
||||
BERSequence seq = (BERSequence) BERElement.getElement(new SpecificTagDecoder(),
|
||||
new ByteArrayInputStream(PasswordPolicyResponseControl.this.encodedValue), bread);
|
||||
|
||||
int size = seq.size();
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("PasswordPolicyResponse, ASN.1 sequence has " + size
|
||||
+ " elements");
|
||||
logger.debug("PasswordPolicyResponse, ASN.1 sequence has " + size + " elements");
|
||||
}
|
||||
|
||||
for (int i = 0; i < seq.size(); i++) {
|
||||
@@ -252,20 +242,20 @@ public class PasswordPolicyResponseControl extends PasswordPolicyControl {
|
||||
}
|
||||
else if (tag == 1) {
|
||||
BERIntegral error = (BERIntegral) elt.getValue();
|
||||
PasswordPolicyResponseControl.this.errorStatus = PasswordPolicyErrorStatus
|
||||
.values()[error.getValue()];
|
||||
PasswordPolicyResponseControl.this.errorStatus = PasswordPolicyErrorStatus.values()[error
|
||||
.getValue()];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SpecificTagDecoder extends BERTagDecoder {
|
||||
|
||||
/** Allows us to remember which of the two options we're decoding */
|
||||
private Boolean inChoice = null;
|
||||
|
||||
@Override
|
||||
public BERElement getElement(BERTagDecoder decoder, int tag,
|
||||
InputStream stream, int[] bytesRead, boolean[] implicit)
|
||||
throws IOException {
|
||||
public BERElement getElement(BERTagDecoder decoder, int tag, InputStream stream, int[] bytesRead,
|
||||
boolean[] implicit) throws IOException {
|
||||
tag &= 0x1F;
|
||||
implicit[0] = false;
|
||||
|
||||
@@ -278,8 +268,7 @@ public class PasswordPolicyResponseControl extends PasswordPolicyControl {
|
||||
BERElement.readLengthOctets(stream, bytesRead);
|
||||
|
||||
int[] componentLength = new int[1];
|
||||
BERElement choice = new BERChoice(decoder, stream,
|
||||
componentLength);
|
||||
BERElement choice = new BERChoice(decoder, stream, componentLength);
|
||||
bytesRead[0] += componentLength[0];
|
||||
|
||||
// inChoice = null;
|
||||
@@ -312,7 +301,9 @@ public class PasswordPolicyResponseControl extends PasswordPolicyControl {
|
||||
private void setInChoice(boolean inChoice) {
|
||||
this.inChoice = inChoice;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** Decoder based on the OpenLDAP/Novell JLDAP library */
|
||||
@@ -377,4 +368,5 @@ public class PasswordPolicyResponseControl extends PasswordPolicyControl {
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
@@ -14,11 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
/**
|
||||
* Implementation of password policy functionality based on the
|
||||
* <a href="https://tools.ietf.org/draft/draft-behera-ldap-password-policy/draft-behera-ldap-password-policy-09.txt">
|
||||
* Implementation of password policy functionality based on the <a href=
|
||||
* "https://tools.ietf.org/draft/draft-behera-ldap-password-policy/draft-behera-ldap-password-policy-09.txt">
|
||||
* Password Policy for LDAP Directories</a>.
|
||||
* <p>
|
||||
* This code will not work with servers such as Active Directory, which do not implement this standard.
|
||||
* This code will not work with servers such as Active Directory, which do not implement
|
||||
* this standard.
|
||||
*/
|
||||
package org.springframework.security.ldap.ppolicy;
|
||||
|
||||
|
||||
@@ -37,10 +37,10 @@ import javax.naming.directory.SearchControls;
|
||||
*
|
||||
* @author Robert Sanders
|
||||
* @author Luke Taylor
|
||||
*
|
||||
* @see SearchControls
|
||||
*/
|
||||
public class FilterBasedLdapUserSearch implements LdapUserSearch {
|
||||
|
||||
// ~ Static fields/initializers
|
||||
// =====================================================================================
|
||||
|
||||
@@ -57,7 +57,9 @@ public class FilterBasedLdapUserSearch implements LdapUserSearch {
|
||||
*/
|
||||
private final SearchControls searchControls = new SearchControls();
|
||||
|
||||
/** Context name to search in, relative to the base of the configured ContextSource. */
|
||||
/**
|
||||
* Context name to search in, relative to the base of the configured ContextSource.
|
||||
*/
|
||||
private String searchBase = "";
|
||||
|
||||
/**
|
||||
@@ -79,12 +81,10 @@ public class FilterBasedLdapUserSearch implements LdapUserSearch {
|
||||
// ~ Constructors
|
||||
// ===================================================================================================
|
||||
|
||||
public FilterBasedLdapUserSearch(String searchBase, String searchFilter,
|
||||
BaseLdapPathContextSource contextSource) {
|
||||
public FilterBasedLdapUserSearch(String searchBase, String searchFilter, BaseLdapPathContextSource contextSource) {
|
||||
Assert.notNull(contextSource, "contextSource must not be null");
|
||||
Assert.notNull(searchFilter, "searchFilter must not be null.");
|
||||
Assert.notNull(searchBase,
|
||||
"searchBase must not be null (an empty string is acceptable).");
|
||||
Assert.notNull(searchBase, "searchBase must not be null (an empty string is acceptable).");
|
||||
|
||||
this.searchFilter = searchFilter;
|
||||
this.contextSource = contextSource;
|
||||
@@ -93,8 +93,8 @@ public class FilterBasedLdapUserSearch implements LdapUserSearch {
|
||||
setSearchSubtree(true);
|
||||
|
||||
if (searchBase.length() == 0) {
|
||||
logger.info("SearchBase not set. Searches will be performed from the root: "
|
||||
+ contextSource.getBaseLdapPath());
|
||||
logger.info(
|
||||
"SearchBase not set. Searches will be performed from the root: " + contextSource.getBaseLdapPath());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,36 +103,29 @@ public class FilterBasedLdapUserSearch implements LdapUserSearch {
|
||||
|
||||
/**
|
||||
* Return the LdapUserDetails containing the user's information
|
||||
*
|
||||
* @param username the username to search for.
|
||||
*
|
||||
* @return An LdapUserDetails object containing the details of the located user's
|
||||
* directory entry
|
||||
*
|
||||
* @throws UsernameNotFoundException if no matching entry is found.
|
||||
*/
|
||||
@Override
|
||||
public DirContextOperations searchForUser(String username) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Searching for user '" + username + "', with user search "
|
||||
+ this);
|
||||
logger.debug("Searching for user '" + username + "', with user search " + this);
|
||||
}
|
||||
|
||||
SpringSecurityLdapTemplate template = new SpringSecurityLdapTemplate(
|
||||
contextSource);
|
||||
SpringSecurityLdapTemplate template = new SpringSecurityLdapTemplate(contextSource);
|
||||
|
||||
template.setSearchControls(searchControls);
|
||||
|
||||
try {
|
||||
|
||||
return template.searchForSingleEntry(searchBase, searchFilter,
|
||||
new String[] { username });
|
||||
return template.searchForSingleEntry(searchBase, searchFilter, new String[] { username });
|
||||
|
||||
}
|
||||
catch (IncorrectResultSizeDataAccessException notFound) {
|
||||
if (notFound.getActualSize() == 0) {
|
||||
throw new UsernameNotFoundException("User " + username
|
||||
+ " not found in directory.");
|
||||
throw new UsernameNotFoundException("User " + username + " not found in directory.");
|
||||
}
|
||||
// Search should never return multiple results if properly configured, so just
|
||||
// rethrow
|
||||
@@ -143,7 +136,6 @@ public class FilterBasedLdapUserSearch implements LdapUserSearch {
|
||||
/**
|
||||
* Sets the corresponding property on the {@link SearchControls} instance used in the
|
||||
* search.
|
||||
*
|
||||
* @param deref the derefLinkFlag value as defined in SearchControls..
|
||||
*/
|
||||
public void setDerefLinkFlag(boolean deref) {
|
||||
@@ -153,18 +145,15 @@ public class FilterBasedLdapUserSearch implements LdapUserSearch {
|
||||
/**
|
||||
* If true then searches the entire subtree as identified by context, if false (the
|
||||
* default) then only searches the level identified by the context.
|
||||
*
|
||||
* @param searchSubtree true the underlying search controls should be set to
|
||||
* SearchControls.SUBTREE_SCOPE rather than SearchControls.ONELEVEL_SCOPE.
|
||||
*/
|
||||
public void setSearchSubtree(boolean searchSubtree) {
|
||||
searchControls.setSearchScope(searchSubtree ? SearchControls.SUBTREE_SCOPE
|
||||
: SearchControls.ONELEVEL_SCOPE);
|
||||
searchControls.setSearchScope(searchSubtree ? SearchControls.SUBTREE_SCOPE : SearchControls.ONELEVEL_SCOPE);
|
||||
}
|
||||
|
||||
/**
|
||||
* The time to wait before the search fails; the default is zero, meaning forever.
|
||||
*
|
||||
* @param searchTimeLimit the time limit for the search (in milliseconds).
|
||||
*/
|
||||
public void setSearchTimeLimit(int searchTimeLimit) {
|
||||
@@ -176,7 +165,6 @@ public class FilterBasedLdapUserSearch implements LdapUserSearch {
|
||||
* <p>
|
||||
* null indicates that all attributes will be returned. An empty array indicates no
|
||||
* attributes are returned.
|
||||
*
|
||||
* @param attrs An array of attribute names identifying the attributes that will be
|
||||
* returned. Can be null.
|
||||
*/
|
||||
@@ -191,11 +179,10 @@ public class FilterBasedLdapUserSearch implements LdapUserSearch {
|
||||
sb.append("[ searchFilter: '").append(searchFilter).append("', ");
|
||||
sb.append("searchBase: '").append(searchBase).append("'");
|
||||
sb.append(", scope: ")
|
||||
.append(searchControls.getSearchScope() == SearchControls.SUBTREE_SCOPE ? "subtree"
|
||||
: "single-level, ");
|
||||
.append(searchControls.getSearchScope() == SearchControls.SUBTREE_SCOPE ? "subtree" : "single-level, ");
|
||||
sb.append(", searchTimeLimit: ").append(searchControls.getTimeLimit());
|
||||
sb.append(", derefLinkFlag: ").append(searchControls.getDerefLinkFlag())
|
||||
.append(" ]");
|
||||
sb.append(", derefLinkFlag: ").append(searchControls.getDerefLinkFlag()).append(" ]");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -28,19 +28,19 @@ import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
* @author Luke Taylor
|
||||
*/
|
||||
public interface LdapUserSearch {
|
||||
|
||||
// ~ Methods
|
||||
// ========================================================================================================
|
||||
|
||||
/**
|
||||
* Locates a single user in the directory and returns the LDAP information for that
|
||||
* user.
|
||||
*
|
||||
* @param username the login name supplied to the authentication service.
|
||||
*
|
||||
* @return a DirContextOperations object containing the user's full DN and requested
|
||||
* attributes.
|
||||
* @throws UsernameNotFoundException if no user with the supplied name could be
|
||||
* located by the search.
|
||||
*/
|
||||
DirContextOperations searchForUser(String username) throws UsernameNotFoundException;
|
||||
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
/**
|
||||
* {@code LdapUserSearch} implementations. These may be used to locate the user in the directory.
|
||||
* {@code LdapUserSearch} implementations. These may be used to locate the user in the
|
||||
* directory.
|
||||
*/
|
||||
package org.springframework.security.ldap.search;
|
||||
|
||||
|
||||
@@ -65,8 +65,8 @@ import org.springframework.util.Assert;
|
||||
* application context is closed to allow the bean to be disposed of and the server
|
||||
* shutdown prior to attempting to start it again.
|
||||
* <p>
|
||||
* This class is intended for testing and internal security namespace use, only, and is not
|
||||
* considered part of the framework's public API.
|
||||
* This class is intended for testing and internal security namespace use, only, and is
|
||||
* not considered part of the framework's public API.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @author Rob Winch
|
||||
@@ -76,26 +76,36 @@ import org.springframework.util.Assert;
|
||||
* supported with no GA version to replace it.
|
||||
*/
|
||||
@Deprecated
|
||||
public class ApacheDSContainer implements InitializingBean, DisposableBean, Lifecycle,
|
||||
ApplicationContextAware {
|
||||
public class ApacheDSContainer implements InitializingBean, DisposableBean, Lifecycle, ApplicationContextAware {
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
final DefaultDirectoryService service;
|
||||
|
||||
LdapServer server;
|
||||
|
||||
private TcpTransport transport;
|
||||
|
||||
private ApplicationContext ctxt;
|
||||
|
||||
private File workingDir;
|
||||
|
||||
private boolean running;
|
||||
|
||||
private final String ldifResources;
|
||||
|
||||
private final JdbmPartition partition;
|
||||
|
||||
private final String root;
|
||||
|
||||
private int port = 53389;
|
||||
|
||||
private int localPort;
|
||||
|
||||
private boolean ldapOverSslEnabled;
|
||||
|
||||
private File keyStoreFile;
|
||||
|
||||
private String certificatePassord;
|
||||
|
||||
public ApacheDSContainer(String root, String ldifs) throws Exception {
|
||||
@@ -150,9 +160,9 @@ public class ApacheDSContainer implements InitializingBean, DisposableBean, Life
|
||||
|
||||
this.transport = new TcpTransport(port);
|
||||
if (ldapOverSslEnabled) {
|
||||
transport.setEnableSSL(true);
|
||||
server.setKeystoreFile(this.keyStoreFile.getAbsolutePath());
|
||||
server.setCertificatePassword(this.certificatePassord);
|
||||
transport.setEnableSSL(true);
|
||||
server.setKeystoreFile(this.keyStoreFile.getAbsolutePath());
|
||||
server.setCertificatePassword(this.certificatePassord);
|
||||
}
|
||||
server.setTransports(transport);
|
||||
start();
|
||||
@@ -162,24 +172,20 @@ public class ApacheDSContainer implements InitializingBean, DisposableBean, Life
|
||||
stop();
|
||||
}
|
||||
|
||||
public void setApplicationContext(ApplicationContext applicationContext)
|
||||
throws BeansException {
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
ctxt = applicationContext;
|
||||
}
|
||||
|
||||
public void setWorkingDirectory(File workingDir) {
|
||||
Assert.notNull(workingDir, "workingDir cannot be null");
|
||||
|
||||
logger.info("Setting working directory for LDAP_PROVIDER: "
|
||||
+ workingDir.getAbsolutePath());
|
||||
logger.info("Setting working directory for LDAP_PROVIDER: " + workingDir.getAbsolutePath());
|
||||
|
||||
if (workingDir.exists()) {
|
||||
throw new IllegalArgumentException(
|
||||
"The specified working directory '"
|
||||
+ workingDir.getAbsolutePath()
|
||||
+ "' already exists. Another directory service instance may be using it or it may be from a "
|
||||
+ " previous unclean shutdown. Please confirm and delete it or configure a different "
|
||||
+ "working directory");
|
||||
throw new IllegalArgumentException("The specified working directory '" + workingDir.getAbsolutePath()
|
||||
+ "' already exists. Another directory service instance may be using it or it may be from a "
|
||||
+ " previous unclean shutdown. Please confirm and delete it or configure a different "
|
||||
+ "working directory");
|
||||
}
|
||||
|
||||
this.workingDir = workingDir;
|
||||
@@ -197,7 +203,6 @@ public class ApacheDSContainer implements InitializingBean, DisposableBean, Life
|
||||
|
||||
/**
|
||||
* Returns the port that is resolved by {@link TcpTransport}.
|
||||
*
|
||||
* @return the port that is resolved by {@link TcpTransport}
|
||||
*/
|
||||
public int getLocalPort() {
|
||||
@@ -207,7 +212,6 @@ public class ApacheDSContainer implements InitializingBean, DisposableBean, Life
|
||||
/**
|
||||
* If set to {@code true} will enable LDAP over SSL (LDAPs). If set to {@code true}
|
||||
* {@link ApacheDSContainer#setCertificatePassord(String)} must be set as well.
|
||||
*
|
||||
* @param ldapOverSslEnabled If not set, will default to false
|
||||
*/
|
||||
public void setLdapOverSslEnabled(boolean ldapOverSslEnabled) {
|
||||
@@ -215,7 +219,8 @@ public class ApacheDSContainer implements InitializingBean, DisposableBean, Life
|
||||
}
|
||||
|
||||
/**
|
||||
* The keyStore must not be null and must be a valid file. Will set the keyStore file on the underlying {@link LdapServer}.
|
||||
* The keyStore must not be null and must be a valid file. Will set the keyStore file
|
||||
* on the underlying {@link LdapServer}.
|
||||
* @param keyStoreFile Mandatory if LDAPs is enabled
|
||||
*/
|
||||
public void setKeyStoreFile(File keyStoreFile) {
|
||||
@@ -226,7 +231,6 @@ public class ApacheDSContainer implements InitializingBean, DisposableBean, Life
|
||||
|
||||
/**
|
||||
* Will set the certificate password on the underlying {@link LdapServer}.
|
||||
*
|
||||
* @param certificatePassord May be null
|
||||
*/
|
||||
public void setCertificatePassord(String certificatePassord) {
|
||||
@@ -345,14 +349,13 @@ public class ApacheDSContainer implements InitializingBean, DisposableBean, Life
|
||||
ldifFile = ldifs[0].getURI().toString();
|
||||
}
|
||||
logger.info("Loading LDIF file: " + ldifFile);
|
||||
LdifFileLoader loader = new LdifFileLoader(service.getAdminSession(),
|
||||
new File(ldifFile), null, getClass().getClassLoader());
|
||||
LdifFileLoader loader = new LdifFileLoader(service.getAdminSession(), new File(ldifFile), null,
|
||||
getClass().getClassLoader());
|
||||
loader.execute();
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException(
|
||||
"More than one LDIF resource found with the supplied pattern:"
|
||||
+ ldifResources + " Got " + Arrays.toString(ldifs));
|
||||
throw new IllegalArgumentException("More than one LDIF resource found with the supplied pattern:"
|
||||
+ ldifResources + " Got " + Arrays.toString(ldifs));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -369,8 +372,8 @@ public class ApacheDSContainer implements InitializingBean, DisposableBean, Life
|
||||
fileName = fileNamePrefix + "~" + i;
|
||||
}
|
||||
|
||||
throw new IOException("Failed to create a temporary directory for file at "
|
||||
+ new File(parentTempDir, fileNamePrefix));
|
||||
throw new IOException(
|
||||
"Failed to create a temporary directory for file at " + new File(parentTempDir, fileNamePrefix));
|
||||
}
|
||||
|
||||
private boolean deleteDir(File dir) {
|
||||
@@ -390,4 +393,5 @@ public class ApacheDSContainer implements InitializingBean, DisposableBean, Life
|
||||
public boolean isRunning() {
|
||||
return running;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -37,8 +37,7 @@ import org.springframework.util.StringUtils;
|
||||
/**
|
||||
* @author Eddú Meléndez
|
||||
*/
|
||||
public class UnboundIdContainer implements InitializingBean, DisposableBean, Lifecycle,
|
||||
ApplicationContextAware {
|
||||
public class UnboundIdContainer implements InitializingBean, DisposableBean, Lifecycle, ApplicationContextAware {
|
||||
|
||||
private InMemoryDirectoryServer directoryServer;
|
||||
|
||||
@@ -106,7 +105,8 @@ public class UnboundIdContainer implements InitializingBean, DisposableBean, Lif
|
||||
this.port = directoryServer.getListenPort();
|
||||
this.directoryServer = directoryServer;
|
||||
this.running = true;
|
||||
} catch (LDAPException ex) {
|
||||
}
|
||||
catch (LDAPException ex) {
|
||||
throw new RuntimeException("Server startup failed", ex);
|
||||
}
|
||||
|
||||
@@ -124,7 +124,8 @@ public class UnboundIdContainer implements InitializingBean, DisposableBean, Lif
|
||||
directoryServer.importFromLDIF(false, new LDIFReader(inputStream));
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException("Unable to load LDIF " + this.ldif, ex);
|
||||
}
|
||||
}
|
||||
@@ -139,4 +140,5 @@ public class UnboundIdContainer implements InitializingBean, DisposableBean, Lif
|
||||
public boolean isRunning() {
|
||||
return this.running;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
/**
|
||||
* Embedded Apache Directory Server implementation, as used by the configuration namespace.
|
||||
* Embedded Apache Directory Server implementation, as used by the configuration
|
||||
* namespace.
|
||||
*/
|
||||
package org.springframework.security.ldap.server;
|
||||
|
||||
|
||||
@@ -99,11 +99,11 @@ import org.springframework.util.Assert;
|
||||
* @author Filip Hanik
|
||||
*/
|
||||
public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator {
|
||||
|
||||
// ~ Static fields/initializers
|
||||
// =====================================================================================
|
||||
|
||||
private static final Log logger = LogFactory
|
||||
.getLog(DefaultLdapAuthoritiesPopulator.class);
|
||||
private static final Log logger = LogFactory.getLog(DefaultLdapAuthoritiesPopulator.class);
|
||||
|
||||
// ~ Instance fields
|
||||
// ================================================================================================
|
||||
@@ -160,13 +160,11 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator
|
||||
/**
|
||||
* Constructor for group search scenarios. <tt>userRoleAttributes</tt> may still be
|
||||
* set as a property.
|
||||
*
|
||||
* @param contextSource supplies the contexts used to search for user roles.
|
||||
* @param groupSearchBase if this is an empty string the search will be performed from
|
||||
* the root DN of the context factory. If null, no search will be performed.
|
||||
*/
|
||||
public DefaultLdapAuthoritiesPopulator(ContextSource contextSource,
|
||||
String groupSearchBase) {
|
||||
public DefaultLdapAuthoritiesPopulator(ContextSource contextSource, String groupSearchBase) {
|
||||
Assert.notNull(contextSource, "contextSource must not be null");
|
||||
this.ldapTemplate = new SpringSecurityLdapTemplate(contextSource);
|
||||
getLdapTemplate().setSearchControls(getSearchControls());
|
||||
@@ -176,8 +174,7 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator
|
||||
logger.info("groupSearchBase is null. No group search will be performed.");
|
||||
}
|
||||
else if (groupSearchBase.length() == 0) {
|
||||
logger.info(
|
||||
"groupSearchBase is empty. Searches will be performed from the context source base");
|
||||
logger.info("groupSearchBase is empty. Searches will be performed from the context source base");
|
||||
}
|
||||
|
||||
this.authorityMapper = record -> {
|
||||
@@ -198,27 +195,23 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator
|
||||
* This method should be overridden if required to obtain any additional roles for the
|
||||
* given user (on top of those obtained from the standard search implemented by this
|
||||
* class).
|
||||
*
|
||||
* @param user the context representing the user who's roles are required
|
||||
* @return the extra roles which will be merged with those returned by the group
|
||||
* search
|
||||
*/
|
||||
|
||||
protected Set<GrantedAuthority> getAdditionalRoles(DirContextOperations user,
|
||||
String username) {
|
||||
protected Set<GrantedAuthority> getAdditionalRoles(DirContextOperations user, String username) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the authorities for the user who's directory entry is represented by the
|
||||
* supplied LdapUserDetails object.
|
||||
*
|
||||
* @param user the user who's authorities are required
|
||||
* @return the set of roles granted to the user.
|
||||
*/
|
||||
@Override
|
||||
public final Collection<GrantedAuthority> getGrantedAuthorities(
|
||||
DirContextOperations user, String username) {
|
||||
public final Collection<GrantedAuthority> getGrantedAuthorities(DirContextOperations user, String username) {
|
||||
String userDn = user.getNameInNamespace();
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
@@ -251,16 +244,13 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator
|
||||
Set<GrantedAuthority> authorities = new HashSet<>();
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Searching for roles for user '" + username + "', DN = " + "'"
|
||||
+ userDn + "', with filter " + this.groupSearchFilter
|
||||
+ " in search base '" + getGroupSearchBase() + "'");
|
||||
logger.debug("Searching for roles for user '" + username + "', DN = " + "'" + userDn + "', with filter "
|
||||
+ this.groupSearchFilter + " in search base '" + getGroupSearchBase() + "'");
|
||||
}
|
||||
|
||||
Set<Map<String, List<String>>> userRoles = getLdapTemplate()
|
||||
.searchForMultipleAttributeValues(getGroupSearchBase(),
|
||||
this.groupSearchFilter,
|
||||
new String[] { userDn, username },
|
||||
new String[] { this.groupRoleAttribute });
|
||||
Set<Map<String, List<String>>> userRoles = getLdapTemplate().searchForMultipleAttributeValues(
|
||||
getGroupSearchBase(), this.groupSearchFilter, new String[] { userDn, username },
|
||||
new String[] { this.groupRoleAttribute });
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Roles from search: " + userRoles);
|
||||
@@ -290,7 +280,6 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator
|
||||
|
||||
/**
|
||||
* The default role which will be assigned to all users.
|
||||
*
|
||||
* @param defaultRole the role name, including any desired prefix.
|
||||
*/
|
||||
public void setDefaultRole(String defaultRole) {
|
||||
@@ -320,13 +309,11 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator
|
||||
/**
|
||||
* If set to true, a subtree scope search will be performed. If false a single-level
|
||||
* search is used.
|
||||
*
|
||||
* @param searchSubtree set to true to enable searching of the entire tree below the
|
||||
* <tt>groupSearchBase</tt>.
|
||||
*/
|
||||
public void setSearchSubtree(boolean searchSubtree) {
|
||||
int searchScope = searchSubtree ? SearchControls.SUBTREE_SCOPE
|
||||
: SearchControls.ONELEVEL_SCOPE;
|
||||
int searchScope = searchSubtree ? SearchControls.SUBTREE_SCOPE : SearchControls.ONELEVEL_SCOPE;
|
||||
this.searchControls.setSearchScope(searchScope);
|
||||
}
|
||||
|
||||
@@ -341,9 +328,8 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the mapping function which will be used to create instances of {@link GrantedAuthority}
|
||||
* given the context record.
|
||||
*
|
||||
* Sets the mapping function which will be used to create instances of
|
||||
* {@link GrantedAuthority} given the context record.
|
||||
* @param authorityMapper the mapping function
|
||||
*/
|
||||
public void setAuthorityMapper(Function<Map<String, List<String>>, GrantedAuthority> authorityMapper) {
|
||||
@@ -419,4 +405,5 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator
|
||||
private SearchControls getSearchControls() {
|
||||
return this.searchControls;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -33,26 +33,43 @@ public class InetOrgPerson extends Person {
|
||||
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
|
||||
|
||||
private String carLicense;
|
||||
|
||||
// Person.cn
|
||||
private String destinationIndicator;
|
||||
|
||||
private String departmentNumber;
|
||||
|
||||
// Person.description
|
||||
private String displayName;
|
||||
|
||||
private String employeeNumber;
|
||||
|
||||
private String homePhone;
|
||||
|
||||
private String homePostalAddress;
|
||||
|
||||
private String initials;
|
||||
|
||||
private String mail;
|
||||
|
||||
private String mobile;
|
||||
|
||||
private String o;
|
||||
|
||||
private String ou;
|
||||
|
||||
private String postalAddress;
|
||||
|
||||
private String postalCode;
|
||||
|
||||
private String roomNumber;
|
||||
|
||||
private String street;
|
||||
|
||||
// Person.sn
|
||||
// Person.telephoneNumber
|
||||
private String title;
|
||||
|
||||
private String uid;
|
||||
|
||||
public String getUid() {
|
||||
@@ -146,11 +163,12 @@ public class InetOrgPerson extends Person {
|
||||
adapter.setAttributeValue("roomNumber", roomNumber);
|
||||
adapter.setAttributeValue("street", street);
|
||||
adapter.setAttributeValue("uid", uid);
|
||||
adapter.setAttributeValues("objectclass", new String[] { "top", "person",
|
||||
"organizationalPerson", "inetOrgPerson" });
|
||||
adapter.setAttributeValues("objectclass",
|
||||
new String[] { "top", "person", "organizationalPerson", "inetOrgPerson" });
|
||||
}
|
||||
|
||||
public static class Essence extends Person.Essence {
|
||||
|
||||
public Essence() {
|
||||
}
|
||||
|
||||
@@ -277,5 +295,7 @@ public class InetOrgPerson extends Person {
|
||||
public void setHomePostalAddress(String homePostalAddress) {
|
||||
((InetOrgPerson) instance).homePostalAddress = homePostalAddress;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -40,10 +40,10 @@ public class InetOrgPersonContextMapper implements UserDetailsContextMapper {
|
||||
}
|
||||
|
||||
public void mapUserToContext(UserDetails user, DirContextAdapter ctx) {
|
||||
Assert.isInstanceOf(InetOrgPerson.class, user,
|
||||
"UserDetails must be an InetOrgPerson instance");
|
||||
Assert.isInstanceOf(InetOrgPerson.class, user, "UserDetails must be an InetOrgPerson instance");
|
||||
|
||||
InetOrgPerson p = (InetOrgPerson) user;
|
||||
p.populateContext(ctx);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -32,17 +32,16 @@ import org.springframework.ldap.core.DirContextOperations;
|
||||
* @author Luke Taylor
|
||||
*/
|
||||
public interface LdapAuthoritiesPopulator {
|
||||
|
||||
// ~ Methods
|
||||
// ========================================================================================================
|
||||
|
||||
/**
|
||||
* Get the list of authorities for the user.
|
||||
*
|
||||
* @param userData the context object which was returned by the LDAP authenticator.
|
||||
*
|
||||
* @return the granted authorities for the given user.
|
||||
*
|
||||
*/
|
||||
Collection<? extends GrantedAuthority> getGrantedAuthorities(
|
||||
DirContextOperations userData, String username);
|
||||
Collection<? extends GrantedAuthority> getGrantedAuthorities(DirContextOperations userData, String username);
|
||||
|
||||
}
|
||||
|
||||
@@ -31,12 +31,13 @@ import java.util.Map;
|
||||
public class LdapAuthority implements GrantedAuthority {
|
||||
|
||||
private String dn;
|
||||
|
||||
private String role;
|
||||
|
||||
private Map<String, List<String>> attributes;
|
||||
|
||||
/**
|
||||
* Constructs an LdapAuthority that has a role and a DN but no other attributes
|
||||
*
|
||||
* @param role
|
||||
* @param dn
|
||||
*/
|
||||
@@ -46,7 +47,6 @@ public class LdapAuthority implements GrantedAuthority {
|
||||
|
||||
/**
|
||||
* Constructs an LdapAuthority with the given role, DN and other LDAP attributes
|
||||
*
|
||||
* @param role
|
||||
* @param dn
|
||||
* @param attributes
|
||||
@@ -62,7 +62,6 @@ public class LdapAuthority implements GrantedAuthority {
|
||||
|
||||
/**
|
||||
* Returns the LDAP attributes
|
||||
*
|
||||
* @return the LDAP attributes, map can be null
|
||||
*/
|
||||
public Map<String, List<String>> getAttributes() {
|
||||
@@ -71,7 +70,6 @@ public class LdapAuthority implements GrantedAuthority {
|
||||
|
||||
/**
|
||||
* Returns the DN for this LDAP authority
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getDn() {
|
||||
@@ -80,7 +78,6 @@ public class LdapAuthority implements GrantedAuthority {
|
||||
|
||||
/**
|
||||
* Returns the values for a specific attribute
|
||||
*
|
||||
* @param name the attribute name
|
||||
* @return a String array, never null but may be zero length
|
||||
*/
|
||||
@@ -97,7 +94,6 @@ public class LdapAuthority implements GrantedAuthority {
|
||||
|
||||
/**
|
||||
* Returns the first attribute value for a specified attribute
|
||||
*
|
||||
* @param name
|
||||
* @return the first attribute value for a specified attribute, may be null
|
||||
*/
|
||||
@@ -151,4 +147,5 @@ public class LdapAuthority implements GrantedAuthority {
|
||||
public String toString() {
|
||||
return "LdapAuthority{" + "dn='" + dn + '\'' + ", role='" + role + '\'' + '}';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -25,13 +25,14 @@ import org.springframework.security.core.userdetails.UserDetails;
|
||||
* @author Luke Taylor
|
||||
*/
|
||||
public interface LdapUserDetails extends UserDetails, CredentialsContainer {
|
||||
|
||||
// ~ Methods
|
||||
// ========================================================================================================
|
||||
|
||||
/**
|
||||
* The DN of the entry for this user's account.
|
||||
*
|
||||
* @return the user's DN
|
||||
*/
|
||||
String getDn();
|
||||
|
||||
}
|
||||
|
||||
@@ -55,15 +55,24 @@ public class LdapUserDetailsImpl implements LdapUserDetails, PasswordPolicyData
|
||||
// ================================================================================================
|
||||
|
||||
private String dn;
|
||||
|
||||
private String password;
|
||||
|
||||
private String username;
|
||||
|
||||
private Collection<GrantedAuthority> authorities = AuthorityUtils.NO_AUTHORITIES;
|
||||
|
||||
private boolean accountNonExpired = true;
|
||||
|
||||
private boolean accountNonLocked = true;
|
||||
|
||||
private boolean credentialsNonExpired = true;
|
||||
|
||||
private boolean enabled = true;
|
||||
|
||||
// PPolicy data
|
||||
private int timeBeforeExpiration = Integer.MAX_VALUE;
|
||||
|
||||
private int graceLoginsRemaining = Integer.MAX_VALUE;
|
||||
|
||||
// ~ Constructors
|
||||
@@ -152,8 +161,7 @@ public class LdapUserDetailsImpl implements LdapUserDetails, PasswordPolicyData
|
||||
sb.append("Password: [PROTECTED]; ");
|
||||
sb.append("Enabled: ").append(this.enabled).append("; ");
|
||||
sb.append("AccountNonExpired: ").append(this.accountNonExpired).append("; ");
|
||||
sb.append("CredentialsNonExpired: ").append(this.credentialsNonExpired)
|
||||
.append("; ");
|
||||
sb.append("CredentialsNonExpired: ").append(this.credentialsNonExpired).append("; ");
|
||||
sb.append("AccountNonLocked: ").append(this.accountNonLocked).append("; ");
|
||||
|
||||
if (this.getAuthorities() != null && !this.getAuthorities().isEmpty()) {
|
||||
@@ -185,7 +193,9 @@ public class LdapUserDetailsImpl implements LdapUserDetails, PasswordPolicyData
|
||||
* Variation of essence pattern. Used to create mutable intermediate object
|
||||
*/
|
||||
public static class Essence {
|
||||
|
||||
protected LdapUserDetailsImpl instance = createTarget();
|
||||
|
||||
private List<GrantedAuthority> mutableAuthorities = new ArrayList<>();
|
||||
|
||||
public Essence() {
|
||||
@@ -230,8 +240,7 @@ public class LdapUserDetailsImpl implements LdapUserDetails, PasswordPolicyData
|
||||
}
|
||||
|
||||
public LdapUserDetails createUserDetails() {
|
||||
Assert.notNull(instance,
|
||||
"Essence can only be used to create a single instance");
|
||||
Assert.notNull(instance, "Essence can only be used to create a single instance");
|
||||
Assert.notNull(instance.username, "username must not be null");
|
||||
Assert.notNull(instance.getDn(), "Distinguished name must not be null");
|
||||
|
||||
@@ -292,5 +301,7 @@ public class LdapUserDetailsImpl implements LdapUserDetails, PasswordPolicyData
|
||||
public void setGraceLoginsRemaining(int graceLoginsRemaining) {
|
||||
instance.graceLoginsRemaining = graceLoginsRemaining;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -76,14 +76,14 @@ import org.springframework.util.Assert;
|
||||
* @since 2.0
|
||||
*/
|
||||
public class LdapUserDetailsManager implements UserDetailsManager {
|
||||
|
||||
private final Log logger = LogFactory.getLog(LdapUserDetailsManager.class);
|
||||
|
||||
/**
|
||||
* The strategy for mapping usernames to LDAP distinguished names. This will be used
|
||||
* when building DNs for creating new users etc.
|
||||
*/
|
||||
LdapUsernameToDnMapper usernameMapper = new DefaultLdapUsernameToDnMapper("cn=users",
|
||||
"uid");
|
||||
LdapUsernameToDnMapper usernameMapper = new DefaultLdapUsernameToDnMapper("cn=users", "uid");
|
||||
|
||||
/** The DN under which groups are stored */
|
||||
private DistinguishedName groupSearchBase = new DistinguishedName("cn=groups");
|
||||
@@ -93,6 +93,7 @@ public class LdapUserDetailsManager implements UserDetailsManager {
|
||||
|
||||
/** The attribute which corresponds to the role name of a group. */
|
||||
private String groupRoleAttributeName = "cn";
|
||||
|
||||
/** The attribute which contains members of a group */
|
||||
private String groupMemberAttributeName = "uniquemember";
|
||||
|
||||
@@ -100,6 +101,7 @@ public class LdapUserDetailsManager implements UserDetailsManager {
|
||||
|
||||
/** The pattern to be used for the user search. {0} is the user's DN */
|
||||
private String groupSearchFilter = "(uniquemember={0})";
|
||||
|
||||
/**
|
||||
* The strategy used to create a UserDetails object from the LDAP context, username
|
||||
* and list of authorities. This should be set to match the required UserDetails
|
||||
@@ -140,16 +142,14 @@ public class LdapUserDetailsManager implements UserDetailsManager {
|
||||
return userDetailsMapper.mapUserFromContext(userCtx, username, authorities);
|
||||
}
|
||||
|
||||
private DirContextAdapter loadUserAsContext(final DistinguishedName dn,
|
||||
final String username) {
|
||||
private DirContextAdapter loadUserAsContext(final DistinguishedName dn, final String username) {
|
||||
return (DirContextAdapter) template.executeReadOnly((ContextExecutor) ctx -> {
|
||||
try {
|
||||
Attributes attrs = ctx.getAttributes(dn, attributesToRetrieve);
|
||||
return new DirContextAdapter(attrs, LdapUtils.getFullDn(dn, ctx));
|
||||
}
|
||||
catch (NameNotFoundException notFound) {
|
||||
throw new UsernameNotFoundException(
|
||||
"User " + username + " not found", notFound);
|
||||
throw new UsernameNotFoundException("User " + username + " not found", notFound);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -163,28 +163,25 @@ public class LdapUserDetailsManager implements UserDetailsManager {
|
||||
*
|
||||
* <p>
|
||||
* Configured one way, this method will modify the user's password via the
|
||||
* <a target="_blank" href="https://tools.ietf.org/html/rfc3062">
|
||||
* LDAP Password Modify Extended Operation
|
||||
* </a>.
|
||||
* <a target="_blank" href="https://tools.ietf.org/html/rfc3062"> LDAP Password Modify
|
||||
* Extended Operation </a>.
|
||||
*
|
||||
* See {@link LdapUserDetailsManager#setUsePasswordModifyExtensionOperation(boolean)} for details.
|
||||
* See {@link LdapUserDetailsManager#setUsePasswordModifyExtensionOperation(boolean)}
|
||||
* for details.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* By default, though, if the old password is supplied, the update will be made by rebinding as the user,
|
||||
* thus modifying the password using the user's permissions. If
|
||||
* By default, though, if the old password is supplied, the update will be made by
|
||||
* rebinding as the user, thus modifying the password using the user's permissions. If
|
||||
* <code>oldPassword</code> is null, the update will be attempted using a standard
|
||||
* read/write context supplied by the context source.
|
||||
* </p>
|
||||
*
|
||||
* @param oldPassword the old password
|
||||
* @param newPassword the new value of the password.
|
||||
*/
|
||||
public void changePassword(final String oldPassword, final String newPassword) {
|
||||
Authentication authentication = SecurityContextHolder.getContext()
|
||||
.getAuthentication();
|
||||
Assert.notNull(
|
||||
authentication,
|
||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||
Assert.notNull(authentication,
|
||||
"No authentication object found in security context. Can't change current user's password!");
|
||||
|
||||
String username = authentication.getName();
|
||||
@@ -195,32 +192,29 @@ public class LdapUserDetailsManager implements UserDetailsManager {
|
||||
|
||||
if (usePasswordModifyExtensionOperation) {
|
||||
changePasswordUsingExtensionOperation(userDn, oldPassword, newPassword);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
changePasswordUsingAttributeModification(userDn, oldPassword, newPassword);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param dn the distinguished name of the entry - may be either relative to the base
|
||||
* context or a complete DN including the name of the context (either is supported).
|
||||
* @param username the user whose roles are required.
|
||||
* @return the granted authorities returned by the group search
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
List<GrantedAuthority> getUserAuthorities(final DistinguishedName dn,
|
||||
final String username) {
|
||||
List<GrantedAuthority> getUserAuthorities(final DistinguishedName dn, final String username) {
|
||||
SearchExecutor se = ctx -> {
|
||||
DistinguishedName fullDn = LdapUtils.getFullDn(dn, ctx);
|
||||
SearchControls ctrls = new SearchControls();
|
||||
ctrls.setReturningAttributes(new String[] { groupRoleAttributeName });
|
||||
|
||||
return ctx.search(groupSearchBase, groupSearchFilter, new String[] {
|
||||
fullDn.toUrl(), username }, ctrls);
|
||||
return ctx.search(groupSearchBase, groupSearchFilter, new String[] { fullDn.toUrl(), username }, ctrls);
|
||||
};
|
||||
|
||||
AttributesMapperCallbackHandler roleCollector = new AttributesMapperCallbackHandler(
|
||||
roleMapper);
|
||||
AttributesMapperCallbackHandler roleCollector = new AttributesMapperCallbackHandler(roleMapper);
|
||||
|
||||
template.search(se, roleCollector);
|
||||
return roleCollector.getList();
|
||||
@@ -231,8 +225,7 @@ public class LdapUserDetailsManager implements UserDetailsManager {
|
||||
copyToContext(user, ctx);
|
||||
DistinguishedName dn = usernameMapper.buildDn(user.getUsername());
|
||||
|
||||
logger.debug("Creating new user '" + user.getUsername() + "' with DN '" + dn
|
||||
+ "'");
|
||||
logger.debug("Creating new user '" + user.getUsername() + "' with DN '" + dn + "'");
|
||||
|
||||
template.bind(dn, ctx, null);
|
||||
|
||||
@@ -259,8 +252,7 @@ public class LdapUserDetailsManager implements UserDetailsManager {
|
||||
copyToContext(user, ctx);
|
||||
|
||||
// Remove the objectclass attribute from the list of mods (if present).
|
||||
List<ModificationItem> mods = new LinkedList<>(Arrays.asList(ctx
|
||||
.getModificationItems()));
|
||||
List<ModificationItem> mods = new LinkedList<>(Arrays.asList(ctx.getModificationItems()));
|
||||
ListIterator<ModificationItem> modIt = mods.listIterator();
|
||||
|
||||
while (modIt.hasNext()) {
|
||||
@@ -302,7 +294,6 @@ public class LdapUserDetailsManager implements UserDetailsManager {
|
||||
|
||||
/**
|
||||
* Creates a DN from a group name.
|
||||
*
|
||||
* @param group the name of the group
|
||||
* @return the DN of the corresponding group, including the groupSearchBase
|
||||
*/
|
||||
@@ -317,13 +308,11 @@ public class LdapUserDetailsManager implements UserDetailsManager {
|
||||
userDetailsMapper.mapUserToContext(user, ctx);
|
||||
}
|
||||
|
||||
protected void addAuthorities(DistinguishedName userDn,
|
||||
Collection<? extends GrantedAuthority> authorities) {
|
||||
protected void addAuthorities(DistinguishedName userDn, Collection<? extends GrantedAuthority> authorities) {
|
||||
modifyAuthorities(userDn, authorities, DirContext.ADD_ATTRIBUTE);
|
||||
}
|
||||
|
||||
protected void removeAuthorities(DistinguishedName userDn,
|
||||
Collection<? extends GrantedAuthority> authorities) {
|
||||
protected void removeAuthorities(DistinguishedName userDn, Collection<? extends GrantedAuthority> authorities) {
|
||||
modifyAuthorities(userDn, authorities, DirContext.REMOVE_ATTRIBUTE);
|
||||
}
|
||||
|
||||
@@ -336,8 +325,7 @@ public class LdapUserDetailsManager implements UserDetailsManager {
|
||||
ModificationItem addGroup = new ModificationItem(modType,
|
||||
new BasicAttribute(groupMemberAttributeName, fullDn.toUrl()));
|
||||
|
||||
ctx.modifyAttributes(buildGroupDn(group),
|
||||
new ModificationItem[] { addGroup });
|
||||
ctx.modifyAttributes(buildGroupDn(group), new ModificationItem[] { addGroup });
|
||||
}
|
||||
return null;
|
||||
});
|
||||
@@ -384,7 +372,6 @@ public class LdapUserDetailsManager implements UserDetailsManager {
|
||||
* <p>
|
||||
* Usually this will be <tt>uniquemember</tt> (the default value) or <tt>member</tt>.
|
||||
* </p>
|
||||
*
|
||||
* @param groupMemberAttributeName the name of the attribute used to store group
|
||||
* members.
|
||||
*/
|
||||
@@ -401,17 +388,19 @@ public class LdapUserDetailsManager implements UserDetailsManager {
|
||||
/**
|
||||
* Sets the method by which a user's password gets modified.
|
||||
*
|
||||
* If set to {@code true}, then {@link LdapUserDetailsManager#changePassword} will modify
|
||||
* the user's password by way of the
|
||||
* <a target="_blank" href="https://tools.ietf.org/html/rfc3062">Password Modify Extension Operation</a>.
|
||||
* If set to {@code true}, then {@link LdapUserDetailsManager#changePassword} will
|
||||
* modify the user's password by way of the
|
||||
* <a target="_blank" href="https://tools.ietf.org/html/rfc3062">Password Modify
|
||||
* Extension Operation</a>.
|
||||
*
|
||||
* If set to {@code false}, then {@link LdapUserDetailsManager#changePassword} will modify
|
||||
* the user's password by directly modifying attributes on the corresponding entry.
|
||||
* If set to {@code false}, then {@link LdapUserDetailsManager#changePassword} will
|
||||
* modify the user's password by directly modifying attributes on the corresponding
|
||||
* entry.
|
||||
*
|
||||
* Before using this setting, ensure that the corresponding LDAP server supports this extended operation.
|
||||
* Before using this setting, ensure that the corresponding LDAP server supports this
|
||||
* extended operation.
|
||||
*
|
||||
* By default, {@code usePasswordModifyExtensionOperation} is false.
|
||||
*
|
||||
* @param usePasswordModifyExtensionOperation
|
||||
* @since 4.2.9
|
||||
*/
|
||||
@@ -419,12 +408,11 @@ public class LdapUserDetailsManager implements UserDetailsManager {
|
||||
this.usePasswordModifyExtensionOperation = usePasswordModifyExtensionOperation;
|
||||
}
|
||||
|
||||
private void changePasswordUsingAttributeModification
|
||||
(DistinguishedName userDn, String oldPassword, String newPassword) {
|
||||
private void changePasswordUsingAttributeModification(DistinguishedName userDn, String oldPassword,
|
||||
String newPassword) {
|
||||
|
||||
final ModificationItem[] passwordChange = new ModificationItem[] { new ModificationItem(
|
||||
DirContext.REPLACE_ATTRIBUTE, new BasicAttribute(passwordAttributeName,
|
||||
newPassword)) };
|
||||
DirContext.REPLACE_ATTRIBUTE, new BasicAttribute(passwordAttributeName, newPassword)) };
|
||||
|
||||
if (oldPassword == null) {
|
||||
template.modifyAttributes(userDn, passwordChange);
|
||||
@@ -434,15 +422,14 @@ public class LdapUserDetailsManager implements UserDetailsManager {
|
||||
template.executeReadWrite(dirCtx -> {
|
||||
LdapContext ctx = (LdapContext) dirCtx;
|
||||
ctx.removeFromEnvironment("com.sun.jndi.ldap.connect.pool");
|
||||
ctx.addToEnvironment(Context.SECURITY_PRINCIPAL,
|
||||
LdapUtils.getFullDn(userDn, ctx).toString());
|
||||
ctx.addToEnvironment(Context.SECURITY_PRINCIPAL, LdapUtils.getFullDn(userDn, ctx).toString());
|
||||
ctx.addToEnvironment(Context.SECURITY_CREDENTIALS, oldPassword);
|
||||
// TODO: reconnect doesn't appear to actually change the credentials
|
||||
try {
|
||||
ctx.reconnect(null);
|
||||
} catch (javax.naming.AuthenticationException e) {
|
||||
throw new BadCredentialsException(
|
||||
"Authentication for password change failed.");
|
||||
}
|
||||
catch (javax.naming.AuthenticationException e) {
|
||||
throw new BadCredentialsException("Authentication for password change failed.");
|
||||
}
|
||||
|
||||
ctx.modifyAttributes(userDn, passwordChange);
|
||||
@@ -452,43 +439,45 @@ public class LdapUserDetailsManager implements UserDetailsManager {
|
||||
|
||||
}
|
||||
|
||||
private void changePasswordUsingExtensionOperation
|
||||
(DistinguishedName userDn, String oldPassword, String newPassword) {
|
||||
private void changePasswordUsingExtensionOperation(DistinguishedName userDn, String oldPassword,
|
||||
String newPassword) {
|
||||
|
||||
template.executeReadWrite(dirCtx -> {
|
||||
LdapContext ctx = (LdapContext) dirCtx;
|
||||
|
||||
String userIdentity = LdapUtils.getFullDn(userDn, ctx).encode();
|
||||
PasswordModifyRequest request =
|
||||
new PasswordModifyRequest(userIdentity, oldPassword, newPassword);
|
||||
PasswordModifyRequest request = new PasswordModifyRequest(userIdentity, oldPassword, newPassword);
|
||||
|
||||
try {
|
||||
return ctx.extendedOperation(request);
|
||||
} catch (javax.naming.AuthenticationException e) {
|
||||
throw new BadCredentialsException(
|
||||
"Authentication for password change failed.");
|
||||
}
|
||||
catch (javax.naming.AuthenticationException e) {
|
||||
throw new BadCredentialsException("Authentication for password change failed.");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* An implementation of the
|
||||
* <a target="_blank" href="https://tools.ietf.org/html/rfc3062">
|
||||
* LDAP Password Modify Extended Operation
|
||||
* </a>
|
||||
* client request.
|
||||
* <a target="_blank" href="https://tools.ietf.org/html/rfc3062"> LDAP Password Modify
|
||||
* Extended Operation </a> client request.
|
||||
*
|
||||
* Can be directed at any LDAP server that supports the Password Modify Extended Operation.
|
||||
* Can be directed at any LDAP server that supports the Password Modify Extended
|
||||
* Operation.
|
||||
*
|
||||
* @author Josh Cummings
|
||||
* @since 4.2.9
|
||||
*/
|
||||
private static class PasswordModifyRequest implements ExtendedRequest {
|
||||
|
||||
private static final byte SEQUENCE_TYPE = 48;
|
||||
|
||||
private static final String PASSWORD_MODIFY_OID = "1.3.6.1.4.1.4203.1.11.1";
|
||||
|
||||
private static final byte USER_IDENTITY_OCTET_TYPE = -128;
|
||||
|
||||
private static final byte OLD_PASSWORD_OCTET_TYPE = -127;
|
||||
|
||||
private static final byte NEW_PASSWORD_OCTET_TYPE = -126;
|
||||
|
||||
private final ByteArrayOutputStream value = new ByteArrayOutputStream();
|
||||
@@ -527,10 +516,9 @@ public class LdapUserDetailsManager implements UserDetailsManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Only minimal support for
|
||||
* <a target="_blank" href="https://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf">
|
||||
* BER encoding
|
||||
* </a>; just what is necessary for the Password Modify request.
|
||||
* Only minimal support for <a target="_blank" href=
|
||||
* "https://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf"> BER
|
||||
* encoding </a>; just what is necessary for the Password Modify request.
|
||||
*
|
||||
*/
|
||||
private void berEncode(byte type, byte[] src, ByteArrayOutputStream dest) {
|
||||
@@ -540,19 +528,23 @@ public class LdapUserDetailsManager implements UserDetailsManager {
|
||||
|
||||
if (length < 128) {
|
||||
dest.write(length);
|
||||
} else if ((length & 0x0000_00FF) == length) {
|
||||
}
|
||||
else if ((length & 0x0000_00FF) == length) {
|
||||
dest.write((byte) 0x81);
|
||||
dest.write((byte) (length & 0xFF));
|
||||
} else if ((length & 0x0000_FFFF) == length) {
|
||||
}
|
||||
else if ((length & 0x0000_FFFF) == length) {
|
||||
dest.write((byte) 0x82);
|
||||
dest.write((byte) ((length >> 8) & 0xFF));
|
||||
dest.write((byte) (length & 0xFF));
|
||||
} else if ((length & 0x00FF_FFFF) == length) {
|
||||
}
|
||||
else if ((length & 0x00FF_FFFF) == length) {
|
||||
dest.write((byte) 0x83);
|
||||
dest.write((byte) ((length >> 16) & 0xFF));
|
||||
dest.write((byte) ((length >> 8) & 0xFF));
|
||||
dest.write((byte) (length & 0xFF));
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
dest.write((byte) 0x84);
|
||||
dest.write((byte) ((length >> 24) & 0xFF));
|
||||
dest.write((byte) ((length >> 16) & 0xFF));
|
||||
@@ -562,9 +554,12 @@ public class LdapUserDetailsManager implements UserDetailsManager {
|
||||
|
||||
try {
|
||||
dest.write(src);
|
||||
} catch (IOException e) {
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new IllegalArgumentException("Failed to BER encode provided value of type: " + type);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -38,13 +38,18 @@ import org.springframework.util.Assert;
|
||||
* @author Eddú Meléndez
|
||||
*/
|
||||
public class LdapUserDetailsMapper implements UserDetailsContextMapper {
|
||||
|
||||
// ~ Instance fields
|
||||
// ================================================================================================
|
||||
|
||||
private final Log logger = LogFactory.getLog(LdapUserDetailsMapper.class);
|
||||
|
||||
private String passwordAttributeName = "userPassword";
|
||||
|
||||
private String rolePrefix = "ROLE_";
|
||||
|
||||
private String[] roleAttributes = null;
|
||||
|
||||
private boolean convertToUpperCase = true;
|
||||
|
||||
// ~ Methods
|
||||
@@ -69,13 +74,11 @@ public class LdapUserDetailsMapper implements UserDetailsContextMapper {
|
||||
essence.setUsername(username);
|
||||
|
||||
// Map the roles
|
||||
for (int i = 0; (this.roleAttributes != null)
|
||||
&& (i < this.roleAttributes.length); i++) {
|
||||
for (int i = 0; (this.roleAttributes != null) && (i < this.roleAttributes.length); i++) {
|
||||
String[] rolesForAttribute = ctx.getStringAttributes(this.roleAttributes[i]);
|
||||
|
||||
if (rolesForAttribute == null) {
|
||||
this.logger.debug("Couldn't read role attribute '"
|
||||
+ this.roleAttributes[i] + "' for user " + dn);
|
||||
this.logger.debug("Couldn't read role attribute '" + this.roleAttributes[i] + "' for user " + dn);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -110,15 +113,13 @@ public class LdapUserDetailsMapper implements UserDetailsContextMapper {
|
||||
|
||||
@Override
|
||||
public void mapUserToContext(UserDetails user, DirContextAdapter ctx) {
|
||||
throw new UnsupportedOperationException(
|
||||
"LdapUserDetailsMapper only supports reading from a context. Please"
|
||||
+ " use a subclass if mapUserToContext() is required.");
|
||||
throw new UnsupportedOperationException("LdapUserDetailsMapper only supports reading from a context. Please"
|
||||
+ " use a subclass if mapUserToContext() is required.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Extension point to allow customized creation of the user's password from the
|
||||
* attribute stored in the directory.
|
||||
*
|
||||
* @param passwordValue the value of the password attribute
|
||||
* @return a String representation of the password.
|
||||
*/
|
||||
@@ -141,7 +142,6 @@ public class LdapUserDetailsMapper implements UserDetailsContextMapper {
|
||||
* <tt>rolePrefix</tt> and <tt>convertToUpperCase</tt> properties. Non-String
|
||||
* attributes are ignored.
|
||||
* </p>
|
||||
*
|
||||
* @param role the attribute returned from
|
||||
* @return the authority to be added to the list of authorities for the user, or null
|
||||
* if this attribute should be ignored.
|
||||
@@ -159,7 +159,6 @@ public class LdapUserDetailsMapper implements UserDetailsContextMapper {
|
||||
/**
|
||||
* Determines whether role field values will be converted to upper case when loaded.
|
||||
* The default is true.
|
||||
*
|
||||
* @param convertToUpperCase true if the roles should be converted to upper case.
|
||||
*/
|
||||
public void setConvertToUpperCase(boolean convertToUpperCase) {
|
||||
@@ -169,7 +168,6 @@ public class LdapUserDetailsMapper implements UserDetailsContextMapper {
|
||||
/**
|
||||
* The name of the attribute which contains the user's password. Defaults to
|
||||
* "userPassword".
|
||||
*
|
||||
* @param passwordAttributeName the name of the attribute
|
||||
*/
|
||||
public void setPasswordAttributeName(String passwordAttributeName) {
|
||||
@@ -180,7 +178,6 @@ public class LdapUserDetailsMapper implements UserDetailsContextMapper {
|
||||
* The names of any attributes in the user's entry which represent application roles.
|
||||
* These will be converted to <tt>GrantedAuthority</tt>s and added to the list in the
|
||||
* returned LdapUserDetails object. The attribute values must be Strings by default.
|
||||
*
|
||||
* @param roleAttributes the names of the role attributes.
|
||||
*/
|
||||
public void setRoleAttributes(String[] roleAttributes) {
|
||||
@@ -195,4 +192,5 @@ public class LdapUserDetailsMapper implements UserDetailsContextMapper {
|
||||
public void setRolePrefix(String rolePrefix) {
|
||||
this.rolePrefix = rolePrefix;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -35,24 +35,25 @@ import org.springframework.util.Assert;
|
||||
* @author Luke Taylor
|
||||
*/
|
||||
public class LdapUserDetailsService implements UserDetailsService {
|
||||
|
||||
private final LdapUserSearch userSearch;
|
||||
|
||||
private final LdapAuthoritiesPopulator authoritiesPopulator;
|
||||
|
||||
private UserDetailsContextMapper userDetailsMapper = new LdapUserDetailsMapper();
|
||||
|
||||
public LdapUserDetailsService(LdapUserSearch userSearch) {
|
||||
this(userSearch, new NullLdapAuthoritiesPopulator());
|
||||
}
|
||||
|
||||
public LdapUserDetailsService(LdapUserSearch userSearch,
|
||||
LdapAuthoritiesPopulator authoritiesPopulator) {
|
||||
public LdapUserDetailsService(LdapUserSearch userSearch, LdapAuthoritiesPopulator authoritiesPopulator) {
|
||||
Assert.notNull(userSearch, "userSearch must not be null");
|
||||
Assert.notNull(authoritiesPopulator, "authoritiesPopulator must not be null");
|
||||
this.userSearch = userSearch;
|
||||
this.authoritiesPopulator = authoritiesPopulator;
|
||||
}
|
||||
|
||||
public UserDetails loadUserByUsername(String username)
|
||||
throws UsernameNotFoundException {
|
||||
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||
DirContextOperations userData = userSearch.searchForUser(username);
|
||||
|
||||
return userDetailsMapper.mapUserFromContext(userData, username,
|
||||
@@ -64,11 +65,12 @@ public class LdapUserDetailsService implements UserDetailsService {
|
||||
this.userDetailsMapper = userDetailsMapper;
|
||||
}
|
||||
|
||||
private static final class NullLdapAuthoritiesPopulator implements
|
||||
LdapAuthoritiesPopulator {
|
||||
public Collection<GrantedAuthority> getGrantedAuthorities(
|
||||
DirContextOperations userDetails, String username) {
|
||||
private static final class NullLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator {
|
||||
|
||||
public Collection<GrantedAuthority> getGrantedAuthorities(DirContextOperations userDetails, String username) {
|
||||
return AuthorityUtils.NO_AUTHORITIES;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -119,8 +119,8 @@ import org.springframework.util.StringUtils;
|
||||
*/
|
||||
|
||||
public class NestedLdapAuthoritiesPopulator extends DefaultLdapAuthoritiesPopulator {
|
||||
private static final Log logger = LogFactory
|
||||
.getLog(NestedLdapAuthoritiesPopulator.class);
|
||||
|
||||
private static final Log logger = LogFactory.getLog(NestedLdapAuthoritiesPopulator.class);
|
||||
|
||||
/**
|
||||
* The attribute names to retrieve for each LDAP group
|
||||
@@ -135,13 +135,11 @@ public class NestedLdapAuthoritiesPopulator extends DefaultLdapAuthoritiesPopula
|
||||
/**
|
||||
* Constructor for group search scenarios. <tt>userRoleAttributes</tt> may still be
|
||||
* set as a property.
|
||||
*
|
||||
* @param contextSource supplies the contexts used to search for user roles.
|
||||
* @param groupSearchBase if this is an empty string the search will be performed from
|
||||
* the root DN of the
|
||||
*/
|
||||
public NestedLdapAuthoritiesPopulator(ContextSource contextSource,
|
||||
String groupSearchBase) {
|
||||
public NestedLdapAuthoritiesPopulator(ContextSource contextSource, String groupSearchBase) {
|
||||
super(contextSource, groupSearchBase);
|
||||
}
|
||||
|
||||
@@ -163,45 +161,38 @@ public class NestedLdapAuthoritiesPopulator extends DefaultLdapAuthoritiesPopula
|
||||
|
||||
/**
|
||||
* Performs the nested group search
|
||||
*
|
||||
* @param userDn - the userDN to search for, will become the group DN for subsequent
|
||||
* searches
|
||||
* @param username - the username of the user
|
||||
* @param authorities - the authorities set that will be populated, must not be null
|
||||
* @param depth - the depth remaining, when 0 recursion will end
|
||||
*/
|
||||
private void performNestedSearch(String userDn, String username,
|
||||
Set<GrantedAuthority> authorities, int depth) {
|
||||
private void performNestedSearch(String userDn, String username, Set<GrantedAuthority> authorities, int depth) {
|
||||
if (depth == 0) {
|
||||
// back out of recursion
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Search aborted, max depth reached,"
|
||||
+ " for roles for user '" + username + "', DN = " + "'" + userDn
|
||||
+ "', with filter " + getGroupSearchFilter() + " in search base '"
|
||||
logger.debug("Search aborted, max depth reached," + " for roles for user '" + username + "', DN = "
|
||||
+ "'" + userDn + "', with filter " + getGroupSearchFilter() + " in search base '"
|
||||
+ getGroupSearchBase() + "'");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Searching for roles for user '" + username + "', DN = " + "'"
|
||||
+ userDn + "', with filter " + getGroupSearchFilter()
|
||||
+ " in search base '" + getGroupSearchBase() + "'");
|
||||
logger.debug("Searching for roles for user '" + username + "', DN = " + "'" + userDn + "', with filter "
|
||||
+ getGroupSearchFilter() + " in search base '" + getGroupSearchBase() + "'");
|
||||
}
|
||||
|
||||
if (getAttributeNames() == null) {
|
||||
setAttributeNames(new HashSet<>());
|
||||
}
|
||||
if (StringUtils.hasText(getGroupRoleAttribute())
|
||||
&& !getAttributeNames().contains(getGroupRoleAttribute())) {
|
||||
if (StringUtils.hasText(getGroupRoleAttribute()) && !getAttributeNames().contains(getGroupRoleAttribute())) {
|
||||
getAttributeNames().add(getGroupRoleAttribute());
|
||||
}
|
||||
|
||||
Set<Map<String, List<String>>> userRoles = getLdapTemplate()
|
||||
.searchForMultipleAttributeValues(getGroupSearchBase(),
|
||||
getGroupSearchFilter(), new String[] { userDn, username },
|
||||
getAttributeNames()
|
||||
.toArray(new String[0]));
|
||||
Set<Map<String, List<String>>> userRoles = getLdapTemplate().searchForMultipleAttributeValues(
|
||||
getGroupSearchBase(), getGroupSearchFilter(), new String[] { userDn, username },
|
||||
getAttributeNames().toArray(new String[0]));
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Roles from search: " + userRoles);
|
||||
@@ -222,8 +213,7 @@ public class NestedLdapAuthoritiesPopulator extends DefaultLdapAuthoritiesPopula
|
||||
role = getRolePrefix() + role;
|
||||
// if the group already exist, we will not search for it's parents again.
|
||||
// this prevents a forever loop for a misconfigured ldap directory
|
||||
circular = circular
|
||||
| (!authorities.add(new LdapAuthority(role, dn, record)));
|
||||
circular = circular | (!authorities.add(new LdapAuthority(role, dn, record)));
|
||||
}
|
||||
String roleName = roles.size() > 0 ? roles.iterator().next() : dn;
|
||||
if (!circular) {
|
||||
@@ -236,7 +226,6 @@ public class NestedLdapAuthoritiesPopulator extends DefaultLdapAuthoritiesPopula
|
||||
/**
|
||||
* Returns the attribute names that this populator has been configured to retrieve
|
||||
* Value can be null, represents fetch all attributes
|
||||
*
|
||||
* @return the attribute names or null for all
|
||||
*/
|
||||
private Set<String> getAttributeNames() {
|
||||
@@ -245,7 +234,6 @@ public class NestedLdapAuthoritiesPopulator extends DefaultLdapAuthoritiesPopula
|
||||
|
||||
/**
|
||||
* Sets the attribute names to retrieve for each ldap groups. Null means retrieve all
|
||||
*
|
||||
* @param attributeNames - the names of the LDAP attributes to retrieve
|
||||
*/
|
||||
public void setAttributeNames(Set<String> attributeNames) {
|
||||
@@ -255,7 +243,6 @@ public class NestedLdapAuthoritiesPopulator extends DefaultLdapAuthoritiesPopula
|
||||
/**
|
||||
* How far should a nested search go. Depth is calculated in the number of levels we
|
||||
* search up for parent groups.
|
||||
*
|
||||
* @return the max search depth, default is 10
|
||||
*/
|
||||
private int getMaxSearchDepth() {
|
||||
@@ -265,7 +252,6 @@ public class NestedLdapAuthoritiesPopulator extends DefaultLdapAuthoritiesPopula
|
||||
/**
|
||||
* How far should a nested search go. Depth is calculated in the number of levels we
|
||||
* search up for parent groups.
|
||||
*
|
||||
* @param maxSearchDepth the max search depth
|
||||
*/
|
||||
public void setMaxSearchDepth(int maxSearchDepth) {
|
||||
|
||||
@@ -39,9 +39,13 @@ public class Person extends LdapUserDetailsImpl {
|
||||
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
|
||||
|
||||
private String givenName;
|
||||
|
||||
private String sn;
|
||||
|
||||
private String description;
|
||||
|
||||
private String telephoneNumber;
|
||||
|
||||
private List<String> cn = new ArrayList<>();
|
||||
|
||||
protected Person() {
|
||||
@@ -144,5 +148,7 @@ public class Person extends LdapUserDetailsImpl {
|
||||
// TODO: Check contents for null entries
|
||||
return p;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -45,4 +45,5 @@ public class PersonContextMapper implements UserDetailsContextMapper {
|
||||
Person p = (Person) user;
|
||||
p.populateContext(ctx);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -36,7 +36,6 @@ public interface UserDetailsContextMapper {
|
||||
|
||||
/**
|
||||
* Creates a fully populated UserDetails object for use by the security framework.
|
||||
*
|
||||
* @param ctx the context object which contains the user information.
|
||||
* @param username the user's supplied login name.
|
||||
* @param authorities
|
||||
@@ -50,4 +49,5 @@ public interface UserDetailsContextMapper {
|
||||
* object. Called when saving a user, for example.
|
||||
*/
|
||||
void mapUserToContext(UserDetails user, DirContextAdapter ctx);
|
||||
|
||||
}
|
||||
|
||||
@@ -18,4 +18,3 @@
|
||||
* contained in some of the standard LDAP types (such as {@code InetOrgPerson}).
|
||||
*/
|
||||
package org.springframework.security.ldap.userdetails;
|
||||
|
||||
|
||||
@@ -43,8 +43,7 @@ public class LdapUtilsTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetRelativeNameReturnsEmptyStringForDnEqualToBaseName()
|
||||
throws Exception {
|
||||
public void testGetRelativeNameReturnsEmptyStringForDnEqualToBaseName() throws Exception {
|
||||
final DirContext mockCtx = mock(DirContext.class);
|
||||
|
||||
when(mockCtx.getNameInNamespace()).thenReturn("dc=springframework,dc=org");
|
||||
@@ -57,7 +56,8 @@ public class LdapUtilsTests {
|
||||
final DirContext mockCtx = mock(DirContext.class);
|
||||
when(mockCtx.getNameInNamespace()).thenReturn("");
|
||||
|
||||
assertThat(LdapUtils.getRelativeName("cn=jane,dc=springframework,dc=org", mockCtx)).isEqualTo("cn=jane,dc=springframework,dc=org");
|
||||
assertThat(LdapUtils.getRelativeName("cn=jane,dc=springframework,dc=org", mockCtx))
|
||||
.isEqualTo("cn=jane,dc=springframework,dc=org");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -65,8 +65,8 @@ public class LdapUtilsTests {
|
||||
final DirContext mockCtx = mock(DirContext.class);
|
||||
when(mockCtx.getNameInNamespace()).thenReturn("dc=springsecurity,dc = org");
|
||||
|
||||
assertThat(LdapUtils.getRelativeName(
|
||||
"cn=jane smith, dc = springsecurity , dc=org", mockCtx)).isEqualTo("cn=jane smith");
|
||||
assertThat(LdapUtils.getRelativeName("cn=jane smith, dc = springsecurity , dc=org", mockCtx))
|
||||
.isEqualTo("cn=jane smith");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -75,19 +75,16 @@ public class LdapUtilsTests {
|
||||
assertThat(LdapUtils.parseRootDnFromUrl("ldap://monkeymachine:11389")).isEqualTo("");
|
||||
assertThat(LdapUtils.parseRootDnFromUrl("ldap://monkeymachine/")).isEqualTo("");
|
||||
assertThat(LdapUtils.parseRootDnFromUrl("ldap://monkeymachine.co.uk/")).isEqualTo("");
|
||||
assertThat(
|
||||
LdapUtils
|
||||
.parseRootDnFromUrl("ldaps://monkeymachine.co.uk/dc=springframework,dc=org")).isEqualTo("dc=springframework,dc=org");
|
||||
assertThat(
|
||||
LdapUtils.parseRootDnFromUrl("ldap:///dc=springframework,dc=org")).isEqualTo("dc=springframework,dc=org");
|
||||
assertThat(
|
||||
LdapUtils
|
||||
.parseRootDnFromUrl("ldap://monkeymachine/dc=springframework,dc=org")).isEqualTo("dc=springframework,dc=org");
|
||||
assertThat(
|
||||
LdapUtils
|
||||
.parseRootDnFromUrl("ldap://monkeymachine.co.uk/dc=springframework,dc=org/ou=blah")).isEqualTo("dc=springframework,dc=org/ou=blah");
|
||||
assertThat(
|
||||
LdapUtils
|
||||
.parseRootDnFromUrl("ldap://monkeymachine.co.uk:389/dc=springframework,dc=org/ou=blah")).isEqualTo("dc=springframework,dc=org/ou=blah");
|
||||
assertThat(LdapUtils.parseRootDnFromUrl("ldaps://monkeymachine.co.uk/dc=springframework,dc=org"))
|
||||
.isEqualTo("dc=springframework,dc=org");
|
||||
assertThat(LdapUtils.parseRootDnFromUrl("ldap:///dc=springframework,dc=org"))
|
||||
.isEqualTo("dc=springframework,dc=org");
|
||||
assertThat(LdapUtils.parseRootDnFromUrl("ldap://monkeymachine/dc=springframework,dc=org"))
|
||||
.isEqualTo("dc=springframework,dc=org");
|
||||
assertThat(LdapUtils.parseRootDnFromUrl("ldap://monkeymachine.co.uk/dc=springframework,dc=org/ou=blah"))
|
||||
.isEqualTo("dc=springframework,dc=org/ou=blah");
|
||||
assertThat(LdapUtils.parseRootDnFromUrl("ldap://monkeymachine.co.uk:389/dc=springframework,dc=org/ou=blah"))
|
||||
.isEqualTo("dc=springframework,dc=org/ou=blah");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ import org.junit.Test;
|
||||
* @author Luke Taylor
|
||||
*/
|
||||
public class SpringSecurityAuthenticationSourceTests {
|
||||
|
||||
@Before
|
||||
@After
|
||||
public void clearContext() {
|
||||
@@ -51,16 +52,14 @@ public class SpringSecurityAuthenticationSourceTests {
|
||||
AuthenticationSource source = new SpringSecurityAuthenticationSource();
|
||||
|
||||
SecurityContextHolder.getContext().setAuthentication(
|
||||
new AnonymousAuthenticationToken("key", "anonUser", AuthorityUtils
|
||||
.createAuthorityList("ignored")));
|
||||
new AnonymousAuthenticationToken("key", "anonUser", AuthorityUtils.createAuthorityList("ignored")));
|
||||
assertThat(source.getPrincipal()).isEqualTo("");
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void getPrincipalRejectsNonLdapUserDetailsObject() {
|
||||
AuthenticationSource source = new SpringSecurityAuthenticationSource();
|
||||
SecurityContextHolder.getContext().setAuthentication(
|
||||
new TestingAuthenticationToken(new Object(), "password"));
|
||||
SecurityContextHolder.getContext().setAuthentication(new TestingAuthenticationToken(new Object(), "password"));
|
||||
|
||||
source.getPrincipal();
|
||||
}
|
||||
@@ -68,8 +67,7 @@ public class SpringSecurityAuthenticationSourceTests {
|
||||
@Test
|
||||
public void expectedCredentialsAreReturned() {
|
||||
AuthenticationSource source = new SpringSecurityAuthenticationSource();
|
||||
SecurityContextHolder.getContext().setAuthentication(
|
||||
new TestingAuthenticationToken(new Object(), "password"));
|
||||
SecurityContextHolder.getContext().setAuthentication(new TestingAuthenticationToken(new Object(), "password"));
|
||||
|
||||
assertThat(source.getCredentials()).isEqualTo("password");
|
||||
}
|
||||
@@ -80,9 +78,10 @@ public class SpringSecurityAuthenticationSourceTests {
|
||||
user.setUsername("joe");
|
||||
user.setDn(new DistinguishedName("uid=joe,ou=users"));
|
||||
AuthenticationSource source = new SpringSecurityAuthenticationSource();
|
||||
SecurityContextHolder.getContext().setAuthentication(
|
||||
new TestingAuthenticationToken(user.createUserDetails(), null));
|
||||
SecurityContextHolder.getContext()
|
||||
.setAuthentication(new TestingAuthenticationToken(user.createUserDetails(), null));
|
||||
|
||||
assertThat(source.getPrincipal()).isEqualTo("uid=joe,ou=users");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -37,10 +37,13 @@ public class SpringSecurityLdapTemplateTests {
|
||||
|
||||
@Mock
|
||||
private DirContext ctx;
|
||||
|
||||
@Captor
|
||||
private ArgumentCaptor<SearchControls> searchControls;
|
||||
|
||||
@Mock
|
||||
private NamingEnumeration<SearchResult> resultsEnum;
|
||||
|
||||
@Mock
|
||||
private SearchResult searchResult;
|
||||
|
||||
@@ -53,15 +56,13 @@ public class SpringSecurityLdapTemplateTests {
|
||||
Object[] params = new Object[] {};
|
||||
DirContextAdapter searchResultObject = mock(DirContextAdapter.class);
|
||||
|
||||
when(
|
||||
ctx.search(any(DistinguishedName.class), eq(filter), eq(params),
|
||||
searchControls.capture())).thenReturn(resultsEnum);
|
||||
when(ctx.search(any(DistinguishedName.class), eq(filter), eq(params), searchControls.capture()))
|
||||
.thenReturn(resultsEnum);
|
||||
when(resultsEnum.hasMore()).thenReturn(true, false);
|
||||
when(resultsEnum.next()).thenReturn(searchResult);
|
||||
when(searchResult.getObject()).thenReturn(searchResultObject);
|
||||
|
||||
SpringSecurityLdapTemplate.searchForSingleEntryInternal(ctx,
|
||||
mock(SearchControls.class), base, filter, params);
|
||||
SpringSecurityLdapTemplate.searchForSingleEntryInternal(ctx, mock(SearchControls.class), base, filter, params);
|
||||
|
||||
assertThat(searchControls.getValue().getReturningObjFlag()).isTrue();
|
||||
}
|
||||
|
||||
@@ -51,36 +51,34 @@ public class LdapAuthenticationProviderTests {
|
||||
|
||||
@Test
|
||||
public void testSupportsUsernamePasswordAuthenticationToken() {
|
||||
LdapAuthenticationProvider ldapProvider = new LdapAuthenticationProvider(
|
||||
new MockAuthenticator(), new MockAuthoritiesPopulator());
|
||||
LdapAuthenticationProvider ldapProvider = new LdapAuthenticationProvider(new MockAuthenticator(),
|
||||
new MockAuthoritiesPopulator());
|
||||
|
||||
assertThat(ldapProvider.supports(UsernamePasswordAuthenticationToken.class)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultMapperIsSet() {
|
||||
LdapAuthenticationProvider ldapProvider = new LdapAuthenticationProvider(
|
||||
new MockAuthenticator(), new MockAuthoritiesPopulator());
|
||||
LdapAuthenticationProvider ldapProvider = new LdapAuthenticationProvider(new MockAuthenticator(),
|
||||
new MockAuthoritiesPopulator());
|
||||
|
||||
assertThat(ldapProvider.getUserDetailsContextMapper() instanceof LdapUserDetailsMapper).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyOrNullUserNameThrowsException() {
|
||||
LdapAuthenticationProvider ldapProvider = new LdapAuthenticationProvider(
|
||||
new MockAuthenticator(), new MockAuthoritiesPopulator());
|
||||
LdapAuthenticationProvider ldapProvider = new LdapAuthenticationProvider(new MockAuthenticator(),
|
||||
new MockAuthoritiesPopulator());
|
||||
|
||||
try {
|
||||
ldapProvider.authenticate(new UsernamePasswordAuthenticationToken(null,
|
||||
"password"));
|
||||
ldapProvider.authenticate(new UsernamePasswordAuthenticationToken(null, "password"));
|
||||
fail("Expected BadCredentialsException for empty username");
|
||||
}
|
||||
catch (BadCredentialsException expected) {
|
||||
}
|
||||
|
||||
try {
|
||||
ldapProvider.authenticate(new UsernamePasswordAuthenticationToken("",
|
||||
"bobspassword"));
|
||||
ldapProvider.authenticate(new UsernamePasswordAuthenticationToken("", "bobspassword"));
|
||||
fail("Expected BadCredentialsException for null username");
|
||||
}
|
||||
catch (BadCredentialsException expected) {
|
||||
@@ -90,26 +88,20 @@ public class LdapAuthenticationProviderTests {
|
||||
@Test(expected = BadCredentialsException.class)
|
||||
public void usernameNotFoundExceptionIsHiddenByDefault() {
|
||||
final LdapAuthenticator authenticator = mock(LdapAuthenticator.class);
|
||||
final UsernamePasswordAuthenticationToken joe = new UsernamePasswordAuthenticationToken(
|
||||
"joe", "password");
|
||||
when(authenticator.authenticate(joe)).thenThrow(
|
||||
new UsernameNotFoundException("nobody"));
|
||||
final UsernamePasswordAuthenticationToken joe = new UsernamePasswordAuthenticationToken("joe", "password");
|
||||
when(authenticator.authenticate(joe)).thenThrow(new UsernameNotFoundException("nobody"));
|
||||
|
||||
LdapAuthenticationProvider provider = new LdapAuthenticationProvider(
|
||||
authenticator);
|
||||
LdapAuthenticationProvider provider = new LdapAuthenticationProvider(authenticator);
|
||||
provider.authenticate(joe);
|
||||
}
|
||||
|
||||
@Test(expected = UsernameNotFoundException.class)
|
||||
public void usernameNotFoundExceptionIsNotHiddenIfConfigured() {
|
||||
final LdapAuthenticator authenticator = mock(LdapAuthenticator.class);
|
||||
final UsernamePasswordAuthenticationToken joe = new UsernamePasswordAuthenticationToken(
|
||||
"joe", "password");
|
||||
when(authenticator.authenticate(joe)).thenThrow(
|
||||
new UsernameNotFoundException("nobody"));
|
||||
final UsernamePasswordAuthenticationToken joe = new UsernamePasswordAuthenticationToken("joe", "password");
|
||||
when(authenticator.authenticate(joe)).thenThrow(new UsernameNotFoundException("nobody"));
|
||||
|
||||
LdapAuthenticationProvider provider = new LdapAuthenticationProvider(
|
||||
authenticator);
|
||||
LdapAuthenticationProvider provider = new LdapAuthenticationProvider(authenticator);
|
||||
provider.setHideUserNotFoundExceptions(false);
|
||||
provider.authenticate(joe);
|
||||
}
|
||||
@@ -117,16 +109,15 @@ public class LdapAuthenticationProviderTests {
|
||||
@Test
|
||||
public void normalUsage() {
|
||||
MockAuthoritiesPopulator populator = new MockAuthoritiesPopulator();
|
||||
LdapAuthenticationProvider ldapProvider = new LdapAuthenticationProvider(
|
||||
new MockAuthenticator(), populator);
|
||||
LdapAuthenticationProvider ldapProvider = new LdapAuthenticationProvider(new MockAuthenticator(), populator);
|
||||
LdapUserDetailsMapper userMapper = new LdapUserDetailsMapper();
|
||||
userMapper.setRoleAttributes(new String[] { "ou" });
|
||||
ldapProvider.setUserDetailsContextMapper(userMapper);
|
||||
|
||||
assertThat(ldapProvider.getAuthoritiesPopulator()).isNotNull();
|
||||
|
||||
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
|
||||
"ben", "benspassword");
|
||||
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken("ben",
|
||||
"benspassword");
|
||||
Object authDetails = new Object();
|
||||
authRequest.setDetails(authDetails);
|
||||
Authentication authResult = ldapProvider.authenticate(authRequest);
|
||||
@@ -138,20 +129,18 @@ public class LdapAuthenticationProviderTests {
|
||||
assertThat(user.getUsername()).isEqualTo("ben");
|
||||
assertThat(populator.getRequestedUsername()).isEqualTo("ben");
|
||||
|
||||
assertThat(AuthorityUtils.authorityListToSet(user.getAuthorities()))
|
||||
.contains("ROLE_FROM_ENTRY");
|
||||
assertThat(AuthorityUtils.authorityListToSet(user.getAuthorities()))
|
||||
.contains("ROLE_FROM_POPULATOR");
|
||||
assertThat(AuthorityUtils.authorityListToSet(user.getAuthorities())).contains("ROLE_FROM_ENTRY");
|
||||
assertThat(AuthorityUtils.authorityListToSet(user.getAuthorities())).contains("ROLE_FROM_POPULATOR");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void passwordIsSetFromUserDataIfUseAuthenticationRequestCredentialsIsFalse() {
|
||||
LdapAuthenticationProvider ldapProvider = new LdapAuthenticationProvider(
|
||||
new MockAuthenticator(), new MockAuthoritiesPopulator());
|
||||
LdapAuthenticationProvider ldapProvider = new LdapAuthenticationProvider(new MockAuthenticator(),
|
||||
new MockAuthoritiesPopulator());
|
||||
ldapProvider.setUseAuthenticationRequestCredentials(false);
|
||||
|
||||
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
|
||||
"ben", "benspassword");
|
||||
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken("ben",
|
||||
"benspassword");
|
||||
Authentication authResult = ldapProvider.authenticate(authRequest);
|
||||
assertThat(authResult.getCredentials()).isEqualTo("{SHA}nFCebWjxfaLbHHG1Qk5UU4trbvQ=");
|
||||
|
||||
@@ -159,31 +148,26 @@ public class LdapAuthenticationProviderTests {
|
||||
|
||||
@Test
|
||||
public void useWithNullAuthoritiesPopulatorReturnsCorrectRole() {
|
||||
LdapAuthenticationProvider ldapProvider = new LdapAuthenticationProvider(
|
||||
new MockAuthenticator());
|
||||
LdapAuthenticationProvider ldapProvider = new LdapAuthenticationProvider(new MockAuthenticator());
|
||||
LdapUserDetailsMapper userMapper = new LdapUserDetailsMapper();
|
||||
userMapper.setRoleAttributes(new String[] { "ou" });
|
||||
ldapProvider.setUserDetailsContextMapper(userMapper);
|
||||
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
|
||||
"ben", "benspassword");
|
||||
UserDetails user = (UserDetails) ldapProvider.authenticate(authRequest)
|
||||
.getPrincipal();
|
||||
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken("ben",
|
||||
"benspassword");
|
||||
UserDetails user = (UserDetails) ldapProvider.authenticate(authRequest).getPrincipal();
|
||||
assertThat(user.getAuthorities()).hasSize(1);
|
||||
assertThat(AuthorityUtils.authorityListToSet(user.getAuthorities()))
|
||||
.contains("ROLE_FROM_ENTRY");
|
||||
assertThat(AuthorityUtils.authorityListToSet(user.getAuthorities())).contains("ROLE_FROM_ENTRY");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void authenticateWithNamingException() {
|
||||
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
|
||||
"ben", "benspassword");
|
||||
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken("ben",
|
||||
"benspassword");
|
||||
LdapAuthenticator mockAuthenticator = mock(LdapAuthenticator.class);
|
||||
CommunicationException expectedCause = new CommunicationException(
|
||||
new javax.naming.CommunicationException());
|
||||
CommunicationException expectedCause = new CommunicationException(new javax.naming.CommunicationException());
|
||||
when(mockAuthenticator.authenticate(authRequest)).thenThrow(expectedCause);
|
||||
|
||||
LdapAuthenticationProvider ldapProvider = new LdapAuthenticationProvider(
|
||||
mockAuthenticator);
|
||||
LdapAuthenticationProvider ldapProvider = new LdapAuthenticationProvider(mockAuthenticator);
|
||||
try {
|
||||
ldapProvider.authenticate(authRequest);
|
||||
fail("Expected Exception");
|
||||
@@ -205,28 +189,27 @@ public class LdapAuthenticationProviderTests {
|
||||
String password = (String) authentication.getCredentials();
|
||||
|
||||
if (username.equals("ben") && password.equals("benspassword")) {
|
||||
ctx.setDn(new DistinguishedName(
|
||||
"cn=ben,ou=people,dc=springframework,dc=org"));
|
||||
ctx.setDn(new DistinguishedName("cn=ben,ou=people,dc=springframework,dc=org"));
|
||||
ctx.setAttributeValue("userPassword", "{SHA}nFCebWjxfaLbHHG1Qk5UU4trbvQ=");
|
||||
|
||||
return ctx;
|
||||
}
|
||||
else if (username.equals("jen") && password.equals("")) {
|
||||
ctx.setDn(new DistinguishedName(
|
||||
"cn=jen,ou=people,dc=springframework,dc=org"));
|
||||
ctx.setDn(new DistinguishedName("cn=jen,ou=people,dc=springframework,dc=org"));
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
throw new BadCredentialsException("Authentication failed.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class MockAuthoritiesPopulator implements LdapAuthoritiesPopulator {
|
||||
|
||||
String username;
|
||||
|
||||
public Collection<GrantedAuthority> getGrantedAuthorities(
|
||||
DirContextOperations userCtx, String username) {
|
||||
public Collection<GrantedAuthority> getGrantedAuthorities(DirContextOperations userCtx, String username) {
|
||||
this.username = username;
|
||||
return AuthorityUtils.createAuthorityList("ROLE_FROM_POPULATOR");
|
||||
}
|
||||
@@ -234,5 +217,7 @@ public class LdapAuthenticationProviderTests {
|
||||
String getRequestedUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -21,11 +21,10 @@ import org.springframework.security.ldap.search.LdapUserSearch;
|
||||
import org.springframework.ldap.core.DirContextOperations;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author Luke Taylor
|
||||
*/
|
||||
public class MockUserSearch implements LdapUserSearch {
|
||||
|
||||
// ~ Instance fields
|
||||
// ================================================================================================
|
||||
|
||||
@@ -47,4 +46,5 @@ public class MockUserSearch implements LdapUserSearch {
|
||||
public DirContextOperations searchForUser(String username) {
|
||||
return user;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -29,7 +29,6 @@ import org.springframework.ldap.core.support.BaseLdapPathContextSource;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Luke Taylor
|
||||
*/
|
||||
public class PasswordComparisonAuthenticatorMockTests {
|
||||
@@ -44,26 +43,22 @@ public class PasswordComparisonAuthenticatorMockTests {
|
||||
final BasicAttributes attrs = new BasicAttributes();
|
||||
attrs.put(new BasicAttribute("uid", "bob"));
|
||||
|
||||
PasswordComparisonAuthenticator authenticator = new PasswordComparisonAuthenticator(
|
||||
source);
|
||||
PasswordComparisonAuthenticator authenticator = new PasswordComparisonAuthenticator(source);
|
||||
|
||||
authenticator.setUserDnPatterns(new String[] { "cn={0},ou=people" });
|
||||
|
||||
// Get the mock to return an empty attribute set
|
||||
when(source.getReadOnlyContext()).thenReturn(dirCtx);
|
||||
when(dirCtx.getAttributes(eq("cn=Bob,ou=people"), any(String[].class)))
|
||||
.thenReturn(attrs);
|
||||
when(dirCtx.getAttributes(eq("cn=Bob,ou=people"), any(String[].class))).thenReturn(attrs);
|
||||
when(dirCtx.getNameInNamespace()).thenReturn("dc=springframework,dc=org");
|
||||
|
||||
// Setup a single return value (i.e. success)
|
||||
final NamingEnumeration searchResults = new BasicAttributes("", null).getAll();
|
||||
|
||||
when(
|
||||
dirCtx.search(eq("cn=Bob,ou=people"), eq("(userPassword={0})"),
|
||||
any(Object[].class), any(SearchControls.class))).thenReturn(
|
||||
searchResults);
|
||||
when(dirCtx.search(eq("cn=Bob,ou=people"), eq("(userPassword={0})"), any(Object[].class),
|
||||
any(SearchControls.class))).thenReturn(searchResults);
|
||||
|
||||
authenticator.authenticate(new UsernamePasswordAuthenticationToken("Bob",
|
||||
"bobspassword"));
|
||||
authenticator.authenticate(new UsernamePasswordAuthenticationToken("Bob", "bobspassword"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -64,20 +64,21 @@ import static org.springframework.security.ldap.authentication.ad.ActiveDirector
|
||||
* @author Rob Winch
|
||||
*/
|
||||
public class ActiveDirectoryLdapAuthenticationProviderTests {
|
||||
|
||||
public static final String EXISTING_LDAP_PROVIDER = "ldap://192.168.1.200/";
|
||||
|
||||
public static final String NON_EXISTING_LDAP_PROVIDER = "ldap://192.168.1.201/";
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
ActiveDirectoryLdapAuthenticationProvider provider;
|
||||
UsernamePasswordAuthenticationToken joe = new UsernamePasswordAuthenticationToken(
|
||||
"joe", "password");
|
||||
|
||||
UsernamePasswordAuthenticationToken joe = new UsernamePasswordAuthenticationToken("joe", "password");
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
provider = new ActiveDirectoryLdapAuthenticationProvider("mydomain.eu",
|
||||
"ldap://192.168.1.200/");
|
||||
provider = new ActiveDirectoryLdapAuthenticationProvider("mydomain.eu", "ldap://192.168.1.200/");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -101,12 +102,9 @@ public class ActiveDirectoryLdapAuthenticationProviderTests {
|
||||
when(ctx.getNameInNamespace()).thenReturn("");
|
||||
|
||||
DirContextAdapter dca = new DirContextAdapter();
|
||||
SearchResult sr = new SearchResult("CN=Joe Jannsen,CN=Users", dca,
|
||||
dca.getAttributes());
|
||||
when(
|
||||
ctx.search(any(Name.class), eq(customSearchFilter), any(Object[].class),
|
||||
any(SearchControls.class))).thenReturn(
|
||||
new MockNamingEnumeration(sr));
|
||||
SearchResult sr = new SearchResult("CN=Joe Jannsen,CN=Users", dca, dca.getAttributes());
|
||||
when(ctx.search(any(Name.class), eq(customSearchFilter), any(Object[].class), any(SearchControls.class)))
|
||||
.thenReturn(new MockNamingEnumeration(sr));
|
||||
|
||||
ActiveDirectoryLdapAuthenticationProvider customProvider = new ActiveDirectoryLdapAuthenticationProvider(
|
||||
"mydomain.eu", "ldap://192.168.1.200/");
|
||||
@@ -129,12 +127,9 @@ public class ActiveDirectoryLdapAuthenticationProviderTests {
|
||||
when(ctx.getNameInNamespace()).thenReturn("");
|
||||
|
||||
DirContextAdapter dca = new DirContextAdapter();
|
||||
SearchResult sr = new SearchResult("CN=Joe Jannsen,CN=Users", dca,
|
||||
dca.getAttributes());
|
||||
when(
|
||||
ctx.search(any(Name.class), eq(defaultSearchFilter), any(Object[].class),
|
||||
any(SearchControls.class))).thenReturn(
|
||||
new MockNamingEnumeration(sr));
|
||||
SearchResult sr = new SearchResult("CN=Joe Jannsen,CN=Users", dca, dca.getAttributes());
|
||||
when(ctx.search(any(Name.class), eq(defaultSearchFilter), any(Object[].class), any(SearchControls.class)))
|
||||
.thenReturn(new MockNamingEnumeration(sr));
|
||||
|
||||
ActiveDirectoryLdapAuthenticationProvider customProvider = new ActiveDirectoryLdapAuthenticationProvider(
|
||||
"mydomain.eu", "ldap://192.168.1.200/");
|
||||
@@ -145,8 +140,8 @@ public class ActiveDirectoryLdapAuthenticationProviderTests {
|
||||
|
||||
// then
|
||||
assertThat(result.isAuthenticated()).isTrue();
|
||||
verify(ctx).search(any(DistinguishedName.class), eq(defaultSearchFilter),
|
||||
any(Object[].class), any(SearchControls.class));
|
||||
verify(ctx).search(any(DistinguishedName.class), eq(defaultSearchFilter), any(Object[].class),
|
||||
any(SearchControls.class));
|
||||
}
|
||||
|
||||
// SEC-2897,SEC-2224
|
||||
@@ -160,12 +155,9 @@ public class ActiveDirectoryLdapAuthenticationProviderTests {
|
||||
when(ctx.getNameInNamespace()).thenReturn("");
|
||||
|
||||
DirContextAdapter dca = new DirContextAdapter();
|
||||
SearchResult sr = new SearchResult("CN=Joe Jannsen,CN=Users", dca,
|
||||
dca.getAttributes());
|
||||
when(
|
||||
ctx.search(any(Name.class), eq(defaultSearchFilter), captor.capture(),
|
||||
any(SearchControls.class))).thenReturn(
|
||||
new MockNamingEnumeration(sr));
|
||||
SearchResult sr = new SearchResult("CN=Joe Jannsen,CN=Users", dca, dca.getAttributes());
|
||||
when(ctx.search(any(Name.class), eq(defaultSearchFilter), captor.capture(), any(SearchControls.class)))
|
||||
.thenReturn(new MockNamingEnumeration(sr));
|
||||
|
||||
ActiveDirectoryLdapAuthenticationProvider customProvider = new ActiveDirectoryLdapAuthenticationProvider(
|
||||
"mydomain.eu", "ldap://192.168.1.200/");
|
||||
@@ -190,20 +182,15 @@ public class ActiveDirectoryLdapAuthenticationProviderTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nullDomainIsSupportedIfAuthenticatingWithFullUserPrincipal()
|
||||
throws Exception {
|
||||
provider = new ActiveDirectoryLdapAuthenticationProvider(null,
|
||||
"ldap://192.168.1.200/");
|
||||
public void nullDomainIsSupportedIfAuthenticatingWithFullUserPrincipal() throws Exception {
|
||||
provider = new ActiveDirectoryLdapAuthenticationProvider(null, "ldap://192.168.1.200/");
|
||||
DirContext ctx = mock(DirContext.class);
|
||||
when(ctx.getNameInNamespace()).thenReturn("");
|
||||
|
||||
DirContextAdapter dca = new DirContextAdapter();
|
||||
SearchResult sr = new SearchResult("CN=Joe Jannsen,CN=Users", dca,
|
||||
dca.getAttributes());
|
||||
when(
|
||||
ctx.search(eq(new DistinguishedName("DC=mydomain,DC=eu")),
|
||||
any(String.class), any(Object[].class), any(SearchControls.class)))
|
||||
.thenReturn(new MockNamingEnumeration(sr));
|
||||
SearchResult sr = new SearchResult("CN=Joe Jannsen,CN=Users", dca, dca.getAttributes());
|
||||
when(ctx.search(eq(new DistinguishedName("DC=mydomain,DC=eu")), any(String.class), any(Object[].class),
|
||||
any(SearchControls.class))).thenReturn(new MockNamingEnumeration(sr));
|
||||
provider.contextFactory = createContextFactoryReturning(ctx);
|
||||
|
||||
try {
|
||||
@@ -213,17 +200,14 @@ public class ActiveDirectoryLdapAuthenticationProviderTests {
|
||||
catch (BadCredentialsException expected) {
|
||||
}
|
||||
|
||||
provider.authenticate(new UsernamePasswordAuthenticationToken("joe@mydomain.eu",
|
||||
"password"));
|
||||
provider.authenticate(new UsernamePasswordAuthenticationToken("joe@mydomain.eu", "password"));
|
||||
}
|
||||
|
||||
@Test(expected = BadCredentialsException.class)
|
||||
public void failedUserSearchCausesBadCredentials() throws Exception {
|
||||
DirContext ctx = mock(DirContext.class);
|
||||
when(ctx.getNameInNamespace()).thenReturn("");
|
||||
when(
|
||||
ctx.search(any(Name.class), any(String.class), any(Object[].class),
|
||||
any(SearchControls.class)))
|
||||
when(ctx.search(any(Name.class), any(String.class), any(Object[].class), any(SearchControls.class)))
|
||||
.thenThrow(new NameNotFoundException());
|
||||
|
||||
provider.contextFactory = createContextFactoryReturning(ctx);
|
||||
@@ -236,10 +220,8 @@ public class ActiveDirectoryLdapAuthenticationProviderTests {
|
||||
public void noUserSearchCausesUsernameNotFound() throws Exception {
|
||||
DirContext ctx = mock(DirContext.class);
|
||||
when(ctx.getNameInNamespace()).thenReturn("");
|
||||
when(
|
||||
ctx.search(any(Name.class), any(String.class), any(Object[].class),
|
||||
any(SearchControls.class))).thenReturn(
|
||||
new EmptyEnumeration<>());
|
||||
when(ctx.search(any(Name.class), any(String.class), any(Object[].class), any(SearchControls.class)))
|
||||
.thenReturn(new EmptyEnumeration<>());
|
||||
|
||||
provider.contextFactory = createContextFactoryReturning(ctx);
|
||||
|
||||
@@ -260,12 +242,10 @@ public class ActiveDirectoryLdapAuthenticationProviderTests {
|
||||
NamingEnumeration<SearchResult> searchResults = mock(NamingEnumeration.class);
|
||||
when(searchResults.hasMore()).thenReturn(true, true, false);
|
||||
SearchResult searchResult = mock(SearchResult.class);
|
||||
when(searchResult.getObject()).thenReturn(new DirContextAdapter("ou=1"),
|
||||
new DirContextAdapter("ou=2"));
|
||||
when(searchResult.getObject()).thenReturn(new DirContextAdapter("ou=1"), new DirContextAdapter("ou=2"));
|
||||
when(searchResults.next()).thenReturn(searchResult);
|
||||
when(
|
||||
ctx.search(any(Name.class), any(String.class), any(Object[].class),
|
||||
any(SearchControls.class))).thenReturn(searchResults);
|
||||
when(ctx.search(any(Name.class), any(String.class), any(Object[].class), any(SearchControls.class)))
|
||||
.thenReturn(searchResults);
|
||||
|
||||
provider.contextFactory = createContextFactoryReturning(ctx);
|
||||
|
||||
@@ -276,24 +256,21 @@ public class ActiveDirectoryLdapAuthenticationProviderTests {
|
||||
|
||||
@Test(expected = BadCredentialsException.class)
|
||||
public void userNotFoundIsCorrectlyMapped() {
|
||||
provider.contextFactory = createContextFactoryThrowing(new AuthenticationException(
|
||||
msg + "525, xxxx]"));
|
||||
provider.contextFactory = createContextFactoryThrowing(new AuthenticationException(msg + "525, xxxx]"));
|
||||
provider.setConvertSubErrorCodesToExceptions(true);
|
||||
provider.authenticate(joe);
|
||||
}
|
||||
|
||||
@Test(expected = BadCredentialsException.class)
|
||||
public void incorrectPasswordIsCorrectlyMapped() {
|
||||
provider.contextFactory = createContextFactoryThrowing(new AuthenticationException(
|
||||
msg + "52e, xxxx]"));
|
||||
provider.contextFactory = createContextFactoryThrowing(new AuthenticationException(msg + "52e, xxxx]"));
|
||||
provider.setConvertSubErrorCodesToExceptions(true);
|
||||
provider.authenticate(joe);
|
||||
}
|
||||
|
||||
@Test(expected = BadCredentialsException.class)
|
||||
public void notPermittedIsCorrectlyMapped() {
|
||||
provider.contextFactory = createContextFactoryThrowing(new AuthenticationException(
|
||||
msg + "530, xxxx]"));
|
||||
provider.contextFactory = createContextFactoryThrowing(new AuthenticationException(msg + "530, xxxx]"));
|
||||
provider.setConvertSubErrorCodesToExceptions(true);
|
||||
provider.authenticate(joe);
|
||||
}
|
||||
@@ -301,22 +278,20 @@ public class ActiveDirectoryLdapAuthenticationProviderTests {
|
||||
@Test
|
||||
public void passwordNeedsResetIsCorrectlyMapped() {
|
||||
final String dataCode = "773";
|
||||
provider.contextFactory = createContextFactoryThrowing(new AuthenticationException(
|
||||
msg + dataCode + ", xxxx]"));
|
||||
provider.contextFactory = createContextFactoryThrowing(new AuthenticationException(msg + dataCode + ", xxxx]"));
|
||||
provider.setConvertSubErrorCodesToExceptions(true);
|
||||
|
||||
thrown.expect(BadCredentialsException.class);
|
||||
thrown.expect(new BaseMatcher<BadCredentialsException>() {
|
||||
private Matcher<Object> causeInstance = CoreMatchers
|
||||
.instanceOf(ActiveDirectoryAuthenticationException.class);
|
||||
|
||||
private Matcher<String> causeDataCode = CoreMatchers.equalTo(dataCode);
|
||||
|
||||
public boolean matches(Object that) {
|
||||
Throwable t = (Throwable) that;
|
||||
ActiveDirectoryAuthenticationException cause = (ActiveDirectoryAuthenticationException) t
|
||||
.getCause();
|
||||
return causeInstance.matches(cause)
|
||||
&& causeDataCode.matches(cause.getDataCode());
|
||||
ActiveDirectoryAuthenticationException cause = (ActiveDirectoryAuthenticationException) t.getCause();
|
||||
return causeInstance.matches(cause) && causeDataCode.matches(cause.getDataCode());
|
||||
}
|
||||
|
||||
public void describeTo(Description desc) {
|
||||
@@ -332,8 +307,7 @@ public class ActiveDirectoryLdapAuthenticationProviderTests {
|
||||
|
||||
@Test(expected = CredentialsExpiredException.class)
|
||||
public void expiredPasswordIsCorrectlyMapped() {
|
||||
provider.contextFactory = createContextFactoryThrowing(new AuthenticationException(
|
||||
msg + "532, xxxx]"));
|
||||
provider.contextFactory = createContextFactoryThrowing(new AuthenticationException(msg + "532, xxxx]"));
|
||||
|
||||
try {
|
||||
provider.authenticate(joe);
|
||||
@@ -348,40 +322,35 @@ public class ActiveDirectoryLdapAuthenticationProviderTests {
|
||||
|
||||
@Test(expected = DisabledException.class)
|
||||
public void accountDisabledIsCorrectlyMapped() {
|
||||
provider.contextFactory = createContextFactoryThrowing(new AuthenticationException(
|
||||
msg + "533, xxxx]"));
|
||||
provider.contextFactory = createContextFactoryThrowing(new AuthenticationException(msg + "533, xxxx]"));
|
||||
provider.setConvertSubErrorCodesToExceptions(true);
|
||||
provider.authenticate(joe);
|
||||
}
|
||||
|
||||
@Test(expected = AccountExpiredException.class)
|
||||
public void accountExpiredIsCorrectlyMapped() {
|
||||
provider.contextFactory = createContextFactoryThrowing(new AuthenticationException(
|
||||
msg + "701, xxxx]"));
|
||||
provider.contextFactory = createContextFactoryThrowing(new AuthenticationException(msg + "701, xxxx]"));
|
||||
provider.setConvertSubErrorCodesToExceptions(true);
|
||||
provider.authenticate(joe);
|
||||
}
|
||||
|
||||
@Test(expected = LockedException.class)
|
||||
public void accountLockedIsCorrectlyMapped() {
|
||||
provider.contextFactory = createContextFactoryThrowing(new AuthenticationException(
|
||||
msg + "775, xxxx]"));
|
||||
provider.contextFactory = createContextFactoryThrowing(new AuthenticationException(msg + "775, xxxx]"));
|
||||
provider.setConvertSubErrorCodesToExceptions(true);
|
||||
provider.authenticate(joe);
|
||||
}
|
||||
|
||||
@Test(expected = BadCredentialsException.class)
|
||||
public void unknownErrorCodeIsCorrectlyMapped() {
|
||||
provider.contextFactory = createContextFactoryThrowing(new AuthenticationException(
|
||||
msg + "999, xxxx]"));
|
||||
provider.contextFactory = createContextFactoryThrowing(new AuthenticationException(msg + "999, xxxx]"));
|
||||
provider.setConvertSubErrorCodesToExceptions(true);
|
||||
provider.authenticate(joe);
|
||||
}
|
||||
|
||||
@Test(expected = BadCredentialsException.class)
|
||||
public void errorWithNoSubcodeIsHandledCleanly() {
|
||||
provider.contextFactory = createContextFactoryThrowing(new AuthenticationException(
|
||||
msg));
|
||||
provider.contextFactory = createContextFactoryThrowing(new AuthenticationException(msg));
|
||||
provider.setConvertSubErrorCodesToExceptions(true);
|
||||
provider.authenticate(joe);
|
||||
}
|
||||
@@ -389,22 +358,23 @@ public class ActiveDirectoryLdapAuthenticationProviderTests {
|
||||
@Test(expected = org.springframework.ldap.CommunicationException.class)
|
||||
public void nonAuthenticationExceptionIsConvertedToSpringLdapException() throws Throwable {
|
||||
try {
|
||||
provider.contextFactory = createContextFactoryThrowing(new CommunicationException(
|
||||
msg));
|
||||
provider.contextFactory = createContextFactoryThrowing(new CommunicationException(msg));
|
||||
provider.authenticate(joe);
|
||||
} catch (InternalAuthenticationServiceException e) {
|
||||
// Since GH-8418 ldap communication exception is wrapped into InternalAuthenticationServiceException.
|
||||
}
|
||||
catch (InternalAuthenticationServiceException e) {
|
||||
// Since GH-8418 ldap communication exception is wrapped into
|
||||
// InternalAuthenticationServiceException.
|
||||
// This test is about the wrapped exception, so we throw it.
|
||||
throw e.getCause();
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected = org.springframework.security.authentication.InternalAuthenticationServiceException.class )
|
||||
@Test(expected = org.springframework.security.authentication.InternalAuthenticationServiceException.class)
|
||||
public void connectionExceptionIsWrappedInInternalException() throws Exception {
|
||||
ActiveDirectoryLdapAuthenticationProvider noneReachableProvider = new ActiveDirectoryLdapAuthenticationProvider(
|
||||
"mydomain.eu", NON_EXISTING_LDAP_PROVIDER, "dc=ad,dc=eu,dc=mydomain");
|
||||
noneReachableProvider.setContextEnvironmentProperties(
|
||||
Collections.singletonMap("com.sun.jndi.ldap.connect.timeout", "5"));
|
||||
noneReachableProvider
|
||||
.setContextEnvironmentProperties(Collections.singletonMap("com.sun.jndi.ldap.connect.timeout", "5"));
|
||||
noneReachableProvider.doAuthentication(joe);
|
||||
}
|
||||
|
||||
@@ -439,8 +409,8 @@ public class ActiveDirectoryLdapAuthenticationProviderTests {
|
||||
}
|
||||
catch (InternalAuthenticationServiceException expected) {
|
||||
assertThat(expected.getCause()).isInstanceOf(org.springframework.ldap.CommunicationException.class);
|
||||
org.springframework.ldap.CommunicationException cause =
|
||||
(org.springframework.ldap.CommunicationException) expected.getCause();
|
||||
org.springframework.ldap.CommunicationException cause = (org.springframework.ldap.CommunicationException) expected
|
||||
.getCause();
|
||||
assertThat(cause.getRootCause()).isInstanceOf(ClassNotFoundException.class);
|
||||
}
|
||||
}
|
||||
@@ -463,20 +433,17 @@ public class ActiveDirectoryLdapAuthenticationProviderTests {
|
||||
};
|
||||
}
|
||||
|
||||
private void checkAuthentication(String rootDn,
|
||||
ActiveDirectoryLdapAuthenticationProvider provider) throws NamingException {
|
||||
private void checkAuthentication(String rootDn, ActiveDirectoryLdapAuthenticationProvider provider)
|
||||
throws NamingException {
|
||||
DirContext ctx = mock(DirContext.class);
|
||||
when(ctx.getNameInNamespace()).thenReturn("");
|
||||
|
||||
DirContextAdapter dca = new DirContextAdapter();
|
||||
SearchResult sr = new SearchResult("CN=Joe Jannsen,CN=Users", dca,
|
||||
dca.getAttributes());
|
||||
SearchResult sr = new SearchResult("CN=Joe Jannsen,CN=Users", dca, dca.getAttributes());
|
||||
@SuppressWarnings("deprecation")
|
||||
DistinguishedName searchBaseDn = new DistinguishedName(rootDn);
|
||||
when(
|
||||
ctx.search(eq(searchBaseDn), any(String.class), any(Object[].class),
|
||||
any(SearchControls.class))).thenReturn(
|
||||
new MockNamingEnumeration(sr)).thenReturn(new MockNamingEnumeration(sr));
|
||||
when(ctx.search(eq(searchBaseDn), any(String.class), any(Object[].class), any(SearchControls.class)))
|
||||
.thenReturn(new MockNamingEnumeration(sr)).thenReturn(new MockNamingEnumeration(sr));
|
||||
|
||||
provider.contextFactory = createContextFactoryReturning(ctx);
|
||||
|
||||
@@ -492,6 +459,7 @@ public class ActiveDirectoryLdapAuthenticationProviderTests {
|
||||
}
|
||||
|
||||
static class MockNamingEnumeration implements NamingEnumeration<SearchResult> {
|
||||
|
||||
private SearchResult sr;
|
||||
|
||||
MockNamingEnumeration(SearchResult sr) {
|
||||
@@ -518,5 +486,7 @@ public class ActiveDirectoryLdapAuthenticationProviderTests {
|
||||
public SearchResult nextElement() {
|
||||
return next();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ package org.springframework.security.ldap.ppolicy;
|
||||
public class OpenLDAPIntegrationTestSuite {
|
||||
|
||||
PasswordPolicyAwareContextSource cs;
|
||||
|
||||
/*
|
||||
* @Before public void createContextSource() throws Exception { cs = new
|
||||
* PasswordPolicyAwareContextSource("ldap://localhost:22389/dc=springsource,dc=com");
|
||||
@@ -60,4 +61,5 @@ public class OpenLDAPIntegrationTestSuite {
|
||||
* = (LdapUserDetailsImpl) a.getPrincipal(); assertTrue(ud.getTimeBeforeExpiration() <
|
||||
* Integer.MAX_VALUE && ud.getTimeBeforeExpiration() > 0); }
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
@@ -32,14 +32,15 @@ import java.util.*;
|
||||
* @author Luke Taylor
|
||||
*/
|
||||
public class PasswordPolicyAwareContextSourceTests {
|
||||
|
||||
private PasswordPolicyAwareContextSource ctxSource;
|
||||
|
||||
private final LdapContext ctx = mock(LdapContext.class);
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
reset(ctx);
|
||||
ctxSource = new PasswordPolicyAwareContextSource(
|
||||
"ldap://blah:789/dc=springframework,dc=org") {
|
||||
ctxSource = new PasswordPolicyAwareContextSource("ldap://blah:789/dc=springframework,dc=org") {
|
||||
@Override
|
||||
protected DirContext createContext(Hashtable env) {
|
||||
if ("manager".equals(env.get(Context.SECURITY_PRINCIPAL))) {
|
||||
@@ -60,24 +61,20 @@ public class PasswordPolicyAwareContextSourceTests {
|
||||
}
|
||||
|
||||
@Test(expected = UncategorizedLdapException.class)
|
||||
public void standardExceptionIsPropagatedWhenExceptionRaisedAndNoControlsAreSet()
|
||||
throws Exception {
|
||||
doThrow(new NamingException("some LDAP exception")).when(ctx).reconnect(
|
||||
any(Control[].class));
|
||||
public void standardExceptionIsPropagatedWhenExceptionRaisedAndNoControlsAreSet() throws Exception {
|
||||
doThrow(new NamingException("some LDAP exception")).when(ctx).reconnect(any(Control[].class));
|
||||
|
||||
ctxSource.getContext("user", "ignored");
|
||||
}
|
||||
|
||||
@Test(expected = PasswordPolicyException.class)
|
||||
public void lockedPasswordPolicyControlRaisesPasswordPolicyException()
|
||||
throws Exception {
|
||||
when(ctx.getResponseControls()).thenReturn(
|
||||
new Control[] { new PasswordPolicyResponseControl(
|
||||
PasswordPolicyResponseControlTests.OPENLDAP_LOCKED_CTRL) });
|
||||
public void lockedPasswordPolicyControlRaisesPasswordPolicyException() throws Exception {
|
||||
when(ctx.getResponseControls()).thenReturn(new Control[] {
|
||||
new PasswordPolicyResponseControl(PasswordPolicyResponseControlTests.OPENLDAP_LOCKED_CTRL) });
|
||||
|
||||
doThrow(new NamingException("locked message")).when(ctx).reconnect(
|
||||
any(Control[].class));
|
||||
doThrow(new NamingException("locked message")).when(ctx).reconnect(any(Control[].class));
|
||||
|
||||
ctxSource.getContext("user", "ignored");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -42,10 +42,10 @@ public class PasswordPolicyControlFactoryTests {
|
||||
Control control = mock(Control.class);
|
||||
|
||||
when(control.getID()).thenReturn(PasswordPolicyControl.OID);
|
||||
when(control.getEncodedValue()).thenReturn(
|
||||
PasswordPolicyResponseControlTests.OPENLDAP_LOCKED_CTRL);
|
||||
when(control.getEncodedValue()).thenReturn(PasswordPolicyResponseControlTests.OPENLDAP_LOCKED_CTRL);
|
||||
Control result = ctrlFactory.getControlInstance(control);
|
||||
assertThat(result).isNotNull();
|
||||
assertThat(PasswordPolicyResponseControlTests.OPENLDAP_LOCKED_CTRL).isEqualTo(result.getEncodedValue());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import org.junit.Test;
|
||||
* @author Luke Taylor
|
||||
*/
|
||||
public class PasswordPolicyResponseControlTests {
|
||||
|
||||
// ~ Methods
|
||||
// ========================================================================================================
|
||||
|
||||
@@ -94,8 +95,7 @@ public class PasswordPolicyResponseControlTests {
|
||||
|
||||
@Test
|
||||
public void openLDAP496GraceLoginsRemainingCtrlIsParsedCorrectly() {
|
||||
byte[] ctrlBytes = { 0x30, 0x06, (byte) 0xA0, 0x04, (byte) 0xA1, 0x02, 0x01,
|
||||
(byte) 0xF0 };
|
||||
byte[] ctrlBytes = { 0x30, 0x06, (byte) 0xA0, 0x04, (byte) 0xA1, 0x02, 0x01, (byte) 0xF0 };
|
||||
|
||||
PasswordPolicyResponseControl ctrl = new PasswordPolicyResponseControl(ctrlBytes);
|
||||
|
||||
@@ -103,13 +103,11 @@ public class PasswordPolicyResponseControlTests {
|
||||
assertThat(ctrl.getGraceLoginsRemaining()).isEqualTo(496);
|
||||
}
|
||||
|
||||
static final byte[] OPENLDAP_5_LOGINS_REMAINING_CTRL = { 0x30, 0x05, (byte) 0xA0,
|
||||
0x03, (byte) 0xA1, 0x01, 0x05 };
|
||||
static final byte[] OPENLDAP_5_LOGINS_REMAINING_CTRL = { 0x30, 0x05, (byte) 0xA0, 0x03, (byte) 0xA1, 0x01, 0x05 };
|
||||
|
||||
@Test
|
||||
public void openLDAP5GraceLoginsRemainingCtrlIsParsedCorrectly() {
|
||||
PasswordPolicyResponseControl ctrl = new PasswordPolicyResponseControl(
|
||||
OPENLDAP_5_LOGINS_REMAINING_CTRL);
|
||||
PasswordPolicyResponseControl ctrl = new PasswordPolicyResponseControl(OPENLDAP_5_LOGINS_REMAINING_CTRL);
|
||||
|
||||
assertThat(ctrl.hasWarning()).isTrue();
|
||||
assertThat(ctrl.getGraceLoginsRemaining()).isEqualTo(5);
|
||||
@@ -119,8 +117,7 @@ public class PasswordPolicyResponseControlTests {
|
||||
|
||||
@Test
|
||||
public void openLDAPAccountLockedCtrlIsParsedCorrectly() {
|
||||
PasswordPolicyResponseControl ctrl = new PasswordPolicyResponseControl(
|
||||
OPENLDAP_LOCKED_CTRL);
|
||||
PasswordPolicyResponseControl ctrl = new PasswordPolicyResponseControl(OPENLDAP_LOCKED_CTRL);
|
||||
|
||||
assertThat(ctrl.hasError() && ctrl.isLocked()).isTrue();
|
||||
assertThat(ctrl.hasWarning()).isFalse();
|
||||
@@ -135,4 +132,5 @@ public class PasswordPolicyResponseControlTests {
|
||||
assertThat(ctrl.hasError() && ctrl.isExpired()).isTrue();
|
||||
assertThat(ctrl.hasWarning()).isFalse();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -95,11 +95,10 @@ public class InetOrgPersonTests {
|
||||
public void mappingBackToContextMatchesOriginalData() {
|
||||
DirContextAdapter ctx1 = createUserContext();
|
||||
DirContextAdapter ctx2 = new DirContextAdapter();
|
||||
ctx1.setAttributeValues("objectclass", new String[] { "top", "person",
|
||||
"organizationalPerson", "inetOrgPerson" });
|
||||
ctx1.setAttributeValues("objectclass",
|
||||
new String[] { "top", "person", "organizationalPerson", "inetOrgPerson" });
|
||||
ctx2.setDn(new DistinguishedName("ignored=ignored"));
|
||||
InetOrgPerson p = (InetOrgPerson) (new InetOrgPerson.Essence(ctx1))
|
||||
.createUserDetails();
|
||||
InetOrgPerson p = (InetOrgPerson) (new InetOrgPerson.Essence(ctx1)).createUserDetails();
|
||||
p.populateContext(ctx2);
|
||||
|
||||
assertThat(ctx2).isEqualTo(ctx1);
|
||||
@@ -110,12 +109,10 @@ public class InetOrgPersonTests {
|
||||
DirContextAdapter ctx1 = createUserContext();
|
||||
DirContextAdapter ctx2 = new DirContextAdapter();
|
||||
ctx2.setDn(new DistinguishedName("ignored=ignored"));
|
||||
ctx1.setAttributeValues("objectclass", new String[] { "top", "person",
|
||||
"organizationalPerson", "inetOrgPerson" });
|
||||
InetOrgPerson p = (InetOrgPerson) (new InetOrgPerson.Essence(ctx1))
|
||||
.createUserDetails();
|
||||
InetOrgPerson p2 = (InetOrgPerson) new InetOrgPerson.Essence(p)
|
||||
.createUserDetails();
|
||||
ctx1.setAttributeValues("objectclass",
|
||||
new String[] { "top", "person", "organizationalPerson", "inetOrgPerson" });
|
||||
InetOrgPerson p = (InetOrgPerson) (new InetOrgPerson.Essence(ctx1)).createUserDetails();
|
||||
InetOrgPerson p2 = (InetOrgPerson) new InetOrgPerson.Essence(p).createUserDetails();
|
||||
p2.populateContext(ctx2);
|
||||
|
||||
assertThat(ctx2).isEqualTo(ctx1);
|
||||
|
||||
@@ -32,14 +32,14 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
public class LdapAuthorityTests {
|
||||
|
||||
public static final String DN = "cn=filip,ou=Users,dc=test,dc=com";
|
||||
|
||||
LdapAuthority authority;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
Map<String, List<String>> attributes = new HashMap<>();
|
||||
attributes.put(SpringSecurityLdapTemplate.DN_KEY, Arrays.asList(DN));
|
||||
attributes.put("mail",
|
||||
Arrays.asList("filip@ldap.test.org", "filip@ldap.test2.org"));
|
||||
attributes.put("mail", Arrays.asList("filip@ldap.test.org", "filip@ldap.test2.org"));
|
||||
authority = new LdapAuthority("testRole", DN, attributes);
|
||||
}
|
||||
|
||||
@@ -66,4 +66,5 @@ public class LdapAuthorityTests {
|
||||
assertThat(authority.getAuthority()).isNotNull();
|
||||
assertThat(authority.getAuthority()).isEqualTo("testRole");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -48,8 +48,8 @@ public class LdapUserDetailsMapperTests {
|
||||
ctx.setAttributeValues("userRole", new String[] { "X", "Y", "Z" });
|
||||
ctx.setAttributeValue("uid", "ani");
|
||||
|
||||
LdapUserDetailsImpl user = (LdapUserDetailsImpl) mapper.mapUserFromContext(ctx,
|
||||
"ani", AuthorityUtils.NO_AUTHORITIES);
|
||||
LdapUserDetailsImpl user = (LdapUserDetailsImpl) mapper.mapUserFromContext(ctx, "ani",
|
||||
AuthorityUtils.NO_AUTHORITIES);
|
||||
|
||||
assertThat(user.getAuthorities()).hasSize(3);
|
||||
}
|
||||
@@ -66,12 +66,11 @@ public class LdapUserDetailsMapperTests {
|
||||
BasicAttributes attrs = new BasicAttributes();
|
||||
attrs.put(new BasicAttribute("userRole", "x"));
|
||||
|
||||
DirContextAdapter ctx = new DirContextAdapter(attrs,
|
||||
new DistinguishedName("cn=someName"));
|
||||
DirContextAdapter ctx = new DirContextAdapter(attrs, new DistinguishedName("cn=someName"));
|
||||
ctx.setAttributeValue("uid", "ani");
|
||||
|
||||
LdapUserDetailsImpl user = (LdapUserDetailsImpl) mapper.mapUserFromContext(ctx,
|
||||
"ani", AuthorityUtils.NO_AUTHORITIES);
|
||||
LdapUserDetailsImpl user = (LdapUserDetailsImpl) mapper.mapUserFromContext(ctx, "ani",
|
||||
AuthorityUtils.NO_AUTHORITIES);
|
||||
|
||||
assertThat(user.getAuthorities()).hasSize(1);
|
||||
assertThat(AuthorityUtils.authorityListToSet(user.getAuthorities())).contains("ROLE_X");
|
||||
@@ -85,8 +84,7 @@ public class LdapUserDetailsMapperTests {
|
||||
BasicAttributes attrs = new BasicAttributes();
|
||||
attrs.put(new BasicAttribute("myappsPassword", "mypassword".getBytes()));
|
||||
|
||||
DirContextAdapter ctx = new DirContextAdapter(attrs,
|
||||
new DistinguishedName("cn=someName"));
|
||||
DirContextAdapter ctx = new DirContextAdapter(attrs, new DistinguishedName("cn=someName"));
|
||||
ctx.setAttributeValue("uid", "ani");
|
||||
|
||||
LdapUserDetails user = (LdapUserDetailsImpl) mapper.mapUserFromContext(ctx, "ani",
|
||||
|
||||
@@ -49,36 +49,34 @@ public class LdapUserDetailsServiceTests {
|
||||
|
||||
@Test
|
||||
public void correctAuthoritiesAreReturned() {
|
||||
DirContextAdapter userData = new DirContextAdapter(new DistinguishedName(
|
||||
"uid=joe"));
|
||||
DirContextAdapter userData = new DirContextAdapter(new DistinguishedName("uid=joe"));
|
||||
|
||||
LdapUserDetailsService service = new LdapUserDetailsService(new MockUserSearch(
|
||||
userData), new MockAuthoritiesPopulator());
|
||||
LdapUserDetailsService service = new LdapUserDetailsService(new MockUserSearch(userData),
|
||||
new MockAuthoritiesPopulator());
|
||||
service.setUserDetailsMapper(new LdapUserDetailsMapper());
|
||||
|
||||
UserDetails user = service.loadUserByUsername("doesntmatterwegetjoeanyway");
|
||||
|
||||
Set<String> authorities = AuthorityUtils
|
||||
.authorityListToSet(user.getAuthorities());
|
||||
Set<String> authorities = AuthorityUtils.authorityListToSet(user.getAuthorities());
|
||||
assertThat(authorities).hasSize(1);
|
||||
assertThat(authorities.contains("ROLE_FROM_POPULATOR")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nullPopulatorConstructorReturnsEmptyAuthoritiesList() {
|
||||
DirContextAdapter userData = new DirContextAdapter(new DistinguishedName(
|
||||
"uid=joe"));
|
||||
DirContextAdapter userData = new DirContextAdapter(new DistinguishedName("uid=joe"));
|
||||
|
||||
LdapUserDetailsService service = new LdapUserDetailsService(new MockUserSearch(
|
||||
userData));
|
||||
LdapUserDetailsService service = new LdapUserDetailsService(new MockUserSearch(userData));
|
||||
UserDetails user = service.loadUserByUsername("doesntmatterwegetjoeanyway");
|
||||
assertThat(user.getAuthorities()).isEmpty();
|
||||
}
|
||||
|
||||
class MockAuthoritiesPopulator implements LdapAuthoritiesPopulator {
|
||||
public Collection<GrantedAuthority> getGrantedAuthorities(
|
||||
DirContextOperations userCtx, String username) {
|
||||
|
||||
public Collection<GrantedAuthority> getGrantedAuthorities(DirContextOperations userCtx, String username) {
|
||||
return AuthorityUtils.createAuthorityList("ROLE_FROM_POPULATOR");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -42,12 +42,11 @@ public class UserDetailsServiceLdapAuthoritiesPopulatorTests {
|
||||
List authorities = AuthorityUtils.createAuthorityList("ROLE_USER");
|
||||
when(user.getAuthorities()).thenReturn(authorities);
|
||||
|
||||
UserDetailsServiceLdapAuthoritiesPopulator populator = new UserDetailsServiceLdapAuthoritiesPopulator(
|
||||
uds);
|
||||
Collection<? extends GrantedAuthority> auths = populator.getGrantedAuthorities(
|
||||
new DirContextAdapter(), "joe");
|
||||
UserDetailsServiceLdapAuthoritiesPopulator populator = new UserDetailsServiceLdapAuthoritiesPopulator(uds);
|
||||
Collection<? extends GrantedAuthority> auths = populator.getGrantedAuthorities(new DirContextAdapter(), "joe");
|
||||
|
||||
assertThat(auths).hasSize(1);
|
||||
assertThat(AuthorityUtils.authorityListToSet(auths).contains("ROLE_USER")).isTrue();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user