Skip to content

Change ActiveDirectoryLdapAuthenticationProvider to use LdapClient#18627

Open
therepanic wants to merge 1 commit intospring-projects:mainfrom
therepanic:gh-17291
Open

Change ActiveDirectoryLdapAuthenticationProvider to use LdapClient#18627
therepanic wants to merge 1 commit intospring-projects:mainfrom
therepanic:gh-17291

Conversation

@therepanic
Copy link
Contributor

Replaces SpringSecurityLdapTemplate with LdapClient for user search operations.

Closes: gh-17291

Comment on lines 318 to +338
}
catch (org.springframework.ldap.NamingException ex) {
throw badCredentials(ex);
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is what you should do, otherwise the tests will fail.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the reason is because SpringSecurityLdapTemplate propagates the javax.naming exception while LdapClient wraps them. For passivity reasons, let's do something closer to:

if (ex.getCause() instanceof NamingException original) {
    throw original;
}
throw badCredentials(ex);

In this way, the original naming exception is propagated like it was before.

Comment on lines -100 to +101
given(this.ctx.search(any(Name.class), eq(customSearchFilter), any(Object[].class), any(SearchControls.class)))
given(this.ctx.search(any(Name.class), any(String.class), any(SearchControls.class)))
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not entirely sure whether this is a breaking change or not. Because when switching to LdapClient, we don't accept any(Object[].class) as the fourth argument, so we don't need to mock it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is okay, though I'd recommend that we still use eq to test the filter so that the expectations are still the same level of specificity:

String customSearchFilter = "(&(objectClass=user)(sAMAccountName={0}))";
String domain = "mydomain.eu";
String encoded = MessageFormat.format(customSearchFilter, this.joe.getPrincipal() + "@" + domain);
...
given(this.ctx.search(any(Name.class), eq(encoded), any(SearchControls.class)))
		.willReturn(new MockNamingEnumeration(sr));
...

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Feb 1, 2026
@therepanic
Copy link
Contributor Author

I don't quite understand whether this is breaking Change or not, please take a look at #18627 (comment)

@rwinch
Copy link
Member

rwinch commented Feb 2, 2026

FYI I rebased based off origin/main

@jzheaux jzheaux self-assigned this Feb 18, 2026
@jzheaux jzheaux added in: ldap An issue in spring-security-ldap type: enhancement A general enhancement and removed status: waiting-for-triage An issue we've not yet triaged labels Feb 18, 2026
Replaces SpringSecurityLdapTemplate with LdapClient for user search
operations.

Closes: spring-projectsgh-17291

Signed-off-by: Andrey Litvitski <andrey1010102008@gmail.com>
Copy link
Contributor

@jzheaux jzheaux left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR, @therepanic! I've left some feedback inline.

Comment on lines -100 to +101
given(this.ctx.search(any(Name.class), eq(customSearchFilter), any(Object[].class), any(SearchControls.class)))
given(this.ctx.search(any(Name.class), any(String.class), any(SearchControls.class)))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is okay, though I'd recommend that we still use eq to test the filter so that the expectations are still the same level of specificity:

String customSearchFilter = "(&(objectClass=user)(sAMAccountName={0}))";
String domain = "mydomain.eu";
String encoded = MessageFormat.format(customSearchFilter, this.joe.getPrincipal() + "@" + domain);
...
given(this.ctx.search(any(Name.class), eq(encoded), any(SearchControls.class)))
		.willReturn(new MockNamingEnumeration(sr));
...

Comment on lines 318 to +338
}
catch (org.springframework.ldap.NamingException ex) {
throw badCredentials(ex);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the reason is because SpringSecurityLdapTemplate propagates the javax.naming exception while LdapClient wraps them. For passivity reasons, let's do something closer to:

if (ex.getCause() instanceof NamingException original) {
    throw original;
}
throw badCredentials(ex);

In this way, the original naming exception is propagated like it was before.

.searchScope(SearchScope.SUBTREE)
.filter(this.searchFilter, bindPrincipal, username))
.toEntry();
if (result == null) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like something we should consider as an enhancement to LdapClient; a way to indicate that no results is an error. Will you please file an issue?

try {
return SpringSecurityLdapTemplate.searchForSingleEntryInternal(context, searchControls, searchRoot,
this.searchFilter, new Object[] { bindPrincipal, username });
DirContextOperations result = ldapClient.search()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might be slightly more readable as:

LdapQuery query = LdapQueryBuilder.query()
		.base(searchRoot)
		.searchScope(SearchScope.SUBTREE)
		.filter(this.searchFilter, bindPrincipal, username);
DirContextOperations result = ldapClient.search().query(query).toEntry();

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

in: ldap An issue in spring-security-ldap type: enhancement A general enhancement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Change ActiveDirectoryLdapAuthenticationProvider to use LdapClient

4 participants