diff --git a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/AuthenticationProviderService.java b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/AuthenticationProviderService.java index 833674c1b..856a536e0 100644 --- a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/AuthenticationProviderService.java +++ b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/AuthenticationProviderService.java @@ -35,6 +35,7 @@ import org.apache.directory.ldap.client.api.LdapNetworkConnection; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleServerException; import org.apache.guacamole.auth.ldap.conf.ConfigurationService; +import org.apache.guacamole.auth.ldap.conf.LDAPConfiguration; import org.apache.guacamole.auth.ldap.group.UserGroupService; import org.apache.guacamole.auth.ldap.user.LDAPAuthenticatedUser; import org.apache.guacamole.auth.ldap.user.LDAPUserContext; @@ -105,6 +106,9 @@ public class AuthenticationProviderService { * or queried from the LDAP server, depending on how LDAP authentication * has been configured. * + * @param config + * The configuration of the LDAP server being queried. + * * @param username * The username of the user whose corresponding DN should be returned. * @@ -115,18 +119,17 @@ public class AuthenticationProviderService { * If required properties are missing, and thus the user DN cannot be * determined. */ - private Dn getUserBindDN(String username) throws GuacamoleException { + private Dn getUserBindDN(LDAPConfiguration config, String username) + throws GuacamoleException { // If a search DN is provided, search the LDAP directory for the DN // corresponding to the given username - String searchBindLogon = confService.getSearchBindDN(); + String searchBindLogon = config.getSearchBindDN(); if (searchBindLogon != null) { // Create an LDAP connection using the search account - LdapNetworkConnection searchConnection = ldapService.bindAs( - searchBindLogon, - confService.getSearchBindPassword() - ); + LdapNetworkConnection searchConnection = ldapService.bindAs(config, + searchBindLogon, config.getSearchBindPassword()); // Warn of failure to find if (searchConnection == null) { @@ -138,7 +141,7 @@ public class AuthenticationProviderService { try { // Retrieve all DNs associated with the given username - List userDNs = userService.getUserDNs(searchConnection, username); + List userDNs = userService.getUserDNs(config, searchConnection, username); if (userDNs.isEmpty()) return null; @@ -161,7 +164,7 @@ public class AuthenticationProviderService { } // Otherwise, derive user DN from base DN - return userService.deriveUserDN(username); + return userService.deriveUserDN(config, username); } @@ -196,8 +199,15 @@ public class AuthenticationProviderService { "Anonymous bind is not currently allowed by the LDAP" + " authentication provider.", CredentialsInfo.USERNAME_PASSWORD); } - - Dn bindDn = getUserBindDN(username); + + // Get relevant LDAP configuration for user + LDAPConfiguration config = confService.getLDAPConfiguration(username); + if (config == null) { + throw new GuacamoleInvalidCredentialsException("User \"" + username + "\" " + + "does not map to any defined LDAP configuration.", CredentialsInfo.USERNAME_PASSWORD); + } + + Dn bindDn = getUserBindDN(config, username); if (bindDn == null || bindDn.isEmpty()) { throw new GuacamoleInvalidCredentialsException("Unable to determine" + " DN of user " + username, CredentialsInfo.USERNAME_PASSWORD); @@ -205,7 +215,7 @@ public class AuthenticationProviderService { // Attempt bind LdapNetworkConnection ldapConnection = - ldapService.bindAs(bindDn.getName(), password); + ldapService.bindAs(config, bindDn.getName(), password); if (ldapConnection == null) throw new GuacamoleInvalidCredentialsException("Invalid login.", CredentialsInfo.USERNAME_PASSWORD); @@ -214,13 +224,14 @@ public class AuthenticationProviderService { // Retrieve group membership of the user that just authenticated Set effectiveGroups = - userGroupService.getParentUserGroupIdentifiers(ldapConnection, - bindDn); + userGroupService.getParentUserGroupIdentifiers(config, + ldapConnection, bindDn); // Return AuthenticatedUser if bind succeeds LDAPAuthenticatedUser authenticatedUser = authenticatedUserProvider.get(); - authenticatedUser.init(credentials, getAttributeTokens(ldapConnection, - bindDn), effectiveGroups, bindDn); + authenticatedUser.init(config, credentials, + getAttributeTokens(config, ldapConnection, bindDn), + effectiveGroups, bindDn); return authenticatedUser; @@ -240,6 +251,9 @@ public class AuthenticationProviderService { * guacamole.properties. If no attributes are specified or none are * found on the LDAP user object, an empty map is returned. * + * @param config + * The configuration of the LDAP server being queried. + * * @param ldapConnection * LDAP connection to use to read the attributes of the user. * @@ -255,11 +269,11 @@ public class AuthenticationProviderService { * @throws GuacamoleException * If an error occurs retrieving the user DN or the attributes. */ - private Map getAttributeTokens(LdapNetworkConnection ldapConnection, - Dn userDn) throws GuacamoleException { + private Map getAttributeTokens(LDAPConfiguration config, + LdapNetworkConnection ldapConnection, Dn userDn) throws GuacamoleException { // Get attributes from configuration information - List attrList = confService.getAttributes(); + List attrList = config.getAttributes(); // If there are no attributes there is no reason to search LDAP if (attrList.isEmpty()) @@ -316,9 +330,11 @@ public class AuthenticationProviderService { Credentials credentials = authenticatedUser.getCredentials(); if (authenticatedUser instanceof LDAPAuthenticatedUser) { - Dn bindDn = ((LDAPAuthenticatedUser) authenticatedUser).getBindDn(); - LdapNetworkConnection ldapConnection = - ldapService.bindAs(bindDn.getName(), credentials.getPassword()); + LDAPAuthenticatedUser ldapAuthenticatedUser = (LDAPAuthenticatedUser) authenticatedUser; + LDAPConfiguration config = ldapAuthenticatedUser.getLDAPConfiguration(); + Dn bindDn = ldapAuthenticatedUser.getBindDn(); + + LdapNetworkConnection ldapConnection = ldapService.bindAs(config, bindDn.getName(), credentials.getPassword()); if (ldapConnection == null) { logger.debug("LDAP bind succeeded for \"{}\" during " + "authentication but failed during data retrieval.", @@ -331,7 +347,7 @@ public class AuthenticationProviderService { // Build user context by querying LDAP LDAPUserContext userContext = userContextProvider.get(); - userContext.init(authenticatedUser, ldapConnection); + userContext.init(ldapAuthenticatedUser, ldapConnection); return userContext; } diff --git a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/LDAPConnectionService.java b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/LDAPConnectionService.java index d93ae23a8..f9d2a0513 100644 --- a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/LDAPConnectionService.java +++ b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/LDAPConnectionService.java @@ -19,7 +19,6 @@ package org.apache.guacamole.auth.ldap; -import com.google.inject.Inject; import org.apache.directory.api.ldap.model.exception.LdapAuthenticationException; import org.apache.directory.api.ldap.model.exception.LdapException; import org.apache.directory.api.ldap.model.exception.LdapInvalidDnException; @@ -34,8 +33,8 @@ import org.apache.directory.ldap.client.api.LdapNetworkConnection; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleServerException; import org.apache.guacamole.GuacamoleUnsupportedException; -import org.apache.guacamole.auth.ldap.conf.ConfigurationService; import org.apache.guacamole.auth.ldap.conf.EncryptionMethod; +import org.apache.guacamole.auth.ldap.conf.LDAPConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -49,12 +48,6 @@ public class LDAPConnectionService { */ private static final Logger logger = LoggerFactory.getLogger(LDAPConnectionService.class); - /** - * Service for retrieving LDAP server configuration information. - */ - @Inject - private ConfigurationService confService; - /** * Creates a new instance of LdapNetworkConnection, configured as required * to use the given encryption method to communicate with the LDAP server @@ -130,6 +123,9 @@ public class LDAPConnectionService { * requested, and will not be connected until it is used in an LDAP * operation (such as a bind). * + * @param config + * The configuration of the LDAP server being queried. + * * @return * A new LdapNetworkConnection instance which has already been * configured to use the encryption method, hostname, and port @@ -139,12 +135,12 @@ public class LDAPConnectionService { * If an error occurs while parsing guacamole.properties, or if the * requested encryption method is actually not implemented (a bug). */ - private LdapNetworkConnection createLDAPConnection() + private LdapNetworkConnection createLDAPConnection(LDAPConfiguration config) throws GuacamoleException { return createLDAPConnection( - confService.getServerHostname(), - confService.getServerPort(), - confService.getEncryptionMethod()); + config.getServerHostname(), + config.getServerPort(), + config.getEncryptionMethod()); } /** @@ -156,6 +152,9 @@ public class LDAPConnectionService { * requested, and will not be connected until it is used in an LDAP * operation (such as a bind). * + * @param config + * The configuration of the LDAP server being queried. + * * @param url * The LDAP URL containing the details which should be used to connect * to the LDAP server. @@ -170,8 +169,8 @@ public class LDAPConnectionService { * method indicated by the URL is known but not actually implemented (a * bug). */ - private LdapNetworkConnection createLDAPConnection(String url) - throws GuacamoleException { + private LdapNetworkConnection createLDAPConnection(LDAPConfiguration config, + String url) throws GuacamoleException { // Parse provided LDAP URL LdapUrl ldapUrl; @@ -197,7 +196,7 @@ public class LDAPConnectionService { // Use STARTTLS for otherwise unencrypted ldap:// URLs if the main // LDAP connection requires STARTTLS - else if (confService.getEncryptionMethod() == EncryptionMethod.STARTTLS) { + else if (config.getEncryptionMethod() == EncryptionMethod.STARTTLS) { logger.debug("Using STARTTLS for LDAP URL \"{}\" as the main LDAP " + "connection described in guacamole.properties is " + "configured to use STARTTLS.", url); @@ -329,6 +328,9 @@ public class LDAPConnectionService { * hostname, port, and encryption method of the LDAP server are determined * from guacamole.properties. * + * @param config + * The configuration of the LDAP server being queried. + * * @param bindUser * The DN or UPN of the user to bind as, or null to bind anonymously. * @@ -344,15 +346,18 @@ public class LDAPConnectionService { * If an error occurs while parsing guacamole.properties, or if the * configured encryption method is actually not implemented (a bug). */ - public LdapNetworkConnection bindAs(String bindUser, String password) - throws GuacamoleException { - return bindAs(createLDAPConnection(), bindUser, password); + public LdapNetworkConnection bindAs(LDAPConfiguration config, + String bindUser, String password) throws GuacamoleException { + return bindAs(createLDAPConnection(config), bindUser, password); } /** * Binds to the LDAP server indicated by the given LDAP URL using the * credentials that were used to bind an existing LdapNetworkConnection. * + * @param config + * The configuration of the LDAP server being queried. + * * @param url * The LDAP URL containing the details which should be used to connect * to the LDAP server. @@ -370,16 +375,19 @@ public class LDAPConnectionService { * method indicated by the URL is known but not actually implemented (a * bug). */ - public LdapNetworkConnection bindAs(String url, + public LdapNetworkConnection bindAs(LDAPConfiguration config, String url, LdapNetworkConnection useCredentialsFrom) throws GuacamoleException { - return bindAs(createLDAPConnection(url), useCredentialsFrom); + return bindAs(createLDAPConnection(config, url), useCredentialsFrom); } /** * Generate a SearchRequest object using the given Base DN and filter * and retrieving other properties from the LDAP configuration service. - * + * + * @param config + * The configuration of the LDAP server being queried. + * * @param baseDn * The LDAP Base DN at which to search the search. * @@ -392,19 +400,19 @@ public class LDAPConnectionService { * @throws GuacamoleException * If an error occurs retrieving any of the configuration values. */ - public SearchRequest getSearchRequest(Dn baseDn, ExprNode filter) - throws GuacamoleException { + public SearchRequest getSearchRequest(LDAPConfiguration config, Dn baseDn, + ExprNode filter) throws GuacamoleException { SearchRequest searchRequest = new SearchRequestImpl(); searchRequest.setBase(baseDn); - searchRequest.setDerefAliases(confService.getDereferenceAliases()); + searchRequest.setDerefAliases(config.getDereferenceAliases()); searchRequest.setScope(SearchScope.SUBTREE); searchRequest.setFilter(filter); - searchRequest.setSizeLimit(confService.getMaxResults()); - searchRequest.setTimeLimit(confService.getOperationTimeout()); + searchRequest.setSizeLimit(config.getMaxResults()); + searchRequest.setTimeLimit(config.getOperationTimeout()); searchRequest.setTypesOnly(false); - if (confService.getFollowReferrals()) + if (config.getFollowReferrals()) searchRequest.followReferrals(); return searchRequest; 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 index e54b54f7c..02814ac9c 100644 --- 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 @@ -44,7 +44,7 @@ import org.apache.directory.api.ldap.model.name.Dn; import org.apache.directory.ldap.client.api.LdapNetworkConnection; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleServerException; -import org.apache.guacamole.auth.ldap.conf.ConfigurationService; +import org.apache.guacamole.auth.ldap.conf.LDAPConfiguration; import org.apache.guacamole.auth.ldap.conf.LDAPGuacamoleProperties; import org.apache.guacamole.net.auth.Identifiable; import org.slf4j.Logger; @@ -70,12 +70,6 @@ public class ObjectQueryService { @Inject private LDAPConnectionService ldapService; - /** - * 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 @@ -184,6 +178,9 @@ public class ObjectQueryService { * list of all results. Only objects beneath the given base DN are * included in the search. * + * @param config + * The configuration of the LDAP server being queried. + * * @param ldapConnection * The current connection to the LDAP server, associated with the * current user. @@ -212,12 +209,13 @@ public class ObjectQueryService { * information required to execute the query cannot be read from * guacamole.properties. */ - public List search(LdapNetworkConnection ldapConnection, - Dn baseDN, ExprNode query, int searchHop, - Collection attributes) throws GuacamoleException { + public List search(LDAPConfiguration config, + LdapNetworkConnection ldapConnection, Dn baseDN, ExprNode query, + int searchHop, Collection attributes) + throws GuacamoleException { // Refuse to follow referrals if limit has been reached - int maxHops = confService.getMaxReferralHops(); + int maxHops = config.getMaxReferralHops(); if (searchHop >= maxHops) { logger.debug("Refusing to follow further referrals as the maximum " + "number of referral hops ({}) has been reached. LDAP " @@ -230,8 +228,7 @@ public class ObjectQueryService { logger.debug("Searching \"{}\" for objects matching \"{}\".", baseDN, query); // Search within subtree of given base DN - SearchRequest request = ldapService.getSearchRequest(baseDN, query); - + SearchRequest request = ldapService.getSearchRequest(config, baseDN, query); if (attributes != null) request.addAttributes(attributes.toArray(new String[0])); @@ -257,10 +254,10 @@ public class ObjectQueryService { // Connect to referred LDAP server to retrieve further results, ensuring the network // connection is always closed when it will no longer be used - try (LdapNetworkConnection referralConnection = ldapService.bindAs(url, ldapConnection)) { + try (LdapNetworkConnection referralConnection = ldapService.bindAs(config, url, ldapConnection)) { if (referralConnection != null) { logger.debug("Following referral to \"{}\"...", url); - entries.addAll(search(referralConnection, baseDN, query, searchHop + 1, attributes)); + entries.addAll(search(config, referralConnection, baseDN, query, searchHop + 1, attributes)); } else logger.debug("Could not bind with LDAP " @@ -304,6 +301,9 @@ public class ObjectQueryService { * given connection, returning a list of all results. Only objects beneath * the given base DN are included in the search. * + * @param config + * The configuration of the LDAP server being queried. + * * @param ldapConnection * The current connection to the LDAP server, associated with the * current user. @@ -339,12 +339,12 @@ public class ObjectQueryService { * information required to execute the query cannot be read from * guacamole.properties. */ - public List search(LdapNetworkConnection ldapConnection, Dn baseDN, - ExprNode filter, Collection filterAttributes, String filterValue, - Collection attributes) - throws GuacamoleException { + public List search(LDAPConfiguration config, + LdapNetworkConnection ldapConnection, Dn baseDN, ExprNode filter, + Collection filterAttributes, String filterValue, + Collection attributes) throws GuacamoleException { ExprNode query = generateQuery(filter, filterAttributes, filterValue); - return search(ldapConnection, baseDN, query, 0, attributes); + return search(config, ldapConnection, baseDN, query, 0, attributes); } /** diff --git a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/ConfigurationService.java b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/ConfigurationService.java index 2071dfa02..80d98cd22 100644 --- a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/ConfigurationService.java +++ b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/ConfigurationService.java @@ -20,12 +20,6 @@ package org.apache.guacamole.auth.ldap.conf; import com.google.inject.Inject; -import java.util.Collections; -import java.util.List; -import org.apache.directory.api.ldap.model.filter.ExprNode; -import org.apache.directory.api.ldap.model.filter.PresenceNode; -import org.apache.directory.api.ldap.model.message.AliasDerefMode; -import org.apache.directory.api.ldap.model.name.Dn; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.environment.Environment; @@ -41,375 +35,24 @@ public class ConfigurationService { private Environment environment; /** - * Returns the hostname of the LDAP server as configured with - * guacamole.properties. By default, this will be "localhost". - * - * @return - * The hostname of the LDAP server, as configured with - * guacamole.properties. - * - * @throws GuacamoleException - * If guacamole.properties cannot be parsed. - */ - public String getServerHostname() throws GuacamoleException { - return environment.getProperty( - LDAPGuacamoleProperties.LDAP_HOSTNAME, - "localhost" - ); - } - - /** - * Returns the port of the LDAP server configured with - * guacamole.properties. The default value depends on which encryption - * method is being used. For unencrypted LDAP and STARTTLS, this will be - * 389. For LDAPS (LDAP over SSL) this will be 636. - * - * @return - * The port of the LDAP server, as configured with - * guacamole.properties. - * - * @throws GuacamoleException - * If guacamole.properties cannot be parsed. - */ - public int getServerPort() throws GuacamoleException { - return environment.getProperty( - LDAPGuacamoleProperties.LDAP_PORT, - getEncryptionMethod().DEFAULT_PORT - ); - } - - /** - * Returns all username attributes which should be used to query and bind - * users using the LDAP directory. By default, this will be "uid" - a - * common attribute used for this purpose. - * - * @return - * The username attributes which should be used to query and bind users - * using the LDAP directory. - * - * @throws GuacamoleException - * If guacamole.properties cannot be parsed. - */ - public List getUsernameAttributes() throws GuacamoleException { - return environment.getProperty( - LDAPGuacamoleProperties.LDAP_USERNAME_ATTRIBUTE, - Collections.singletonList("uid") - ); - } - - /** - * Returns the base DN under which all Guacamole users will be stored - * within the LDAP directory. - * - * @return - * The base DN under which all Guacamole users will be stored within - * the LDAP directory. - * - * @throws GuacamoleException - * If guacamole.properties cannot be parsed, or if the user base DN - * property is not specified. - */ - public Dn getUserBaseDN() throws GuacamoleException { - return environment.getRequiredProperty( - LDAPGuacamoleProperties.LDAP_USER_BASE_DN - ); - } - - /** - * Returns the base DN under which all Guacamole configurations - * (connections) will be stored within the LDAP directory. If Guacamole - * configurations will not be stored within LDAP, null is returned. - * - * @return - * The base DN under which all Guacamole configurations will be stored - * within the LDAP directory, or null if no Guacamole configurations - * will be stored within the LDAP directory. - * - * @throws GuacamoleException - * If guacamole.properties cannot be parsed. - */ - public Dn getConfigurationBaseDN() throws GuacamoleException { - return environment.getProperty( - LDAPGuacamoleProperties.LDAP_CONFIG_BASE_DN - ); - } - - /** - * Returns all attributes which should be used to determine the unique - * identifier of each user group. By default, this will be "cn". - * - * @return - * The attributes which should be used to determine the unique - * identifier of each group. - * - * @throws GuacamoleException - * If guacamole.properties cannot be parsed. - */ - public List getGroupNameAttributes() throws GuacamoleException { - return environment.getProperty( - LDAPGuacamoleProperties.LDAP_GROUP_NAME_ATTRIBUTE, - Collections.singletonList("cn") - ); - } - - /** - * Returns the base DN under which all Guacamole role based access control - * (RBAC) groups will be stored within the LDAP directory. If RBAC will not - * be used, null is returned. - * - * @return - * The base DN under which all Guacamole RBAC groups will be stored - * within the LDAP directory, or null if RBAC will not be used. - * - * @throws GuacamoleException - * If guacamole.properties cannot be parsed. - */ - public Dn getGroupBaseDN() throws GuacamoleException { - return environment.getProperty( - LDAPGuacamoleProperties.LDAP_GROUP_BASE_DN - ); - } - - /** - * Returns the login that should be used when searching for the DNs of users - * attempting to authenticate. If no such search should be performed, null + * Returns the configuration information for the LDAP server related to the + * user having the given username. If no such LDAP server is defined, null * is returned. * - * @return - * The DN that should be used when searching for the DNs of users - * attempting to authenticate, or null if no such search should be - * performed. - * - * @throws GuacamoleException - * If guacamole.properties cannot be parsed. - */ - public String getSearchBindDN() throws GuacamoleException { - return environment.getProperty( - LDAPGuacamoleProperties.LDAP_SEARCH_BIND_DN - ); - } - - /** - * Returns the password that should be used when binding to the LDAP server - * using the DN returned by getSearchBindDN(). If no password should be - * used, null is returned. + * @param username + * The username of the user whose corresponding LDAP server + * configuration should be retrieved. * * @return - * The password that should be used when binding to the LDAP server - * using the DN returned by getSearchBindDN(), or null if no password - * should be used. + * The configuration of the LDAP server related to the user having the + * given username, or null if no such LDAP server is defined. * * @throws GuacamoleException - * If guacamole.properties cannot be parsed. + * If the configuration information of the LDAP server related to the + * user having the given username cannot be retrieved due to an error. */ - public String getSearchBindPassword() throws GuacamoleException { - return environment.getProperty( - LDAPGuacamoleProperties.LDAP_SEARCH_BIND_PASSWORD - ); - } - - /** - * Returns the encryption method that should be used when connecting to the - * LDAP server. By default, no encryption is used. - * - * @return - * The encryption method that should be used when connecting to the - * LDAP server. - * - * @throws GuacamoleException - * If guacamole.properties cannot be parsed. - */ - public EncryptionMethod getEncryptionMethod() throws GuacamoleException { - return environment.getProperty( - LDAPGuacamoleProperties.LDAP_ENCRYPTION_METHOD, - EncryptionMethod.NONE - ); - } - - /** - * Returns maximum number of results a LDAP query can return, - * as configured with guacamole.properties. - * By default, this will be 1000. - * - * @return - * The maximum number of results a LDAP query can return, - * as configured with guacamole.properties. - * - * @throws GuacamoleException - * If guacamole.properties cannot be parsed. - */ - public int getMaxResults() throws GuacamoleException { - return environment.getProperty( - LDAPGuacamoleProperties.LDAP_MAX_SEARCH_RESULTS, - 1000 - ); - } - - /** - * Returns whether or not LDAP aliases will be dereferenced, - * as configured with guacamole.properties. The default - * behavior if not explicitly defined is to never - * dereference them. - * - * @return - * The behavior for handling dereferencing of aliases - * as configured in guacamole.properties. - * - * @throws GuacamoleException - * If guacamole.properties cannot be parsed. - */ - public AliasDerefMode getDereferenceAliases() throws GuacamoleException { - return environment.getProperty( - LDAPGuacamoleProperties.LDAP_DEREFERENCE_ALIASES, - AliasDerefMode.NEVER_DEREF_ALIASES - ); - } - - /** - * Returns the boolean value for whether the connection should - * follow referrals or not. By default, it will not. - * - * @return - * The boolean value of whether to follow referrals - * as configured in guacamole.properties. - * - * @throws GuacamoleException - * If guacamole.properties cannot be parsed. - */ - public boolean getFollowReferrals() throws GuacamoleException { - return environment.getProperty( - LDAPGuacamoleProperties.LDAP_FOLLOW_REFERRALS, - false - ); - } - - /** - * Returns the maximum number of referral hops to follow. By default - * a maximum of 5 hops is allowed. - * - * @return - * The maximum number of referral hops to follow - * as configured in guacamole.properties. - * - * @throws GuacamoleException - * If guacamole.properties cannot be parsed. - */ - public int getMaxReferralHops() throws GuacamoleException { - return environment.getProperty( - LDAPGuacamoleProperties.LDAP_MAX_REFERRAL_HOPS, - 5 - ); - } - - /** - * Returns the search filter that should be used when querying the - * LDAP server for Guacamole users. If no filter is specified, - * a default of "(objectClass=user)" is returned. - * - * @return - * The search filter that should be used when querying the - * LDAP server for users that are valid in Guacamole, or - * "(objectClass=user)" if not specified. - * - * @throws GuacamoleException - * If guacamole.properties cannot be parsed. - */ - public ExprNode getUserSearchFilter() throws GuacamoleException { - return environment.getProperty( - LDAPGuacamoleProperties.LDAP_USER_SEARCH_FILTER, - new PresenceNode("objectClass") - ); - } - - /** - * Returns the search filter that should be used when querying the - * LDAP server for Guacamole groups. If no filter is specified, - * a default of "(objectClass=*)" is used. - * - * @return - * The search filter that should be used when querying the - * LDAP server for groups that are valid in Guacamole, or - * "(objectClass=*)" if not specified. - * - * @throws GuacamoleException - * If guacamole.properties cannot be parsed. - */ - public ExprNode getGroupSearchFilter() throws GuacamoleException { - return environment.getProperty( - LDAPGuacamoleProperties.LDAP_GROUP_SEARCH_FILTER, - new PresenceNode("objectClass") - ); - } - - /** - * Returns the maximum number of seconds to wait for LDAP operations. - * - * @return - * The maximum number of seconds to wait for LDAP operations - * as configured in guacamole.properties. - * - * @throws GuacamoleException - * If guacamole.properties cannot be parsed. - */ - public int getOperationTimeout() throws GuacamoleException { - return environment.getProperty( - LDAPGuacamoleProperties.LDAP_OPERATION_TIMEOUT, - 30 - ); - } - - /** - * Returns names for custom LDAP user attributes. By default no - * attributes will be returned. - * - * @return - * Custom LDAP user attributes as configured in guacamole.properties. - * - * @throws GuacamoleException - * If guacamole.properties cannot be parsed. - */ - public List getAttributes() throws GuacamoleException { - return environment.getProperty( - LDAPGuacamoleProperties.LDAP_USER_ATTRIBUTES, - Collections.emptyList() - ); - } - - /** - * Returns the name of the LDAP attribute used to enumerate - * members in a group, or "member" by default. - * - * @return - * The name of the LDAP attribute to use to enumerate - * members in a group. - * - * @throws GuacamoleException - * If guacamole.properties connect be parsed. - */ - public String getMemberAttribute() throws GuacamoleException { - return environment.getProperty( - LDAPGuacamoleProperties.LDAP_MEMBER_ATTRIBUTE, - "member" - ); - } - - /** - * Returns whether the LDAP attribute used to enumerate members in a group - * specifies UID or DN. - * - * @return - * The type of data contained in the LDAP attribute used to enumerate - * members in a group, as configured in guacamole.properties - * - * @throws GuacamoleException - * If guacamole.properties cannot be parsed. - */ - public MemberAttributeType getMemberAttributeType() - throws GuacamoleException { - return environment.getProperty( - LDAPGuacamoleProperties.LDAP_MEMBER_ATTRIBUTE_TYPE, - MemberAttributeType.DN - ); + public LDAPConfiguration getLDAPConfiguration(String username) throws GuacamoleException { + return new EnvironmentLDAPConfiguration(environment); } } diff --git a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/EnvironmentLDAPConfiguration.java b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/EnvironmentLDAPConfiguration.java new file mode 100644 index 000000000..4dca7cde2 --- /dev/null +++ b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/EnvironmentLDAPConfiguration.java @@ -0,0 +1,209 @@ +/* + * 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.conf; + +import java.util.Collections; +import java.util.List; +import org.apache.directory.api.ldap.model.filter.ExprNode; +import org.apache.directory.api.ldap.model.filter.PresenceNode; +import org.apache.directory.api.ldap.model.message.AliasDerefMode; +import org.apache.directory.api.ldap.model.name.Dn; +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.environment.Environment; + +/** + * LDAPConfiguration implementation that reads its configuration details from + * guacamole.properties. + */ +public class EnvironmentLDAPConfiguration implements LDAPConfiguration { + + /** + * The Guacamole server environment. + */ + private final Environment environment; + + /** + * Creates a new EnvironmentLDAPConfiguration that reads its configuration + * details from guacamole.properties, as exposed by the given Environment. + * + * @param environment + * The Guacamole server environment. + */ + public EnvironmentLDAPConfiguration(Environment environment) { + this.environment = environment; + } + + @Override + public String getServerHostname() throws GuacamoleException { + return environment.getProperty( + LDAPGuacamoleProperties.LDAP_HOSTNAME, + "localhost" + ); + } + + @Override + public int getServerPort() throws GuacamoleException { + return environment.getProperty( + LDAPGuacamoleProperties.LDAP_PORT, + getEncryptionMethod().DEFAULT_PORT + ); + } + + @Override + public List getUsernameAttributes() throws GuacamoleException { + return environment.getProperty( + LDAPGuacamoleProperties.LDAP_USERNAME_ATTRIBUTE, + Collections.singletonList("uid") + ); + } + + @Override + public Dn getUserBaseDN() throws GuacamoleException { + return environment.getRequiredProperty( + LDAPGuacamoleProperties.LDAP_USER_BASE_DN + ); + } + + @Override + public Dn getConfigurationBaseDN() throws GuacamoleException { + return environment.getProperty( + LDAPGuacamoleProperties.LDAP_CONFIG_BASE_DN + ); + } + + @Override + public List getGroupNameAttributes() throws GuacamoleException { + return environment.getProperty( + LDAPGuacamoleProperties.LDAP_GROUP_NAME_ATTRIBUTE, + Collections.singletonList("cn") + ); + } + + @Override + public Dn getGroupBaseDN() throws GuacamoleException { + return environment.getProperty( + LDAPGuacamoleProperties.LDAP_GROUP_BASE_DN + ); + } + + @Override + public String getSearchBindDN() throws GuacamoleException { + return environment.getProperty( + LDAPGuacamoleProperties.LDAP_SEARCH_BIND_DN + ); + } + + @Override + public String getSearchBindPassword() throws GuacamoleException { + return environment.getProperty( + LDAPGuacamoleProperties.LDAP_SEARCH_BIND_PASSWORD + ); + } + + @Override + public EncryptionMethod getEncryptionMethod() throws GuacamoleException { + return environment.getProperty( + LDAPGuacamoleProperties.LDAP_ENCRYPTION_METHOD, + EncryptionMethod.NONE + ); + } + + @Override + public int getMaxResults() throws GuacamoleException { + return environment.getProperty( + LDAPGuacamoleProperties.LDAP_MAX_SEARCH_RESULTS, + 1000 + ); + } + + @Override + public AliasDerefMode getDereferenceAliases() throws GuacamoleException { + return environment.getProperty( + LDAPGuacamoleProperties.LDAP_DEREFERENCE_ALIASES, + AliasDerefMode.NEVER_DEREF_ALIASES + ); + } + + @Override + public boolean getFollowReferrals() throws GuacamoleException { + return environment.getProperty( + LDAPGuacamoleProperties.LDAP_FOLLOW_REFERRALS, + false + ); + } + + @Override + public int getMaxReferralHops() throws GuacamoleException { + return environment.getProperty( + LDAPGuacamoleProperties.LDAP_MAX_REFERRAL_HOPS, + 5 + ); + } + + @Override + public ExprNode getUserSearchFilter() throws GuacamoleException { + return environment.getProperty( + LDAPGuacamoleProperties.LDAP_USER_SEARCH_FILTER, + new PresenceNode("objectClass") + ); + } + + @Override + public ExprNode getGroupSearchFilter() throws GuacamoleException { + return environment.getProperty( + LDAPGuacamoleProperties.LDAP_GROUP_SEARCH_FILTER, + new PresenceNode("objectClass") + ); + } + + @Override + public int getOperationTimeout() throws GuacamoleException { + return environment.getProperty( + LDAPGuacamoleProperties.LDAP_OPERATION_TIMEOUT, + 30 + ); + } + + @Override + public List getAttributes() throws GuacamoleException { + return environment.getProperty( + LDAPGuacamoleProperties.LDAP_USER_ATTRIBUTES, + Collections.emptyList() + ); + } + + @Override + public String getMemberAttribute() throws GuacamoleException { + return environment.getProperty( + LDAPGuacamoleProperties.LDAP_MEMBER_ATTRIBUTE, + "member" + ); + } + + @Override + public MemberAttributeType getMemberAttributeType() + throws GuacamoleException { + return environment.getProperty( + LDAPGuacamoleProperties.LDAP_MEMBER_ATTRIBUTE_TYPE, + MemberAttributeType.DN + ); + } + +} diff --git a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/LDAPConfiguration.java b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/LDAPConfiguration.java new file mode 100644 index 000000000..5c3315741 --- /dev/null +++ b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/LDAPConfiguration.java @@ -0,0 +1,303 @@ +/* + * 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.conf; + +import java.util.List; +import org.apache.directory.api.ldap.model.filter.ExprNode; +import org.apache.directory.api.ldap.model.message.AliasDerefMode; +import org.apache.directory.api.ldap.model.name.Dn; +import org.apache.guacamole.GuacamoleException; + +/** + * Configuration information defining how a particular LDAP server should be + * queried. + */ +public interface LDAPConfiguration { + + /** + * Returns the hostname or IP address of the LDAP server. By default, this + * will be "localhost". + * + * @return + * The hostname or IP address of the LDAP server. + * + * @throws GuacamoleException + * If the hostname or IP address of the LDAP server cannot be + * retrieved. + */ + String getServerHostname() throws GuacamoleException; + + /** + * Returns the port of the LDAP server. The default value depends on which + * encryption method is being used. For unencrypted LDAP and STARTTLS, this + * will be 389. For LDAPS (LDAP over SSL) this will be 636. + * + * @return + * The port of the LDAP server. + * + * @throws GuacamoleException + * If the port of the LDAP server cannot be retrieved. + */ + int getServerPort() throws GuacamoleException; + + /** + * Returns all username attributes which should be used to query and bind + * users using the LDAP directory. By default, this will be "uid" - a + * common attribute used for this purpose. + * + * @return + * The username attributes which should be used to query and bind users + * using the LDAP directory. + * + * @throws GuacamoleException + * If the username attributes cannot be retrieved. + */ + List getUsernameAttributes() throws GuacamoleException; + + /** + * Returns the base DN under which all Guacamole users will be stored + * within the LDAP directory. + * + * @return + * The base DN under which all Guacamole users will be stored within + * the LDAP directory. + * + * @throws GuacamoleException + * If the user base DN cannot be retrieved. + */ + Dn getUserBaseDN() throws GuacamoleException; + + /** + * Returns the base DN under which all Guacamole configurations + * (connections) will be stored within the LDAP directory. If Guacamole + * configurations will not be stored within LDAP, null is returned. + * + * @return + * The base DN under which all Guacamole configurations will be stored + * within the LDAP directory, or null if no Guacamole configurations + * will be stored within the LDAP directory. + * + * @throws GuacamoleException + * If the configuration base DN cannot be retrieved. + */ + Dn getConfigurationBaseDN() throws GuacamoleException; + + /** + * Returns all attributes which should be used to determine the unique + * identifier of each user group. By default, this will be "cn". + * + * @return + * The attributes which should be used to determine the unique + * identifier of each group. + * + * @throws GuacamoleException + * If the group name attributes cannot be retrieved. + */ + List getGroupNameAttributes() throws GuacamoleException; + + /** + * Returns the base DN under which all Guacamole role based access control + * (RBAC) groups will be stored within the LDAP directory. If RBAC will not + * be used, null is returned. + * + * @return + * The base DN under which all Guacamole RBAC groups will be stored + * within the LDAP directory, or null if RBAC will not be used. + * + * @throws GuacamoleException + * If the group base DN cannot be retrieved. + */ + Dn getGroupBaseDN() throws GuacamoleException; + + /** + * Returns the login that should be used when searching for the DNs of users + * attempting to authenticate. If no such search should be performed, null + * is returned. + * + * @return + * The DN that should be used when searching for the DNs of users + * attempting to authenticate, or null if no such search should be + * performed. + * + * @throws GuacamoleException + * If the search bind DN cannot be retrieved. + */ + String getSearchBindDN() throws GuacamoleException; + + /** + * Returns the password that should be used when binding to the LDAP server + * using the DN returned by getSearchBindDN(). If no password should be + * used, null is returned. + * + * @return + * The password that should be used when binding to the LDAP server + * using the DN returned by getSearchBindDN(), or null if no password + * should be used. + * + * @throws GuacamoleException + * If the search bind password cannot be retrieved. + */ + String getSearchBindPassword() throws GuacamoleException; + + /** + * Returns the encryption method that should be used when connecting to the + * LDAP server. By default, no encryption is used. + * + * @return + * The encryption method that should be used when connecting to the + * LDAP server. + * + * @throws GuacamoleException + * If the encryption method cannot be retrieved. + */ + EncryptionMethod getEncryptionMethod() throws GuacamoleException; + + /** + * Returns maximum number of results a LDAP query can return. By default, + * this will be 1000. + * + * @return + * The maximum number of results a LDAP query can return. + * + * @throws GuacamoleException + * If the maximum number of results cannot be retrieved. + */ + int getMaxResults() throws GuacamoleException; + + /** + * Returns whether or not LDAP aliases will be dereferenced. By default, + * aliases are never dereferenced. + * + * @return + * The LDAP alias dereferencing mode. + * + * @throws GuacamoleException + * If the LDAP alias dereferencing mode cannot be retrieved. + */ + AliasDerefMode getDereferenceAliases() throws GuacamoleException; + + /** + * Returns whether referrals should be automatically followed. By default, + * referrals are not followed. + * + * @return + * Whether referrals should be followed. + * + * @throws GuacamoleException + * If the configuration information determining whether LDAP referrals + * should be followed cannot be retrieved. + */ + boolean getFollowReferrals() throws GuacamoleException; + + /** + * Returns the maximum number of referral hops to follow. By default + * a maximum of 5 hops is allowed. + * + * @return + * The maximum number of referral hops to follow. + * + * @throws GuacamoleException + * If the maximum number of referral hops cannot be retrieved. + */ + int getMaxReferralHops() throws GuacamoleException; + + /** + * Returns the search filter that should be used when querying the + * LDAP server for Guacamole users. If no filter is specified, + * a default of "(objectClass=user)" is returned. + * + * @return + * The search filter that should be used when querying the + * LDAP server for users that are valid in Guacamole, or + * "(objectClass=user)" if not specified. + * + * @throws GuacamoleException + * If the user search filter cannot be retrieved. + */ + ExprNode getUserSearchFilter() throws GuacamoleException; + + /** + * Returns the search filter that should be used when querying the + * LDAP server for Guacamole groups. If no filter is specified, + * a default of "(objectClass=*)" is used. + * + * @return + * The search filter that should be used when querying the + * LDAP server for groups that are valid in Guacamole, or + * "(objectClass=*)" if not specified. + * + * @throws GuacamoleException + * If the group search filter cannot be retrieved. + */ + ExprNode getGroupSearchFilter() throws GuacamoleException; + + /** + * Returns the maximum number of seconds to wait for LDAP operations. + * + * @return + * The maximum number of seconds to wait for LDAP operations. + * + * @throws GuacamoleException + * If the LDAP operation timeout cannot be retrieved. + */ + int getOperationTimeout() throws GuacamoleException; + + /** + * Returns names for custom LDAP user attributes that should be made + * available as parameter tokens. By default, no additional LDAP attributes + * will be exposed as parameter tokens. + * + * @return + * A list of all LDAP user attributes that should be made available as + * parameter tokens. + * + * @throws GuacamoleException + * If the names of custom LDAP user attributes cannot be retrieved. + */ + List getAttributes() throws GuacamoleException; + + /** + * Returns the name of the LDAP attribute used to enumerate members in a + * group. By default, this will be "member". + * + * @return + * The name of the LDAP attribute to use to enumerate + * members in a group. + * + * @throws GuacamoleException + * If the group member attribute cannot be retrieved. + */ + String getMemberAttribute() throws GuacamoleException; + + /** + * Returns whether the LDAP attribute used to enumerate members in a group + * specifies a UID or DN. + * + * @return + * The type of data contained in the LDAP attribute used to enumerate + * members in a group. + * + * @throws GuacamoleException + * If the type of attribute used to enumerate group members cannot be + * retrieved. + */ + MemberAttributeType getMemberAttributeType() throws GuacamoleException; + +} 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 84bef7f92..bff49858c 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 @@ -38,13 +38,12 @@ import org.apache.directory.api.ldap.model.name.Dn; import org.apache.directory.ldap.client.api.LdapConnectionConfig; import org.apache.directory.ldap.client.api.LdapNetworkConnection; import org.apache.guacamole.auth.ldap.LDAPAuthenticationProvider; -import org.apache.guacamole.auth.ldap.conf.ConfigurationService; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleServerException; import org.apache.guacamole.auth.ldap.ObjectQueryService; +import org.apache.guacamole.auth.ldap.conf.LDAPConfiguration; import org.apache.guacamole.auth.ldap.group.UserGroupService; import org.apache.guacamole.auth.ldap.user.LDAPAuthenticatedUser; -import org.apache.guacamole.net.auth.AuthenticatedUser; import org.apache.guacamole.net.auth.Connection; import org.apache.guacamole.net.auth.TokenInjectingConnection; import org.apache.guacamole.net.auth.simple.SimpleConnection; @@ -63,12 +62,6 @@ public class ConnectionService { */ private static final Logger logger = LoggerFactory.getLogger(ConnectionService.class); - /** - * Service for retrieving LDAP server configuration information. - */ - @Inject - private ConfigurationService confService; - /** * Service for executing LDAP queries. */ @@ -143,11 +136,13 @@ public class ConnectionService { * @throws GuacamoleException * If an error occurs preventing retrieval of connections. */ - public Map getConnections(AuthenticatedUser user, + public Map getConnections(LDAPAuthenticatedUser user, LdapNetworkConnection ldapConnection) throws GuacamoleException { + LDAPConfiguration ldapConfig = user.getLDAPConfiguration(); + // Do not return any connections if base DN is not specified - Dn configurationBaseDN = confService.getConfigurationBaseDN(); + Dn configurationBaseDN = ldapConfig.getConfigurationBaseDN(); if (configurationBaseDN == null) return Collections.emptyMap(); @@ -164,13 +159,13 @@ public class ConnectionService { // Get the search filter for finding connections accessible by the // current user - ExprNode connectionSearchFilter = getConnectionSearchFilter(userDN, ldapConnection); + ExprNode connectionSearchFilter = getConnectionSearchFilter(user, userDN, ldapConnection); // Find all Guacamole connections for the given user by // 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. - List results = queryService.search(ldapConnection, + List results = queryService.search(ldapConfig, ldapConnection, configurationBaseDN, connectionSearchFilter, 0, GUAC_CONFIG_LDAP_ATTRIBUTES); // Return a map of all readable connections @@ -261,8 +256,7 @@ public class ConnectionService { // Inject LDAP-specific tokens only if LDAP handled user // authentication if (user instanceof LDAPAuthenticatedUser) - connection = new TokenInjectingConnection(connection, - ((LDAPAuthenticatedUser) user).getTokens()); + connection = new TokenInjectingConnection(connection, user.getTokens()); return connection; @@ -279,6 +273,10 @@ public class ConnectionService { * Returns an LDAP search filter which queries all connections accessible * by the user having the given DN. * + * @param user + * The AuthenticatedUser object associated with the user who is + * currently authenticated with Guacamole. + * * @param userDN * DN of the user to search for associated guacConfigGroup connections. * @@ -296,10 +294,12 @@ public class ConnectionService { * @throws GuacamoleException * If an error occurs retrieving the group base DN. */ - private ExprNode getConnectionSearchFilter(Dn userDN, - LdapNetworkConnection ldapConnection) + private ExprNode getConnectionSearchFilter(LDAPAuthenticatedUser user, + Dn userDN, LdapNetworkConnection ldapConnection) throws LdapException, GuacamoleException { + LDAPConfiguration config = user.getLDAPConfiguration(); + AndNode searchFilter = new AndNode(); // Add the prefix to the search filter, prefix filter searches for guacConfigGroups with the userDN as the member attribute value @@ -307,12 +307,12 @@ public class ConnectionService { // Apply group filters OrNode groupFilter = new OrNode(); - groupFilter.addNode(new EqualityNode(confService.getMemberAttribute(), + groupFilter.addNode(new EqualityNode(config.getMemberAttribute(), userDN.toString())); // Additionally filter by group membership if the current user is a // member of any user groups - List userGroups = userGroupService.getParentUserGroupEntries(ldapConnection, userDN); + List userGroups = userGroupService.getParentUserGroupEntries(config, ldapConnection, userDN); if (!userGroups.isEmpty()) { userGroups.forEach(entry -> groupFilter.addNode(new EqualityNode(LDAP_ATTRIBUTE_NAME_GROUPS,entry.getDn().toString())) diff --git a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/group/UserGroupService.java b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/group/UserGroupService.java index 67ffbc3fb..a3dc9c1e8 100644 --- a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/group/UserGroupService.java +++ b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/group/UserGroupService.java @@ -34,10 +34,11 @@ import org.apache.directory.api.ldap.model.filter.ExprNode; import org.apache.directory.api.ldap.model.filter.NotNode; import org.apache.directory.api.ldap.model.name.Dn; import org.apache.directory.ldap.client.api.LdapNetworkConnection; -import org.apache.guacamole.auth.ldap.conf.ConfigurationService; import org.apache.guacamole.auth.ldap.conf.MemberAttributeType; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.auth.ldap.ObjectQueryService; +import org.apache.guacamole.auth.ldap.conf.LDAPConfiguration; +import org.apache.guacamole.auth.ldap.user.LDAPAuthenticatedUser; import org.apache.guacamole.net.auth.UserGroup; import org.apache.guacamole.net.auth.simple.SimpleUserGroup; import org.slf4j.Logger; @@ -54,12 +55,6 @@ public class UserGroupService { */ private static final Logger logger = LoggerFactory.getLogger(UserGroupService.class); - /** - * Service for retrieving LDAP server configuration information. - */ - @Inject - private ConfigurationService confService; - /** * Service for executing LDAP queries. */ @@ -73,22 +68,25 @@ public class UserGroupService { * defined (may always return zero results), it should only be explicitly * excluded if it is expected to have been defined. * + * @param config + * The configuration of the LDAP server being queried. + * * @return * The base search filter which should be used to retrieve user groups. * * @throws GuacamoleException * If guacamole.properties cannot be parsed. */ - private ExprNode getGroupSearchFilter() throws GuacamoleException { + private ExprNode getGroupSearchFilter(LDAPConfiguration config) throws GuacamoleException { // Use filter defined by "ldap-group-search-filter" as basis for all // retrieval of user groups - ExprNode groupFilter = confService.getGroupSearchFilter(); + ExprNode groupFilter = config.getGroupSearchFilter(); // Explicitly exclude guacConfigGroup object class only if it should // be assumed to be defined (query may fail due to no such object // class existing otherwise) - if (confService.getConfigurationBaseDN() != null) { + if (config.getConfigurationBaseDN() != null) { groupFilter = new AndNode( groupFilter, new NotNode(new EqualityNode("objectClass", "guacConfigGroup")) @@ -103,6 +101,10 @@ public class UserGroupService { * Returns all Guacamole user groups accessible to the user currently bound * under the given LDAP connection. * + * @param user + * The AuthenticatedUser object associated with the user who is + * currently authenticated with Guacamole. + * * @param ldapConnection * The current connection to the LDAP server, associated with the * current user. @@ -115,25 +117,28 @@ public class UserGroupService { * @throws GuacamoleException * If an error occurs preventing retrieval of user groups. */ - public Map getUserGroups(LdapNetworkConnection ldapConnection) - throws GuacamoleException { + public Map getUserGroups(LDAPAuthenticatedUser user, + LdapNetworkConnection ldapConnection) throws GuacamoleException { + LDAPConfiguration config = user.getLDAPConfiguration(); + // Do not return any user groups if base DN is not specified - Dn groupBaseDN = confService.getGroupBaseDN(); + Dn groupBaseDN = config.getGroupBaseDN(); if (groupBaseDN == null) return Collections.emptyMap(); // Gather all attributes relevant for a group - String memberAttribute = confService.getMemberAttribute(); - Collection groupAttributes = new HashSet<>(confService.getGroupNameAttributes()); + String memberAttribute = config.getMemberAttribute(); + Collection groupAttributes = new HashSet<>(config.getGroupNameAttributes()); groupAttributes.add(memberAttribute); // Retrieve all visible user groups which are not guacConfigGroups - Collection attributes = confService.getGroupNameAttributes(); + Collection attributes = config.getGroupNameAttributes(); List results = queryService.search( + config, ldapConnection, groupBaseDN, - getGroupSearchFilter(), + getGroupSearchFilter(config), attributes, null, groupAttributes @@ -167,6 +172,9 @@ public class UserGroupService { * user is a member of. Only user groups which are readable by the current * user will be retrieved. * + * @param config + * The configuration of the LDAP server being queried. + * * @param ldapConnection * The current connection to the LDAP server, associated with the * current user. @@ -181,24 +189,26 @@ public class UserGroupService { * @throws GuacamoleException * If an error occurs preventing retrieval of user groups. */ - public List getParentUserGroupEntries(LdapNetworkConnection ldapConnection, - Dn userDN) throws GuacamoleException { + public List getParentUserGroupEntries(LDAPConfiguration config, + LdapNetworkConnection ldapConnection, Dn userDN) + throws GuacamoleException { // Do not return any user groups if base DN is not specified - Dn groupBaseDN = confService.getGroupBaseDN(); + Dn groupBaseDN = config.getGroupBaseDN(); if (groupBaseDN == null) return Collections.emptyList(); // memberAttribute specified in properties could contain DN or username - MemberAttributeType memberAttributeType = confService.getMemberAttributeType(); + MemberAttributeType memberAttributeType = config.getMemberAttributeType(); String userIDorDN = userDN.toString(); - Collection userAttributes = confService.getUsernameAttributes(); + Collection userAttributes = config.getUsernameAttributes(); if (memberAttributeType == MemberAttributeType.UID) { // Retrieve user objects with userDN List userEntries = queryService.search( + config, ldapConnection, userDN, - confService.getUserSearchFilter(), + config.getUserSearchFilter(), 0, userAttributes); // ... there can surely only be one @@ -222,16 +232,17 @@ public class UserGroupService { } // Gather all attributes relevant for a group - String memberAttribute = confService.getMemberAttribute(); - Collection groupAttributes = new HashSet<>(confService.getGroupNameAttributes()); + String memberAttribute = config.getMemberAttribute(); + Collection groupAttributes = new HashSet<>(config.getGroupNameAttributes()); groupAttributes.add(memberAttribute); // Get all groups the user is a member of starting at the groupBaseDN, // excluding guacConfigGroups return queryService.search( + config, ldapConnection, groupBaseDN, - getGroupSearchFilter(), + getGroupSearchFilter(config), Collections.singleton(memberAttribute), userIDorDN, groupAttributes @@ -244,6 +255,9 @@ public class UserGroupService { * member of. Only identifiers of user groups which are readable by the * current user will be retrieved. * + * @param config + * The configuration of the LDAP server being queried. + * * @param ldapConnection * The current connection to the LDAP server, associated with the * current user. @@ -258,11 +272,12 @@ public class UserGroupService { * @throws GuacamoleException * If an error occurs preventing retrieval of user groups. */ - public Set getParentUserGroupIdentifiers(LdapNetworkConnection ldapConnection, - Dn userDN) throws GuacamoleException { + public Set getParentUserGroupIdentifiers(LDAPConfiguration config, + LdapNetworkConnection ldapConnection, Dn userDN) + throws GuacamoleException { - Collection attributes = confService.getGroupNameAttributes(); - List userGroups = getParentUserGroupEntries(ldapConnection, userDN); + Collection attributes = config.getGroupNameAttributes(); + List userGroups = getParentUserGroupEntries(config, ldapConnection, userDN); Set identifiers = new HashSet<>(userGroups.size()); userGroups.forEach(entry -> { diff --git a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/user/LDAPAuthenticatedUser.java b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/user/LDAPAuthenticatedUser.java index 44296432f..97eab6cae 100644 --- a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/user/LDAPAuthenticatedUser.java +++ b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/user/LDAPAuthenticatedUser.java @@ -24,6 +24,7 @@ import java.util.Collections; import java.util.Map; import java.util.Set; import org.apache.directory.api.ldap.model.name.Dn; +import org.apache.guacamole.auth.ldap.conf.LDAPConfiguration; import org.apache.guacamole.net.auth.AbstractAuthenticatedUser; import org.apache.guacamole.net.auth.AuthenticationProvider; import org.apache.guacamole.net.auth.Credentials; @@ -63,10 +64,20 @@ public class LDAPAuthenticatedUser extends AbstractAuthenticatedUser { */ private Dn bindDn; + /** + * The configuration of the LDAP server that should be used for all queries + * related to this AuthenticatedUser. + */ + private LDAPConfiguration config; + /** * Initializes this AuthenticatedUser with the given credentials, * connection parameter tokens. and set of effective user groups. * + * @param config + * The configuration of the LDAP server that should be used for all + * queries related to this AuthenticatedUser. + * * @param credentials * The credentials provided when this user was authenticated. * @@ -81,8 +92,9 @@ public class LDAPAuthenticatedUser extends AbstractAuthenticatedUser { * @param bindDn * The LDAP DN used to bind this user. */ - public void init(Credentials credentials, Map tokens, - Set effectiveGroups, Dn bindDn) { + public void init(LDAPConfiguration config, Credentials credentials, + Map tokens, Set effectiveGroups, Dn bindDn) { + this.config = config; this.credentials = credentials; this.tokens = Collections.unmodifiableMap(tokens); this.effectiveGroups = effectiveGroups; @@ -114,6 +126,18 @@ public class LDAPAuthenticatedUser extends AbstractAuthenticatedUser { return bindDn; } + /** + * Returns the configuration of the LDAP server that should be used for all + * queries related to this AuthenticatedUser. + * + * @return + * The configuration of the LDAP server related to this + * AuthenticatedUser. + */ + public LDAPConfiguration getLDAPConfiguration() { + return config; + } + @Override public AuthenticationProvider getAuthenticationProvider() { return authProvider; diff --git a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/user/LDAPUserContext.java b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/user/LDAPUserContext.java index b5c789e1e..cb2afd516 100644 --- a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/user/LDAPUserContext.java +++ b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/user/LDAPUserContext.java @@ -27,7 +27,6 @@ import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.auth.ldap.LDAPAuthenticationProvider; import org.apache.guacamole.auth.ldap.group.UserGroupService; import org.apache.guacamole.net.auth.AbstractUserContext; -import org.apache.guacamole.net.auth.AuthenticatedUser; import org.apache.guacamole.net.auth.AuthenticationProvider; import org.apache.guacamole.net.auth.Connection; import org.apache.guacamole.net.auth.ConnectionGroup; @@ -106,8 +105,8 @@ public class LDAPUserContext extends AbstractUserContext { * * @param user * The AuthenticatedUser representing the user that authenticated. This - * user may have been authenticated by a different authentication - * provider (not LDAP). + * user will always have been authenticated via LDAP, as LDAP data is + * not provided to non-LDAP users. * * @param ldapConnection * The connection to the LDAP server to use when querying accessible @@ -117,17 +116,17 @@ public class LDAPUserContext extends AbstractUserContext { * If associated data stored within the LDAP directory cannot be * queried due to an error. */ - public void init(AuthenticatedUser user, LdapNetworkConnection ldapConnection) + public void init(LDAPAuthenticatedUser user, LdapNetworkConnection ldapConnection) throws GuacamoleException { // Query all accessible users userDirectory = new SimpleDirectory<>( - userService.getUsers(ldapConnection) + userService.getUsers(user, ldapConnection) ); // Query all accessible user groups userGroupDirectory = new SimpleDirectory<>( - userGroupService.getUserGroups(ldapConnection) + userGroupService.getUserGroups(user, ldapConnection) ); // Query all accessible connections 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 1ffc270be..69df58a79 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 @@ -32,11 +32,11 @@ import org.apache.directory.api.ldap.model.exception.LdapInvalidAttributeValueEx import org.apache.directory.api.ldap.model.name.Dn; import org.apache.directory.api.ldap.model.name.Rdn; import org.apache.directory.ldap.client.api.LdapNetworkConnection; -import org.apache.guacamole.auth.ldap.conf.ConfigurationService; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleServerException; import org.apache.guacamole.auth.ldap.conf.LDAPGuacamoleProperties; import org.apache.guacamole.auth.ldap.ObjectQueryService; +import org.apache.guacamole.auth.ldap.conf.LDAPConfiguration; import org.apache.guacamole.net.auth.User; import org.apache.guacamole.net.auth.simple.SimpleUser; import org.slf4j.Logger; @@ -53,12 +53,6 @@ public class UserService { */ private static final Logger logger = LoggerFactory.getLogger(UserService.class); - /** - * Service for retrieving LDAP server configuration information. - */ - @Inject - private ConfigurationService confService; - /** * Service for executing LDAP queries. */ @@ -69,6 +63,10 @@ public class UserService { * Returns all Guacamole users accessible to the user currently bound under * the given LDAP connection. * + * @param user + * The AuthenticatedUser object associated with the user who is + * currently authenticated with Guacamole. + * * @param ldapConnection * The current connection to the LDAP server, associated with the * current user. @@ -81,16 +79,18 @@ public class UserService { * @throws GuacamoleException * If an error occurs preventing retrieval of users. */ - public Map getUsers(LdapNetworkConnection ldapConnection) - throws GuacamoleException { + public Map getUsers(LDAPAuthenticatedUser user, + LdapNetworkConnection ldapConnection) throws GuacamoleException { + LDAPConfiguration config = user.getLDAPConfiguration(); + // Retrieve all visible user objects - Collection usernameAttrs = confService.getUsernameAttributes(); + Collection usernameAttrs = config.getUsernameAttributes(); Collection attributes = new HashSet<>(usernameAttrs); - attributes.addAll(confService.getAttributes()); - List results = queryService.search(ldapConnection, - confService.getUserBaseDN(), - confService.getUserSearchFilter(), + attributes.addAll(config.getAttributes()); + List results = queryService.search(config, ldapConnection, + config.getUserBaseDN(), + config.getUserSearchFilter(), usernameAttrs, null, attributes); @@ -124,6 +124,9 @@ public class UserService { * is not enforced across the username attribute, it is possible that this * will return multiple DNs. * + * @param config + * The configuration of the LDAP server being queried. + * * @param ldapConnection * The connection to the LDAP server to use when querying user DNs. * @@ -139,14 +142,14 @@ public class UserService { * If an error occurs while querying the user DNs, or if the username * attribute property cannot be parsed within guacamole.properties. */ - public List getUserDNs(LdapNetworkConnection ldapConnection, + public List getUserDNs(LDAPConfiguration config, LdapNetworkConnection ldapConnection, String username) throws GuacamoleException { // Retrieve user objects having a matching username - List results = queryService.search(ldapConnection, - confService.getUserBaseDN(), - confService.getUserSearchFilter(), - confService.getUsernameAttributes(), + List results = queryService.search(config, ldapConnection, + config.getUserBaseDN(), + config.getUserSearchFilter(), + config.getUsernameAttributes(), username, Collections.singletonList("dn")); @@ -164,6 +167,9 @@ public class UserService { * or queried from the LDAP server, depending on how LDAP authentication * has been configured. * + * @param config + * The configuration of the LDAP server being queried. + * * @param username * The username of the user whose corresponding DN should be returned. * @@ -174,11 +180,11 @@ public class UserService { * If required properties are missing, and thus the user DN cannot be * determined. */ - public Dn deriveUserDN(String username) + public Dn deriveUserDN(LDAPConfiguration config, String username) throws GuacamoleException { // Pull username attributes from properties - List usernameAttributes = confService.getUsernameAttributes(); + List usernameAttributes = config.getUsernameAttributes(); // We need exactly one base DN to derive the user DN if (usernameAttributes.size() != 1) { @@ -193,7 +199,7 @@ public class UserService { // Derive user DN from base DN try { return new Dn(new Rdn(usernameAttributes.get(0), username), - confService.getUserBaseDN()); + config.getUserBaseDN()); } catch (LdapInvalidAttributeValueException | LdapInvalidDnException e) { throw new GuacamoleServerException("Error trying to derive user DN.", e);