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/staging/1.0.0
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;
 
     }
 

Reply via email to