Skip to content

Commit 14f3ad5

Browse files
committed
Fixed CLOUDSTACK-7374: added PaginationControl while querying ldap users
1 parent 28ad34e commit 14f3ad5

11 files changed

Lines changed: 356 additions & 332 deletions

File tree

plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapCreateAccountCmd.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,6 @@
2121
import java.util.Map;
2222

2323
import javax.inject.Inject;
24-
import javax.naming.NamingException;
25-
2624
import org.apache.cloudstack.api.APICommand;
2725
import org.apache.cloudstack.api.ApiConstants;
2826
import org.apache.cloudstack.api.ApiErrorCode;
@@ -35,6 +33,7 @@
3533
import org.apache.cloudstack.context.CallContext;
3634
import org.apache.cloudstack.ldap.LdapManager;
3735
import org.apache.cloudstack.ldap.LdapUser;
36+
import org.apache.cloudstack.ldap.NoLdapUserMatchingQueryException;
3837
import org.apache.log4j.Logger;
3938
import org.bouncycastle.util.encoders.Base64;
4039

@@ -134,7 +133,7 @@ public void execute() throws ServerApiException {
134133
} else {
135134
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create a user account");
136135
}
137-
} catch (final NamingException e) {
136+
} catch (NoLdapUserMatchingQueryException e) {
138137
throw new ServerApiException(ApiErrorCode.RESOURCE_UNAVAILABLE_ERROR, "No LDAP user exists with the username of " + username);
139138
}
140139
}

plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapConfiguration.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ public class LdapConfiguration implements Configurable{
3434
private static final ConfigKey<Long> ldapReadTimeout = new ConfigKey<Long>(Long.class, "ldap.read.timeout", "Advanced", "1000",
3535
"LDAP connection Timeout in milli sec", true, ConfigKey.Scope.Global, 1l);
3636

37+
private static final ConfigKey<Integer> ldapPageSize = new ConfigKey<Integer>(Integer.class, "ldap.request.page.size", "Advanced", "1000",
38+
"page size sent to ldap server on each request to get user", true, ConfigKey.Scope.Global, 1);
39+
3740
private final static int scope = SearchControls.SUBTREE_SCOPE;
3841

3942
@Inject
@@ -158,13 +161,17 @@ public Long getReadTimeout() {
158161
return ldapReadTimeout.value();
159162
}
160163

164+
public Integer getLdapPageSize() {
165+
return ldapPageSize.value();
166+
}
167+
161168
@Override
162169
public String getConfigComponentName() {
163170
return LdapConfiguration.class.getSimpleName();
164171
}
165172

166173
@Override
167174
public ConfigKey<?>[] getConfigKeys() {
168-
return new ConfigKey<?>[] {ldapReadTimeout};
175+
return new ConfigKey<?>[] {ldapReadTimeout, ldapPageSize};
169176
}
170177
}

plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapContextFactory.java

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,14 @@
1616
// under the License.
1717
package org.apache.cloudstack.ldap;
1818

19+
import java.io.IOException;
1920
import java.util.Hashtable;
2021

2122
import javax.inject.Inject;
2223
import javax.naming.Context;
2324
import javax.naming.NamingException;
24-
import javax.naming.directory.DirContext;
25-
import javax.naming.directory.InitialDirContext;
25+
import javax.naming.ldap.InitialLdapContext;
26+
import javax.naming.ldap.LdapContext;
2627

2728
import org.apache.log4j.Logger;
2829

@@ -39,27 +40,28 @@ public LdapContextFactory(final LdapConfiguration ldapConfiguration) {
3940
_ldapConfiguration = ldapConfiguration;
4041
}
4142

42-
public DirContext createBindContext() throws NamingException {
43+
public LdapContext createBindContext() throws NamingException, IOException {
4344
return createBindContext(null);
4445
}
4546

46-
public DirContext createBindContext(final String providerUrl) throws NamingException {
47+
public LdapContext createBindContext(final String providerUrl) throws NamingException, IOException {
4748
final String bindPrincipal = _ldapConfiguration.getBindPrincipal();
4849
final String bindPassword = _ldapConfiguration.getBindPassword();
4950
return createInitialDirContext(bindPrincipal, bindPassword, providerUrl, true);
5051
}
5152

52-
private DirContext createInitialDirContext(final String principal, final String password, final boolean isSystemContext) throws NamingException {
53+
private LdapContext createInitialDirContext(final String principal, final String password, final boolean isSystemContext) throws NamingException, IOException {
5354
return createInitialDirContext(principal, password, null, isSystemContext);
5455
}
5556

56-
private DirContext createInitialDirContext(final String principal, final String password, final String providerUrl, final boolean isSystemContext) throws NamingException {
57+
private LdapContext createInitialDirContext(final String principal, final String password, final String providerUrl, final boolean isSystemContext)
58+
throws NamingException, IOException {
5759
Hashtable<String, String> environment = getEnvironment(principal, password, providerUrl, isSystemContext);
5860
s_logger.debug("initializing ldap with provider url: " + environment.get(Context.PROVIDER_URL));
59-
return new InitialDirContext(environment);
61+
return new InitialLdapContext(environment, null);
6062
}
6163

62-
public DirContext createUserContext(final String principal, final String password) throws NamingException {
64+
public LdapContext createUserContext(final String principal, final String password) throws NamingException, IOException {
6365
return createInitialDirContext(principal, password, false);
6466
}
6567

@@ -109,14 +111,4 @@ private void setAuthentication(final Hashtable<String, String> environment, fina
109111
}
110112
}
111113

112-
public void testConnection(final String providerUrl) throws NamingException {
113-
try {
114-
createBindContext(providerUrl);
115-
s_logger.info("LDAP Connection was successful");
116-
} catch (final NamingException e) {
117-
s_logger.warn("LDAP Connection failed");
118-
s_logger.error(e.getMessage(), e);
119-
throw e;
120-
}
121-
}
122114
}

plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapManager.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@
1818

1919
import java.util.List;
2020

21-
import javax.naming.NamingException;
22-
2321
import org.apache.cloudstack.api.command.LdapListConfigurationCmd;
2422
import org.apache.cloudstack.api.response.LdapConfigurationResponse;
2523
import org.apache.cloudstack.api.response.LdapUserResponse;
@@ -40,7 +38,7 @@ public interface LdapManager extends PluggableService {
4038

4139
LdapConfigurationResponse deleteConfiguration(String hostname) throws InvalidParameterValueException;
4240

43-
LdapUser getUser(final String username) throws NamingException;
41+
LdapUser getUser(final String username) throws NoLdapUserMatchingQueryException;
4442

4543
List<LdapUser> getUsers() throws NoLdapUserMatchingQueryException;
4644

plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapManagerImpl.java

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,14 @@
1616
// under the License.
1717
package org.apache.cloudstack.ldap;
1818

19+
import java.io.IOException;
1920
import java.util.ArrayList;
2021
import java.util.List;
2122

2223
import javax.ejb.Local;
2324
import javax.inject.Inject;
2425
import javax.naming.NamingException;
25-
import javax.naming.directory.DirContext;
26+
import javax.naming.ldap.LdapContext;
2627

2728
import org.apache.log4j.Logger;
2829
import org.springframework.stereotype.Component;
@@ -80,7 +81,7 @@ public LdapConfigurationResponse addConfiguration(final String hostname, final i
8081
_ldapConfigurationDao.persist(configuration);
8182
s_logger.info("Added new ldap server with hostname: " + hostname);
8283
return new LdapConfigurationResponse(hostname, port);
83-
} catch (final NamingException e) {
84+
} catch (NamingException | IOException e) {
8485
s_logger.debug("NamingException while doing an LDAP bind", e);
8586
throw new InvalidParameterValueException("Unable to bind to the given LDAP server");
8687
}
@@ -95,17 +96,17 @@ public boolean canAuthenticate(final String username, final String password) {
9596
try {
9697
final LdapUser user = getUser(escapedUsername);
9798
final String principal = user.getPrincipal();
98-
final DirContext context = _ldapContextFactory.createUserContext(principal, password);
99+
final LdapContext context = _ldapContextFactory.createUserContext(principal, password);
99100
closeContext(context);
100101
return true;
101-
} catch (final NamingException e) {
102-
s_logger.debug("NamingException: while doing an LDAP bind for user "+" "+username, e);
102+
} catch (NamingException | IOException | NoLdapUserMatchingQueryException e) {
103+
s_logger.debug("Exception while doing an LDAP bind for user "+" "+username, e);
103104
s_logger.info("Failed to authenticate user: " + username + ". incorrect password.");
104105
return false;
105106
}
106107
}
107108

108-
private void closeContext(final DirContext context) {
109+
private void closeContext(final LdapContext context) {
109110
try {
110111
if (context != null) {
111112
context.close();
@@ -163,29 +164,30 @@ public List<Class<?>> getCommands() {
163164
}
164165

165166
@Override
166-
public LdapUser getUser(final String username) throws NamingException {
167-
DirContext context = null;
167+
public LdapUser getUser(final String username) throws NoLdapUserMatchingQueryException {
168+
LdapContext context = null;
168169
try {
169170
context = _ldapContextFactory.createBindContext();
170171

171172
final String escapedUsername = LdapUtils.escapeLDAPSearchFilter(username);
172173
return _ldapUserManager.getUser(escapedUsername, context);
173174

174-
} catch (final NamingException e) {
175-
throw e;
175+
} catch (NamingException | IOException e) {
176+
s_logger.debug("ldap Exception: ",e);
177+
throw new NoLdapUserMatchingQueryException("No Ldap User found for username: "+username);
176178
} finally {
177179
closeContext(context);
178180
}
179181
}
180182

181183
@Override
182184
public List<LdapUser> getUsers() throws NoLdapUserMatchingQueryException {
183-
DirContext context = null;
185+
LdapContext context = null;
184186
try {
185187
context = _ldapContextFactory.createBindContext();
186188
return _ldapUserManager.getUsers(context);
187-
} catch (final NamingException e) {
188-
s_logger.debug("ldap NamingException: ",e);
189+
} catch (NamingException | IOException e) {
190+
s_logger.debug("ldap Exception: ",e);
189191
throw new NoLdapUserMatchingQueryException("*");
190192
} finally {
191193
closeContext(context);
@@ -194,11 +196,11 @@ public List<LdapUser> getUsers() throws NoLdapUserMatchingQueryException {
194196

195197
@Override
196198
public List<LdapUser> getUsersInGroup(String groupName) throws NoLdapUserMatchingQueryException {
197-
DirContext context = null;
199+
LdapContext context = null;
198200
try {
199201
context = _ldapContextFactory.createBindContext();
200202
return _ldapUserManager.getUsersInGroup(groupName, context);
201-
} catch (final NamingException e) {
203+
} catch (NamingException | IOException e) {
202204
s_logger.debug("ldap NamingException: ",e);
203205
throw new NoLdapUserMatchingQueryException("groupName=" + groupName);
204206
} finally {
@@ -221,13 +223,13 @@ public Pair<List<? extends LdapConfigurationVO>, Integer> listConfigurations(fin
221223

222224
@Override
223225
public List<LdapUser> searchUsers(final String username) throws NoLdapUserMatchingQueryException {
224-
DirContext context = null;
226+
LdapContext context = null;
225227
try {
226228
context = _ldapContextFactory.createBindContext();
227229
final String escapedUsername = LdapUtils.escapeLDAPSearchFilter(username);
228230
return _ldapUserManager.getUsers("*" + escapedUsername + "*", context);
229-
} catch (final NamingException e) {
230-
s_logger.debug("ldap NamingException: ",e);
231+
} catch (NamingException | IOException e) {
232+
s_logger.debug("ldap Exception: ",e);
231233
throw new NoLdapUserMatchingQueryException(username);
232234
} finally {
233235
closeContext(context);

plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapUserManager.java

Lines changed: 49 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
// under the License.
1717
package org.apache.cloudstack.ldap;
1818

19+
import java.io.IOException;
1920
import java.util.ArrayList;
2021
import java.util.Collections;
2122
import java.util.List;
@@ -25,10 +26,14 @@
2526
import javax.naming.NamingException;
2627
import javax.naming.directory.Attribute;
2728
import javax.naming.directory.Attributes;
28-
import javax.naming.directory.DirContext;
2929
import javax.naming.directory.SearchControls;
3030
import javax.naming.directory.SearchResult;
31+
import javax.naming.ldap.Control;
32+
import javax.naming.ldap.LdapContext;
33+
import javax.naming.ldap.PagedResultsControl;
34+
import javax.naming.ldap.PagedResultsResponseControl;
3135

36+
import org.apache.commons.collections.CollectionUtils;
3237
import org.apache.commons.lang.StringUtils;
3338
import org.apache.log4j.Logger;
3439

@@ -113,35 +118,29 @@ private String generateGroupSearchFilter(final String groupName) {
113118
return result.toString();
114119
}
115120

116-
public LdapUser getUser(final String username, final DirContext context) throws NamingException {
117-
final NamingEnumeration<SearchResult> result = searchUsers(username, context);
118-
if (result.hasMoreElements()) {
119-
return createUser(result.nextElement());
121+
public LdapUser getUser(final String username, final LdapContext context) throws NamingException, IOException {
122+
List<LdapUser> result = searchUsers(username, context);
123+
if (result!= null && result.size() == 1) {
124+
return result.get(0);
120125
} else {
121126
throw new NamingException("No user found for username " + username);
122127
}
123128
}
124129

125-
public List<LdapUser> getUsers(final DirContext context) throws NamingException {
130+
public List<LdapUser> getUsers(final LdapContext context) throws NamingException, IOException {
126131
return getUsers(null, context);
127132
}
128133

129-
public List<LdapUser> getUsers(final String username, final DirContext context) throws NamingException {
130-
final NamingEnumeration<SearchResult> results = searchUsers(username, context);
134+
public List<LdapUser> getUsers(final String username, final LdapContext context) throws NamingException, IOException {
135+
List<LdapUser> users = searchUsers(username, context);
131136

132-
final List<LdapUser> users = new ArrayList<LdapUser>();
133-
134-
while (results.hasMoreElements()) {
135-
final SearchResult result = results.nextElement();
136-
users.add(createUser(result));
137+
if (CollectionUtils.isNotEmpty(users)) {
138+
Collections.sort(users);
137139
}
138-
139-
Collections.sort(users);
140-
141140
return users;
142141
}
143142

144-
public List<LdapUser> getUsersInGroup(String groupName, DirContext context) throws NamingException {
143+
public List<LdapUser> getUsersInGroup(String groupName, LdapContext context) throws NamingException {
145144
String attributeName = _ldapConfiguration.getGroupUniqueMemeberAttribute();
146145
final SearchControls controls = new SearchControls();
147146
controls.setSearchScope(_ldapConfiguration.getScope());
@@ -170,7 +169,7 @@ public List<LdapUser> getUsersInGroup(String groupName, DirContext context) thro
170169
return users;
171170
}
172171

173-
private LdapUser getUserForDn(String userdn, DirContext context) throws NamingException {
172+
private LdapUser getUserForDn(String userdn, LdapContext context) throws NamingException {
174173
final SearchControls controls = new SearchControls();
175174
controls.setSearchScope(_ldapConfiguration.getScope());
176175
controls.setReturningAttributes(_ldapConfiguration.getReturnAttributes());
@@ -183,20 +182,46 @@ private LdapUser getUserForDn(String userdn, DirContext context) throws NamingEx
183182
}
184183
}
185184

186-
public NamingEnumeration<SearchResult> searchUsers(final DirContext context) throws NamingException {
185+
public List<LdapUser> searchUsers(final LdapContext context) throws NamingException, IOException {
187186
return searchUsers(null, context);
188187
}
189188

190-
public NamingEnumeration<SearchResult> searchUsers(final String username, final DirContext context) throws NamingException {
191-
final SearchControls controls = new SearchControls();
189+
public List<LdapUser> searchUsers(final String username, final LdapContext context) throws NamingException, IOException {
192190

193-
controls.setSearchScope(_ldapConfiguration.getScope());
194-
controls.setReturningAttributes(_ldapConfiguration.getReturnAttributes());
191+
final SearchControls searchControls = new SearchControls();
192+
193+
searchControls.setSearchScope(_ldapConfiguration.getScope());
194+
searchControls.setReturningAttributes(_ldapConfiguration.getReturnAttributes());
195195

196196
String basedn = _ldapConfiguration.getBaseDn();
197197
if (StringUtils.isBlank(basedn)) {
198198
throw new IllegalArgumentException("ldap basedn is not configured");
199199
}
200-
return context.search(basedn, generateSearchFilter(username), controls);
200+
byte[] cookie = null;
201+
int pageSize = _ldapConfiguration.getLdapPageSize();
202+
context.setRequestControls(new Control[]{new PagedResultsControl(pageSize, Control.NONCRITICAL)});
203+
final List<LdapUser> users = new ArrayList<LdapUser>();
204+
NamingEnumeration<SearchResult> results;
205+
do {
206+
results = context.search(basedn, generateSearchFilter(username), searchControls);
207+
while (results.hasMoreElements()) {
208+
final SearchResult result = results.nextElement();
209+
users.add(createUser(result));
210+
}
211+
Control[] contextControls = context.getResponseControls();
212+
if (contextControls != null) {
213+
for (Control control : contextControls) {
214+
if (control instanceof PagedResultsResponseControl) {
215+
PagedResultsResponseControl prrc = (PagedResultsResponseControl) control;
216+
cookie = prrc.getCookie();
217+
}
218+
}
219+
} else {
220+
s_logger.info("No controls were sent from the ldap server");
221+
}
222+
context.setRequestControls(new Control[] {new PagedResultsControl(pageSize, cookie, Control.CRITICAL)});
223+
} while (cookie != null);
224+
225+
return users;
201226
}
202227
}

0 commit comments

Comments
 (0)