Reformat code using spring-javaformat

Run `./gradlew format` to reformat all java files.

Issue gh-8945
This commit is contained in:
Phillip Webb
2020-08-10 16:39:17 -05:00
committed by Rob Winch
parent 81d9c6cac5
commit b7fc18262d
2487 changed files with 41506 additions and 46548 deletions

View File

@@ -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

View File

@@ -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);
}
}
}

View File

@@ -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();
}

View File

@@ -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");
}
}

View File

@@ -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"));
}
}

View File

@@ -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");
}

View File

@@ -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();
}
}
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}
}

View File

@@ -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();
}

View File

@@ -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);
}
}

View File

@@ -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();
}
}
}

View File

@@ -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");
}
}

View File

@@ -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());
}
}

View File

@@ -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;
}
}

View File

@@ -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>

View File

@@ -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/>
* '&lt;' [less than] - "\&lt;" <br/>
* '&gt;' [greater than] - "\&gt;" <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/>
* '&lt;' [less than] - "\&lt;" <br/>
* '&gt;' [greater than] - "\&gt;" <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();
}
}

View File

@@ -23,5 +23,7 @@ import org.springframework.ldap.core.DistinguishedName;
* @author Luke Taylor
*/
public interface LdapUsernameToDnMapper {
DistinguishedName buildDn(String username);
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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/>
* '&lt;' [less than] - "\&lt;" <br/>
* '&gt;' [greater than] - "\&gt;" <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/>
* '&lt;' [less than] - "\&lt;" <br/>
* '&gt;' [greater than] - "\&gt;" <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();
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;

View File

@@ -17,4 +17,3 @@
* Spring Security's LDAP module.
*/
package org.springframework.security.ldap;

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;

View File

@@ -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;
}
}

View File

@@ -20,7 +20,9 @@ package org.springframework.security.ldap.ppolicy;
* @since 3.0
*/
public interface PasswordPolicyData {
int getTimeBeforeExpiration();
int getGraceLoginsRemaining();
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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 {
// }
// }
// }
}

View File

@@ -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;

View File

@@ -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();
}
}

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;

View File

@@ -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;
}
}

View File

@@ -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;
}
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -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 + '\'' + '}';
}
}

View File

@@ -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();
}

View File

@@ -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;
}
}
}

View File

@@ -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);
}
}
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}
}

View File

@@ -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) {

View File

@@ -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;
}
}
}

View File

@@ -45,4 +45,5 @@ public class PersonContextMapper implements UserDetailsContextMapper {
Person p = (Person) user;
p.populateContext(ctx);
}
}

View File

@@ -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);
}

View File

@@ -18,4 +18,3 @@
* contained in some of the standard LDAP types (such as {@code InetOrgPerson}).
*/
package org.springframework.security.ldap.userdetails;

View File

@@ -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");
}
}

View File

@@ -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");
}
}

View File

@@ -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();
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}

View File

@@ -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"));
}
}

View File

@@ -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();
}
}
}

View File

@@ -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); }
*/
}

View File

@@ -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");
}
}

View File

@@ -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());
}
}

View File

@@ -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();
}
}

View File

@@ -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);

View File

@@ -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");
}
}

View File

@@ -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",

View File

@@ -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");
}
}
}

View File

@@ -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();
}
}