GUACAMOLE-220: Define generic service for executing LDAP queries. Refactor existing services to remove common code.
Project: http://git-wip-us.apache.org/repos/asf/guacamole-client/repo Commit: http://git-wip-us.apache.org/repos/asf/guacamole-client/commit/7c57b448 Tree: http://git-wip-us.apache.org/repos/asf/guacamole-client/tree/7c57b448 Diff: http://git-wip-us.apache.org/repos/asf/guacamole-client/diff/7c57b448 Branch: refs/heads/master Commit: 7c57b448bbd6a76018a3fd531950b952ac94dca0 Parents: 929c7de Author: Michael Jumper <mjum...@apache.org> Authored: Fri Nov 2 15:03:56 2018 -0700 Committer: Michael Jumper <mjum...@apache.org> Committed: Sat Nov 3 12:41:54 2018 -0700 ---------------------------------------------------------------------- .../ldap/LDAPAuthenticationProviderModule.java | 1 + .../guacamole/auth/ldap/ObjectQueryService.java | 326 +++++++++++++++++++ .../auth/ldap/connection/ConnectionService.java | 123 +++---- .../guacamole/auth/ldap/user/UserService.java | 232 ++----------- 4 files changed, 410 insertions(+), 272 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/7c57b448/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/LDAPAuthenticationProviderModule.java ---------------------------------------------------------------------- diff --git a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/LDAPAuthenticationProviderModule.java b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/LDAPAuthenticationProviderModule.java index ead26bc..5478080 100644 --- a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/LDAPAuthenticationProviderModule.java +++ b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/LDAPAuthenticationProviderModule.java @@ -77,6 +77,7 @@ public class LDAPAuthenticationProviderModule extends AbstractModule { bind(ConnectionService.class); bind(EscapingService.class); bind(LDAPConnectionService.class); + bind(ObjectQueryService.class); bind(UserService.class); } http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/7c57b448/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/ObjectQueryService.java ---------------------------------------------------------------------- diff --git a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/ObjectQueryService.java b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/ObjectQueryService.java new file mode 100644 index 0000000..89a43c3 --- /dev/null +++ b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/ObjectQueryService.java @@ -0,0 +1,326 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.guacamole.auth.ldap; + +import com.google.inject.Inject; +import com.novell.ldap.LDAPAttribute; +import com.novell.ldap.LDAPConnection; +import com.novell.ldap.LDAPEntry; +import com.novell.ldap.LDAPException; +import com.novell.ldap.LDAPReferralException; +import com.novell.ldap.LDAPSearchResults; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.GuacamoleServerException; +import org.apache.guacamole.net.auth.Identifiable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Service for executing queries against an LDAP directory intended to retrieve + * Guacamole-related objects. Referrals are automatically handled. Convenience + * functions are provided for generating the LDAP queries typically required + * for retrieving Guacamole objects, as well as for converting the results of a + * query into a Map of Guacamole objects. + */ +public class ObjectQueryService { + + /** + * Logger for this class. + */ + private final Logger logger = LoggerFactory.getLogger(ObjectQueryService.class); + + /** + * Service for escaping parts of LDAP queries. + */ + @Inject + private EscapingService escapingService; + + /** + * Service for retrieving LDAP server configuration information. + */ + @Inject + private ConfigurationService confService; + + /** + * Returns the identifier of the object represented by the given LDAP + * entry. Multiple attributes may be declared as containing the identifier + * of the object when present on an LDAP entry. If multiple such attributes + * are present on the same LDAP entry, the value of the attribute with + * highest priority is used. If multiple copies of the same attribute are + * present on the same LDAPentry, the first value of that attribute is + * used. + * + * @param entry + * The entry representing the Guacamole object whose unique identifier + * should be determined. + * + * @param attributes + * A collection of all attributes which may be used to specify the + * unique identifier of the Guacamole object represented by an LDAP + * entry, in order of decreasing priority. + * + * @return + * The identifier of the object represented by the given LDAP entry, or + * null if no attributes declared as containing the identifier of the + * object are present on the entry. + */ + public String getIdentifier(LDAPEntry entry, Collection<String> attributes) { + + // Retrieve the first value of the highest priority identifier attribute + for (String identifierAttribute : attributes) { + LDAPAttribute identifier = entry.getAttribute(identifierAttribute); + if (identifier != null) + return identifier.getStringValue(); + } + + // No identifier attribute is present on the entry + return null; + + } + + /** + * Generates a properly-escaped LDAP query which finds all objects which + * match the given LDAP filter and which have at least one of the given + * attributes set to the specified value. + * + * @param filter + * The LDAP filter to apply to reduce the results of the query in + * addition to testing the values of the given attributes. + * + * @param attributes + * A collection of all attributes to test for equivalence to the given + * value, in order of decreasing priority. + * + * @param attributeValue + * The value that the resulting LDAP query should search for within the + * attributes of objects within the LDAP directory. If null, the + * resulting LDAP query will search for the presence of at least one of + * the given attributes on each object, regardless of the value of + * those attributes. + * + * @return + * An LDAP query which will search for arbitrary LDAP objects having at + * least one of the given attributes set to the specified value. + */ + public String generateQuery(String filter, + Collection<String> attributes, String attributeValue) { + + // Build LDAP query for objects having at least one attribute and with + // the given search filter + StringBuilder ldapQuery = new StringBuilder(); + ldapQuery.append("(&"); + ldapQuery.append(filter); + + // Include all attributes within OR clause if there are more than one + if (attributes.size() > 1) + ldapQuery.append("(|"); + + // Add equality comparison for each possible attribute + for (String attribute : attributes) { + ldapQuery.append("("); + ldapQuery.append(escapingService.escapeLDAPSearchFilter(attribute)); + + if (attributeValue != null) { + ldapQuery.append("="); + ldapQuery.append(escapingService.escapeLDAPSearchFilter(attributeValue)); + ldapQuery.append(")"); + } + else + ldapQuery.append("=*)"); + + } + + // Close OR clause, if any + if (attributes.size() > 1) + ldapQuery.append(")"); + + // Close overall query (AND clause) + ldapQuery.append(")"); + + return ldapQuery.toString(); + + } + + /** + * Executes an arbitrary LDAP query using the given connection, returning a + * list of all results. Only objects beneath the given base DN are + * included in the search. + * + * @param ldapConnection + * The current connection to the LDAP server, associated with the + * current user. + * + * @param baseDN + * The base DN to search using the given LDAP query. + * + * @param query + * The LDAP query to execute. + * + * @return + * A list of all results accessible to the user currently bound under + * the given LDAP connection. + * + * @throws GuacamoleException + * If an error occurs executing the query, or if configuration + * information required to execute the query cannot be read from + * guacamole.properties. + */ + public List<LDAPEntry> search(LDAPConnection ldapConnection, + String baseDN, String query) throws GuacamoleException { + + logger.debug("Searching \"{}\" for objects matching \"{}\".", baseDN, query); + + try { + + // Search within subtree of given base DN + LDAPSearchResults results = ldapConnection.search(baseDN, + LDAPConnection.SCOPE_SUB, query, null, false, + confService.getLDAPSearchConstraints()); + + // Produce list of all entries in the search result, automatically + // following referrals if configured to do so + List<LDAPEntry> entries = new ArrayList<>(results.getCount()); + while (results.hasMore()) { + + try { + entries.add(results.next()); + } + + // Warn if referrals cannot be followed + catch (LDAPReferralException e) { + if (confService.getFollowReferrals()) { + logger.error("Could not follow referral: {}", e.getFailedReferral()); + logger.debug("Error encountered trying to follow referral.", e); + throw new GuacamoleServerException("Could not follow LDAP referral.", e); + } + else { + logger.warn("Given a referral, but referrals are disabled. Error was: {}", e.getMessage()); + logger.debug("Got a referral, but configured to not follow them.", e); + } + } + + } + + return entries; + + } + catch (LDAPException | GuacamoleException e) { + throw new GuacamoleServerException("Unable to query list of " + + "objects from LDAP directory.", e); + } + + } + + /** + * Executes the query which would be returned by generateQuery() using the + * given connection, returning a list of all results. Only objects beneath + * the given base DN are included in the search. + * + * @param ldapConnection + * The current connection to the LDAP server, associated with the + * current user. + * + * @param baseDN + * The base DN to search using the given LDAP query. + * + * @param filter + * The LDAP filter to apply to reduce the results of the query in + * addition to testing the values of the given attributes. + * + * @param attributes + * A collection of all attributes to test for equivalence to the given + * value, in order of decreasing priority. + * + * @param attributeValue + * The value that should be searched search for within the attributes + * of objects within the LDAP directory. If null, the search will test + * only for the presence of at least one of the given attributes on + * each object, regardless of the value of those attributes. + * + * @return + * A list of all results accessible to the user currently bound under + * the given LDAP connection. + * + * @throws GuacamoleException + * If an error occurs executing the query, or if configuration + * information required to execute the query cannot be read from + * guacamole.properties. + */ + public List<LDAPEntry> search(LDAPConnection ldapConnection, String baseDN, + String filter, Collection<String> attributes, String attributeValue) + throws GuacamoleException { + String query = generateQuery(filter, attributes, attributeValue); + return search(ldapConnection, baseDN, query); + } + + /** + * Converts a given list of LDAP entries to a map of Guacamole objects + * stored by their identifiers. + * + * @param <ObjectType> + * The type of object to store within the map. + * + * @param entries + * A list of LDAP entries to convert to Guacamole objects. + * + * @param mapper + * A mapping function which converts a given LDAP entry to its + * corresponding Guacamole object. If the LDAP entry cannot be + * converted, null should be returned. + * + * @return + * A new map containing Guacamole object versions of each of the given + * LDAP entries, where each object is stored within the map under its + * corresponding identifier. + */ + public <ObjectType extends Identifiable> Map<String, ObjectType> + asMap(List<LDAPEntry> entries, Function<LDAPEntry, ObjectType> mapper) { + + // Convert each entry to the corresponding Guacamole API object + Map<String, ObjectType> objects = new HashMap<>(entries.size()); + for (LDAPEntry entry : entries) { + + ObjectType object = mapper.apply(entry); + if (object == null) { + logger.debug("Ignoring object \"{}\".", entry.getDN()); + continue; + } + + // Attempt to add object to map, warning if the object appears + // to be a duplicate + String identifier = object.getIdentifier(); + if (objects.putIfAbsent(identifier, object) != null) + logger.warn("Multiple objects ambiguously map to the " + + "same identifier (\"{}\"). Ignoring \"{}\" as " + + "a duplicate.", identifier, entry.getDN()); + + } + + return objects; + + } + +} http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/7c57b448/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/connection/ConnectionService.java ---------------------------------------------------------------------- diff --git a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/connection/ConnectionService.java b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/connection/ConnectionService.java index 3ce00e3..78100a0 100644 --- a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/connection/ConnectionService.java +++ b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/connection/ConnectionService.java @@ -28,13 +28,14 @@ import com.novell.ldap.LDAPReferralException; import com.novell.ldap.LDAPSearchResults; import java.util.Collections; import java.util.Enumeration; -import java.util.HashMap; +import java.util.List; import java.util.Map; import org.apache.guacamole.auth.ldap.LDAPAuthenticationProvider; import org.apache.guacamole.auth.ldap.ConfigurationService; import org.apache.guacamole.auth.ldap.EscapingService; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleServerException; +import org.apache.guacamole.auth.ldap.ObjectQueryService; import org.apache.guacamole.net.auth.AuthenticatedUser; import org.apache.guacamole.net.auth.Connection; import org.apache.guacamole.net.auth.simple.SimpleConnection; @@ -68,6 +69,12 @@ public class ConnectionService { private ConfigurationService confService; /** + * Service for executing LDAP queries. + */ + @Inject + private ObjectQueryService queryService; + + /** * Returns all Guacamole connections accessible to the user currently bound * under the given LDAP connection. * @@ -113,101 +120,71 @@ public class ConnectionService { // looking for direct membership in the guacConfigGroup // and possibly any groups the user is a member of that are // referred to in the seeAlso attribute of the guacConfigGroup. - LDAPSearchResults results = ldapConnection.search( - configurationBaseDN, - LDAPConnection.SCOPE_SUB, - connectionSearchFilter, - null, - false, - confService.getLDAPSearchConstraints() - ); + List<LDAPEntry> results = queryService.search(ldapConnection, configurationBaseDN, connectionSearchFilter); // Build token filter containing credential tokens TokenFilter tokenFilter = new TokenFilter(); StandardTokens.addStandardTokens(tokenFilter, user); - // Produce connections for each readable configuration - Map<String, Connection> connections = new HashMap<String, Connection>(); - while (results.hasMore()) { - - try { - - LDAPEntry entry = results.next(); + // Return a map of all readable connections + return queryService.asMap(results, (entry) -> { - // Get common name (CN) - LDAPAttribute cn = entry.getAttribute("cn"); - if (cn == null) { - logger.warn("guacConfigGroup is missing a cn."); - continue; - } - - // Get associated protocol - LDAPAttribute protocol = entry.getAttribute("guacConfigProtocol"); - if (protocol == null) { - logger.warn("guacConfigGroup \"{}\" is missing the " - + "required \"guacConfigProtocol\" attribute.", - cn.getStringValue()); - continue; - } + // Get common name (CN) + LDAPAttribute cn = entry.getAttribute("cn"); + if (cn == null) { + logger.warn("guacConfigGroup is missing a cn."); + return null; + } - // Set protocol - GuacamoleConfiguration config = new GuacamoleConfiguration(); - config.setProtocol(protocol.getStringValue()); + // Get associated protocol + LDAPAttribute protocol = entry.getAttribute("guacConfigProtocol"); + if (protocol == null) { + logger.warn("guacConfigGroup \"{}\" is missing the " + + "required \"guacConfigProtocol\" attribute.", + cn.getStringValue()); + return null; + } - // Get parameters, if any - LDAPAttribute parameterAttribute = entry.getAttribute("guacConfigParameter"); - if (parameterAttribute != null) { + // Set protocol + GuacamoleConfiguration config = new GuacamoleConfiguration(); + config.setProtocol(protocol.getStringValue()); - // For each parameter - Enumeration<?> parameters = parameterAttribute.getStringValues(); - while (parameters.hasMoreElements()) { + // Get parameters, if any + LDAPAttribute parameterAttribute = entry.getAttribute("guacConfigParameter"); + if (parameterAttribute != null) { - String parameter = (String) parameters.nextElement(); + // For each parameter + Enumeration<?> parameters = parameterAttribute.getStringValues(); + while (parameters.hasMoreElements()) { - // Parse parameter - int equals = parameter.indexOf('='); - if (equals != -1) { + String parameter = (String) parameters.nextElement(); - // Parse name - String name = parameter.substring(0, equals); - String value = parameter.substring(equals+1); + // Parse parameter + int equals = parameter.indexOf('='); + if (equals != -1) { - config.setParameter(name, value); + // Parse name + String name = parameter.substring(0, equals); + String value = parameter.substring(equals+1); - } + config.setParameter(name, value); } } - // Filter the configuration, substituting all defined tokens - tokenFilter.filterValues(config.getParameters()); - - // Store connection using cn for both identifier and name - String name = cn.getStringValue(); - Connection connection = new SimpleConnection(name, name, config); - connection.setParentIdentifier(LDAPAuthenticationProvider.ROOT_CONNECTION_GROUP); - connections.put(name, connection); - } - // Deal with issues following LDAP referrals - catch (LDAPReferralException e) { - if (confService.getFollowReferrals()) { - logger.error("Could not follow referral: {}", e.getFailedReferral()); - logger.debug("Error encountered trying to follow referral.", e); - throw new GuacamoleServerException("Could not follow LDAP referral.", e); - } - else { - logger.warn("Given a referral, but referrals are disabled. Error was: {}", e.getMessage()); - logger.debug("Got a referral, but configured to not follow them.", e); - } - } + // Filter the configuration, substituting all defined tokens + tokenFilter.filterValues(config.getParameters()); - } + // Store connection using cn for both identifier and name + String name = cn.getStringValue(); + Connection connection = new SimpleConnection(name, name, config); + connection.setParentIdentifier(LDAPAuthenticationProvider.ROOT_CONNECTION_GROUP); + return connection; - // Return map of all connections - return connections; + }); } catch (LDAPException e) { http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/7c57b448/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/user/UserService.java ---------------------------------------------------------------------- diff --git a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/user/UserService.java b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/user/UserService.java index 9d27f1e..3f12ae8 100644 --- a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/user/UserService.java +++ b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/user/UserService.java @@ -20,21 +20,17 @@ package org.apache.guacamole.auth.ldap.user; import com.google.inject.Inject; -import com.novell.ldap.LDAPAttribute; import com.novell.ldap.LDAPConnection; import com.novell.ldap.LDAPEntry; -import com.novell.ldap.LDAPException; -import com.novell.ldap.LDAPReferralException; -import com.novell.ldap.LDAPSearchResults; import java.util.ArrayList; -import java.util.HashMap; +import java.util.Collection; import java.util.List; import java.util.Map; import org.apache.guacamole.auth.ldap.ConfigurationService; import org.apache.guacamole.auth.ldap.EscapingService; import org.apache.guacamole.GuacamoleException; -import org.apache.guacamole.GuacamoleServerException; import org.apache.guacamole.auth.ldap.LDAPGuacamoleProperties; +import org.apache.guacamole.auth.ldap.ObjectQueryService; import org.apache.guacamole.net.auth.User; import org.apache.guacamole.net.auth.simple.SimpleUser; import org.slf4j.Logger; @@ -64,89 +60,10 @@ public class UserService { private ConfigurationService confService; /** - * Adds all Guacamole users accessible to the user currently bound under - * the given LDAP connection to the provided map. Only users with the - * specified attribute are added. If the same username is encountered - * multiple times, warnings about possible ambiguity will be logged. - * - * @param ldapConnection - * The current connection to the LDAP server, associated with the - * current user. - * - * @return - * All users accessible to the user currently bound under the given - * LDAP connection, as a map of connection identifier to corresponding - * user object. - * - * @throws GuacamoleException - * If an error occurs preventing retrieval of users. + * Service for executing LDAP queries. */ - private void putAllUsers(Map<String, User> users, LDAPConnection ldapConnection, - String usernameAttribute) throws GuacamoleException { - - try { - - // Build a filter using the configured or default user search filter - // to find all user objects in the LDAP tree - StringBuilder userSearchFilter = new StringBuilder(); - userSearchFilter.append("(&"); - userSearchFilter.append(confService.getUserSearchFilter()); - userSearchFilter.append("("); - userSearchFilter.append(escapingService.escapeLDAPSearchFilter(usernameAttribute)); - userSearchFilter.append("=*))"); - - // Find all Guacamole users underneath base DN - LDAPSearchResults results = ldapConnection.search( - confService.getUserBaseDN(), - LDAPConnection.SCOPE_SUB, - userSearchFilter.toString(), - null, - false, - confService.getLDAPSearchConstraints() - ); - - // Read all visible users - while (results.hasMore()) { - - try { - - LDAPEntry entry = results.next(); - - // Get username from record - LDAPAttribute username = entry.getAttribute(usernameAttribute); - if (username == null) { - logger.warn("Queried user is missing the username attribute \"{}\".", usernameAttribute); - continue; - } - - // Store user using their username as the identifier - String identifier = username.getStringValue(); - if (users.put(identifier, new SimpleUser(identifier)) != null) - logger.warn("Possibly ambiguous user account: \"{}\".", identifier); - - } - - // Deal with errors trying to follow referrals - catch (LDAPReferralException e) { - if (confService.getFollowReferrals()) { - logger.error("Could not follow referral: {}", e.getFailedReferral()); - logger.debug("Error encountered trying to follow referral.", e); - throw new GuacamoleServerException("Could not follow LDAP referral.", e); - } - else { - logger.warn("Given a referral, but referrals are disabled. Error was: {}", e.getMessage()); - logger.debug("Got a referral, but configured to not follow them.", e); - } - } - - } - - } - catch (LDAPException e) { - throw new GuacamoleServerException("Error while querying users.", e); - } - - } + @Inject + private ObjectQueryService queryService; /** * Returns all Guacamole users accessible to the user currently bound under @@ -167,80 +84,28 @@ public class UserService { public Map<String, User> getUsers(LDAPConnection ldapConnection) throws GuacamoleException { - // Build map of users by querying each username attribute separately - Map<String, User> users = new HashMap<String, User>(); - for (String usernameAttribute : confService.getUsernameAttributes()) { - - // Attempt to pull all users with given attribute - try { - putAllUsers(users, ldapConnection, usernameAttribute); - } - - // Log any errors non-fatally - catch (GuacamoleException e) { - logger.warn("Could not query list of all users for attribute \"{}\": {}", - usernameAttribute, e.getMessage()); - logger.debug("Error querying list of all users.", e); + // Retrieve all visible user objects + Collection<String> attributes = confService.getUsernameAttributes(); + List<LDAPEntry> results = queryService.search(ldapConnection, + confService.getUserBaseDN(), + confService.getUserSearchFilter(), + attributes, + null); + + // Convert retrieved users to map of identifier to Guacamole user object + return queryService.asMap(results, entry -> { + + // Get username from record + String username = queryService.getIdentifier(entry, attributes); + if (username == null) { + logger.warn("User \"{}\" is missing a username attribute " + + "and will be ignored.", entry.getDN()); + return null; } - } + return new SimpleUser(username); - // Return map of all users - return users; - - } - - /** - * Generates a properly-escaped LDAP query which finds all objects having - * at least one username attribute set to the specified username, where - * the possible username attributes are defined within - * guacamole.properties. - * - * @param username - * The username that the resulting LDAP query should search for within - * objects within the LDAP directory. - * - * @return - * An LDAP query which will search for arbitrary LDAP objects - * containing at least one username attribute set to the specified - * username. - * - * @throws GuacamoleException - * If the LDAP query cannot be generated because the list of username - * attributes cannot be parsed from guacamole.properties. - */ - private String generateLDAPQuery(String username) - throws GuacamoleException { - - List<String> usernameAttributes = confService.getUsernameAttributes(); - - // Build LDAP query for users having at least one username attribute - // and with the configured or default search filter - StringBuilder ldapQuery = new StringBuilder(); - ldapQuery.append("(&"); - ldapQuery.append(confService.getUserSearchFilter()); - - // Include all attributes within OR clause if there are more than one - if (usernameAttributes.size() > 1) - ldapQuery.append("(|"); - - // Add equality comparison for each possible username attribute - for (String usernameAttribute : usernameAttributes) { - ldapQuery.append("("); - ldapQuery.append(escapingService.escapeLDAPSearchFilter(usernameAttribute)); - ldapQuery.append("="); - ldapQuery.append(escapingService.escapeLDAPSearchFilter(username)); - ldapQuery.append(")"); - } - - // Close OR clause, if any - if (usernameAttributes.size() > 1) - ldapQuery.append(")"); - - // Close overall query (AND clause) - ldapQuery.append(")"); - - return ldapQuery.toString(); + }); } @@ -268,49 +133,18 @@ public class UserService { public List<String> getUserDNs(LDAPConnection ldapConnection, String username) throws GuacamoleException { - try { - - List<String> userDNs = new ArrayList<String>(); - - // Find all Guacamole users underneath base DN and matching the - // specified username - LDAPSearchResults results = ldapConnection.search( + // Retrieve user objects having a matching username + List<LDAPEntry> results = queryService.search(ldapConnection, confService.getUserBaseDN(), - LDAPConnection.SCOPE_SUB, - generateLDAPQuery(username), - null, - false, - confService.getLDAPSearchConstraints() - ); + confService.getUserSearchFilter(), + confService.getUsernameAttributes(), + username); - // Add all DNs for found users - while (results.hasMore()) { - try { - LDAPEntry entry = results.next(); - userDNs.add(entry.getDN()); - } - - // Deal with errors following referrals - catch (LDAPReferralException e) { - if (confService.getFollowReferrals()) { - logger.error("Error trying to follow a referral: {}", e.getFailedReferral()); - logger.debug("Encountered an error trying to follow a referral.", e); - throw new GuacamoleServerException("Failed while trying to follow referrals.", e); - } - else { - logger.warn("Given a referral, not following it. Error was: {}", e.getMessage()); - logger.debug("Given a referral, but configured to not follow them.", e); - } - } - } + // Build list of all DNs for retrieved users + List<String> userDNs = new ArrayList<>(results.size()); + results.forEach(entry -> userDNs.add(entry.getDN())); - // Return all discovered DNs (if any) - return userDNs; - - } - catch (LDAPException e) { - throw new GuacamoleServerException("Error while query user DNs.", e); - } + return userDNs; }