GUACAMOLE-957: Allow each user to be associated with multiple LDAP servers.

This commit is contained in:
Michael Jumper
2021-10-20 21:29:41 -07:00
parent 07b443257f
commit 278bfa17ae
8 changed files with 361 additions and 158 deletions

View File

@@ -168,6 +168,74 @@ public class AuthenticationProviderService {
} }
/**
* Returns a new ConnectedLDAPConfiguration that is connected to an LDAP
* server associated with the user having the given username and bound
* using the provided password. All LDAP servers associated with the given
* user are tried until the connection and authentication attempt succeeds.
* If no LDAP servers are available, or no LDAP servers are associated with
* the given user, null is returned.
*
* @param username
* The username or DN of the user to bind as.
*
* @param password
* The password of the user to bind as.
*
* @return
* A new ConnectedLDAPConfiguration which is bound to an LDAP server
* using the provided credentials, or null if no LDAP servers are
* available for the given user or connecting/authenticating has
* failed.
*
* @throws GuacamoleException
* If configuration information for the user's LDAP server(s) cannot
* be retrieved.
*/
private ConnectedLDAPConfiguration getLDAPConfiguration(String username,
String password) throws GuacamoleException {
// Get relevant LDAP configurations for user
Collection<LDAPConfiguration> configs = confService.getLDAPConfigurations(username);
if (configs.isEmpty()) {
logger.info("User \"{}\" does not map to any defined LDAP configurations.", username);
return null;
}
// Try each possible LDAP configuration until the TCP connection and
// authentication are successful
for (LDAPConfiguration config : configs) {
// Derive DN of user within this LDAP server
Dn bindDn = getUserBindDN(config, username);
if (bindDn == null || bindDn.isEmpty()) {
logger.info("Unable to determine DN of user \"{}\" using LDAP "
+ "server \"{}\". Proceeding with next server...",
username, config.getServerHostname());
continue;
}
// Attempt bind (authentication)
LdapNetworkConnection ldapConnection = ldapService.bindAs(config, bindDn.getName(), password);
if (ldapConnection == null) {
logger.info("Unable to bind as user \"{}\" against LDAP "
+ "server \"{}\". Proceeding with next server...",
username, config.getServerHostname());
continue;
}
// Connection and bind were successful
logger.info("User \"{}\" was successfully authenticated by LDAP server \"{}\".", username, config.getServerHostname());
return new ConnectedLDAPConfiguration(config, bindDn, ldapConnection);
}
// No LDAP connection/authentication attempt succeeded
logger.info("User \"{}\" did not successfully authenticate against any LDAP server.", username);
return null;
}
/** /**
* Returns an AuthenticatedUser representing the user authenticated by the * Returns an AuthenticatedUser representing the user authenticated by the
* given credentials. Also adds custom LDAP attributes to the * given credentials. Also adds custom LDAP attributes to the
@@ -200,23 +268,8 @@ public class AuthenticationProviderService {
+ " authentication provider.", CredentialsInfo.USERNAME_PASSWORD); + " authentication provider.", CredentialsInfo.USERNAME_PASSWORD);
} }
// Get relevant LDAP configuration for user ConnectedLDAPConfiguration config = getLDAPConfiguration(username, password);
LDAPConfiguration config = confService.getLDAPConfiguration(username); if (config == null)
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);
}
// Attempt bind
LdapNetworkConnection ldapConnection =
ldapService.bindAs(config, bindDn.getName(), password);
if (ldapConnection == null)
throw new GuacamoleInvalidCredentialsException("Invalid login.", throw new GuacamoleInvalidCredentialsException("Invalid login.",
CredentialsInfo.USERNAME_PASSWORD); CredentialsInfo.USERNAME_PASSWORD);
@@ -224,22 +277,20 @@ public class AuthenticationProviderService {
// Retrieve group membership of the user that just authenticated // Retrieve group membership of the user that just authenticated
Set<String> effectiveGroups = Set<String> effectiveGroups =
userGroupService.getParentUserGroupIdentifiers(config, userGroupService.getParentUserGroupIdentifiers(config, config.getBindDN());
ldapConnection, bindDn);
// Return AuthenticatedUser if bind succeeds // Return AuthenticatedUser if bind succeeds
LDAPAuthenticatedUser authenticatedUser = authenticatedUserProvider.get(); LDAPAuthenticatedUser authenticatedUser = authenticatedUserProvider.get();
authenticatedUser.init(config, credentials, authenticatedUser.init(config, credentials,
getAttributeTokens(config, ldapConnection, bindDn), getAttributeTokens(config), effectiveGroups);
effectiveGroups, bindDn);
return authenticatedUser; return authenticatedUser;
} }
// Always disconnect catch (GuacamoleException | RuntimeException | Error e) {
finally { config.close();
ldapConnection.close(); throw e;
} }
} }
@@ -254,12 +305,6 @@ public class AuthenticationProviderService {
* @param config * @param config
* The configuration of the LDAP server being queried. * The configuration of the LDAP server being queried.
* *
* @param ldapConnection
* LDAP connection to use to read the attributes of the user.
*
* @param username
* The username of the user whose attributes are to be queried.
*
* @return * @return
* A map of parameter tokens generated from attributes on the user * A map of parameter tokens generated from attributes on the user
* currently bound under the given LDAP connection, as a map of token * currently bound under the given LDAP connection, as a map of token
@@ -269,8 +314,8 @@ public class AuthenticationProviderService {
* @throws GuacamoleException * @throws GuacamoleException
* If an error occurs retrieving the user DN or the attributes. * If an error occurs retrieving the user DN or the attributes.
*/ */
private Map<String, String> getAttributeTokens(LDAPConfiguration config, private Map<String, String> getAttributeTokens(ConnectedLDAPConfiguration config)
LdapNetworkConnection ldapConnection, Dn userDn) throws GuacamoleException { throws GuacamoleException {
// Get attributes from configuration information // Get attributes from configuration information
List<String> attrList = config.getAttributes(); List<String> attrList = config.getAttributes();
@@ -286,7 +331,7 @@ public class AuthenticationProviderService {
try { try {
// Get LDAP attributes by querying LDAP // Get LDAP attributes by querying LDAP
Entry userEntry = ldapConnection.lookup(userDn, attrArray); Entry userEntry = config.getLDAPConnection().lookup(config.getBindDN(), attrArray);
if (userEntry == null) if (userEntry == null)
return Collections.<String, String>emptyMap(); return Collections.<String, String>emptyMap();
@@ -326,37 +371,26 @@ public class AuthenticationProviderService {
public LDAPUserContext getUserContext(AuthenticatedUser authenticatedUser) public LDAPUserContext getUserContext(AuthenticatedUser authenticatedUser)
throws GuacamoleException { throws GuacamoleException {
// Bind using credentials associated with AuthenticatedUser
Credentials credentials = authenticatedUser.getCredentials();
if (authenticatedUser instanceof LDAPAuthenticatedUser) { if (authenticatedUser instanceof LDAPAuthenticatedUser) {
LDAPAuthenticatedUser ldapAuthenticatedUser = (LDAPAuthenticatedUser) authenticatedUser; LDAPAuthenticatedUser ldapAuthenticatedUser = (LDAPAuthenticatedUser) authenticatedUser;
LDAPConfiguration config = ldapAuthenticatedUser.getLDAPConfiguration(); ConnectedLDAPConfiguration 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.",
authenticatedUser.getIdentifier());
throw new GuacamoleInvalidCredentialsException("Invalid login.",
CredentialsInfo.USERNAME_PASSWORD);
}
try { try {
// Build user context by querying LDAP // Build user context by querying LDAP
LDAPUserContext userContext = userContextProvider.get(); LDAPUserContext userContext = userContextProvider.get();
userContext.init(ldapAuthenticatedUser, ldapConnection); userContext.init(ldapAuthenticatedUser);
return userContext; return userContext;
} }
// Always disconnect // Always disconnect
finally { finally {
ldapConnection.close(); config.close();
} }
} }
return null; return null;
} }

View File

@@ -0,0 +1,210 @@
/*
* 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 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.directory.ldap.client.api.LdapNetworkConnection;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.ldap.conf.EncryptionMethod;
import org.apache.guacamole.auth.ldap.conf.LDAPConfiguration;
import org.apache.guacamole.auth.ldap.conf.MemberAttributeType;
/**
* LDAPConfiguration implementation that is associated with an
* LdapNetworkConnection to the configured LDAP server.
*/
public class ConnectedLDAPConfiguration implements LDAPConfiguration, AutoCloseable {
/**
* The wrapped LDAPConfiguration.
*/
private final LDAPConfiguration config;
/**
* The connection to the LDAP server represented by this configuration.
*/
private final LdapNetworkConnection connection;
/**
* The LDAP DN that was used to bind with the LDAP server to produce
* {@link #connection}.
*/
private final Dn bindDn;
/**
* Creates a new ConnectedLDAPConfiguration that associates the given
* LdapNetworkConnection with the given LDAPConfiguration. All functions
* inherited from the LDAPConfiguration interface are delegated to the
* given LDAPConfiguration. It is the responsibility of the caller to
* ensure the provided LdapNetworkConnection is closed after it is no
* longer needed.
*
* @param config
* The LDAPConfiguration to wrap.
*
* @param bindDn
* The LDAP DN that was used to bind with the LDAP server to produce
* the given LdapNetworkConnection.
*
* @param connection
* The connection to the LDAP server represented by the given
* configuration.
*/
public ConnectedLDAPConfiguration(LDAPConfiguration config, Dn bindDn, LdapNetworkConnection connection) {
this.config = config;
this.bindDn = bindDn;
this.connection = connection;
}
/**
* Returns the LdapNetworkConnection for the connection to the LDAP server
* represented by this configuration. The lifecycle of this connection is
* managed externally. The connection is not guaranteed to still be
* connected.
*
* @return
* The LdapNetworkConnection for the connection to the LDAP server
* represented by this configuration.
*/
public LdapNetworkConnection getLDAPConnection() {
return connection;
}
/**
* Returns the LDAP DN that was used to bind with the LDAP server to
* produce the LdapNetworkConnection associated with this
* ConnectedLDAPConfiguration.
*
* @return
* The LDAP DN that was used to bind with the LDAP server.
*/
public Dn getBindDN() {
return bindDn;
}
@Override
public void close() {
connection.close();
}
@Override
public String getServerHostname() throws GuacamoleException {
return config.getServerHostname();
}
@Override
public int getServerPort() throws GuacamoleException {
return config.getServerPort();
}
@Override
public List<String> getUsernameAttributes() throws GuacamoleException {
return config.getUsernameAttributes();
}
@Override
public Dn getUserBaseDN() throws GuacamoleException {
return config.getUserBaseDN();
}
@Override
public Dn getConfigurationBaseDN() throws GuacamoleException {
return config.getConfigurationBaseDN();
}
@Override
public List<String> getGroupNameAttributes() throws GuacamoleException {
return config.getGroupNameAttributes();
}
@Override
public Dn getGroupBaseDN() throws GuacamoleException {
return config.getGroupBaseDN();
}
@Override
public String getSearchBindDN() throws GuacamoleException {
return config.getSearchBindDN();
}
@Override
public String getSearchBindPassword() throws GuacamoleException {
return config.getSearchBindPassword();
}
@Override
public EncryptionMethod getEncryptionMethod() throws GuacamoleException {
return config.getEncryptionMethod();
}
@Override
public int getMaxResults() throws GuacamoleException {
return config.getMaxResults();
}
@Override
public AliasDerefMode getDereferenceAliases() throws GuacamoleException {
return config.getDereferenceAliases();
}
@Override
public boolean getFollowReferrals() throws GuacamoleException {
return config.getFollowReferrals();
}
@Override
public int getMaxReferralHops() throws GuacamoleException {
return config.getMaxReferralHops();
}
@Override
public ExprNode getUserSearchFilter() throws GuacamoleException {
return config.getUserSearchFilter();
}
@Override
public ExprNode getGroupSearchFilter() throws GuacamoleException {
return config.getGroupSearchFilter();
}
@Override
public int getOperationTimeout() throws GuacamoleException {
return config.getOperationTimeout();
}
@Override
public List<String> getAttributes() throws GuacamoleException {
return config.getAttributes();
}
@Override
public String getMemberAttribute() throws GuacamoleException {
return config.getMemberAttribute();
}
@Override
public MemberAttributeType getMemberAttributeType() throws GuacamoleException {
return config.getMemberAttributeType();
}
}

View File

@@ -20,11 +20,13 @@
package org.apache.guacamole.auth.ldap.conf; package org.apache.guacamole.auth.ldap.conf;
import com.google.inject.Inject; import com.google.inject.Inject;
import java.util.Collection;
import java.util.Collections;
import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.environment.Environment; import org.apache.guacamole.environment.Environment;
/** /**
* Service for retrieving configuration information regarding the LDAP server. * Service for retrieving configuration information regarding LDAP servers.
*/ */
public class ConfigurationService { public class ConfigurationService {
@@ -35,24 +37,25 @@ public class ConfigurationService {
private Environment environment; private Environment environment;
/** /**
* Returns the configuration information for the LDAP server related to the * Returns the configuration information for the LDAP servers related to
* user having the given username. If no such LDAP server is defined, null * the user having the given username, if any. If multiple servers are
* is returned. * returned, each should be tried in order until a successful LDAP
* connection is established.
* *
* @param username * @param username
* The username of the user whose corresponding LDAP server * The username of the user whose corresponding LDAP server
* configuration should be retrieved. * configuration should be retrieved.
* *
* @return * @return
* The configuration of the LDAP server related to the user having the * The configurations of the LDAP servers related to the user having
* given username, or null if no such LDAP server is defined. * the given username.
* *
* @throws GuacamoleException * @throws GuacamoleException
* If the configuration information of the LDAP server related to the * If the configuration information of the LDAP servers related to the
* user having the given username cannot be retrieved due to an error. * user having the given username cannot be retrieved due to an error.
*/ */
public LDAPConfiguration getLDAPConfiguration(String username) throws GuacamoleException { public Collection<LDAPConfiguration> getLDAPConfigurations(String username) throws GuacamoleException {
return new EnvironmentLDAPConfiguration(environment); return Collections.singletonList(new EnvironmentLDAPConfiguration(environment));
} }
} }

View File

@@ -35,13 +35,11 @@ import org.apache.directory.api.ldap.model.filter.EqualityNode;
import org.apache.directory.api.ldap.model.filter.ExprNode; import org.apache.directory.api.ldap.model.filter.ExprNode;
import org.apache.directory.api.ldap.model.filter.OrNode; import org.apache.directory.api.ldap.model.filter.OrNode;
import org.apache.directory.api.ldap.model.name.Dn; 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.LDAPAuthenticationProvider;
import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.GuacamoleServerException; import org.apache.guacamole.GuacamoleServerException;
import org.apache.guacamole.auth.ldap.ConnectedLDAPConfiguration;
import org.apache.guacamole.auth.ldap.ObjectQueryService; 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.group.UserGroupService;
import org.apache.guacamole.auth.ldap.user.LDAPAuthenticatedUser; import org.apache.guacamole.auth.ldap.user.LDAPAuthenticatedUser;
import org.apache.guacamole.net.auth.Connection; import org.apache.guacamole.net.auth.Connection;
@@ -117,17 +115,12 @@ public class ConnectionService {
/** /**
* Returns all Guacamole connections accessible to the user currently bound * Returns all Guacamole connections accessible to the given user.
* under the given LDAP connection.
* *
* @param user * @param user
* The AuthenticatedUser object associated with the user who is * The AuthenticatedUser object associated with the user who is
* currently authenticated with Guacamole. * currently authenticated with Guacamole.
* *
* @param ldapConnection
* The current connection to the LDAP server, associated with the
* current user.
*
* @return * @return
* All connections accessible to the user currently bound under the * All connections accessible to the user currently bound under the
* given LDAP connection, as a map of connection identifier to * given LDAP connection, as a map of connection identifier to
@@ -136,10 +129,10 @@ public class ConnectionService {
* @throws GuacamoleException * @throws GuacamoleException
* If an error occurs preventing retrieval of connections. * If an error occurs preventing retrieval of connections.
*/ */
public Map<String, Connection> getConnections(LDAPAuthenticatedUser user, public Map<String, Connection> getConnections(LDAPAuthenticatedUser user)
LdapNetworkConnection ldapConnection) throws GuacamoleException { throws GuacamoleException {
LDAPConfiguration ldapConfig = user.getLDAPConfiguration(); ConnectedLDAPConfiguration ldapConfig = user.getLDAPConfiguration();
// Do not return any connections if base DN is not specified // Do not return any connections if base DN is not specified
Dn configurationBaseDN = ldapConfig.getConfigurationBaseDN(); Dn configurationBaseDN = ldapConfig.getConfigurationBaseDN();
@@ -148,24 +141,15 @@ public class ConnectionService {
try { try {
// Pull the current user DN from the LDAP connection
LdapConnectionConfig ldapConnectionConfig = ldapConnection.getConfig();
Dn userDN = new Dn(ldapConnectionConfig.getName());
// getConnections() will only be called after a connection has been
// authenticated (via non-anonymous bind), thus userDN cannot
// possibly be null
assert(userDN != null);
// Get the search filter for finding connections accessible by the // Get the search filter for finding connections accessible by the
// current user // current user
ExprNode connectionSearchFilter = getConnectionSearchFilter(user, userDN, ldapConnection); ExprNode connectionSearchFilter = getConnectionSearchFilter(user);
// Find all Guacamole connections for the given user by // Find all Guacamole connections for the given user by
// looking for direct membership in the guacConfigGroup // looking for direct membership in the guacConfigGroup
// and possibly any groups the user is a member of that are // and possibly any groups the user is a member of that are
// referred to in the seeAlso attribute of the guacConfigGroup. // referred to in the seeAlso attribute of the guacConfigGroup.
List<Entry> results = queryService.search(ldapConfig, ldapConnection, List<Entry> results = queryService.search(ldapConfig, ldapConfig.getLDAPConnection(),
configurationBaseDN, connectionSearchFilter, 0, GUAC_CONFIG_LDAP_ATTRIBUTES); configurationBaseDN, connectionSearchFilter, 0, GUAC_CONFIG_LDAP_ATTRIBUTES);
// Return a map of all readable connections // Return a map of all readable connections
@@ -271,19 +255,12 @@ public class ConnectionService {
/** /**
* Returns an LDAP search filter which queries all connections accessible * Returns an LDAP search filter which queries all connections accessible
* by the user having the given DN. * by the given user.
* *
* @param user * @param user
* The AuthenticatedUser object associated with the user who is * The AuthenticatedUser object associated with the user who is
* currently authenticated with Guacamole. * currently authenticated with Guacamole.
* *
* @param userDN
* DN of the user to search for associated guacConfigGroup connections.
*
* @param ldapConnection
* LDAP connection to use if additional information must be queried to
* produce the filter, such as groups driving RBAC.
*
* @return * @return
* An LDAP search filter which queries all guacConfigGroup objects * An LDAP search filter which queries all guacConfigGroup objects
* accessible by the user having the given DN. * accessible by the user having the given DN.
@@ -294,11 +271,11 @@ public class ConnectionService {
* @throws GuacamoleException * @throws GuacamoleException
* If an error occurs retrieving the group base DN. * If an error occurs retrieving the group base DN.
*/ */
private ExprNode getConnectionSearchFilter(LDAPAuthenticatedUser user, private ExprNode getConnectionSearchFilter(LDAPAuthenticatedUser user)
Dn userDN, LdapNetworkConnection ldapConnection)
throws LdapException, GuacamoleException { throws LdapException, GuacamoleException {
LDAPConfiguration config = user.getLDAPConfiguration(); ConnectedLDAPConfiguration config = user.getLDAPConfiguration();
Dn userDN = config.getBindDN();
AndNode searchFilter = new AndNode(); AndNode searchFilter = new AndNode();
@@ -312,7 +289,7 @@ public class ConnectionService {
// Additionally filter by group membership if the current user is a // Additionally filter by group membership if the current user is a
// member of any user groups // member of any user groups
List<Entry> userGroups = userGroupService.getParentUserGroupEntries(config, ldapConnection, userDN); List<Entry> userGroups = userGroupService.getParentUserGroupEntries(config, userDN);
if (!userGroups.isEmpty()) { if (!userGroups.isEmpty()) {
userGroups.forEach(entry -> userGroups.forEach(entry ->
groupFilter.addNode(new EqualityNode(LDAP_ATTRIBUTE_NAME_GROUPS,entry.getDn().toString())) groupFilter.addNode(new EqualityNode(LDAP_ATTRIBUTE_NAME_GROUPS,entry.getDn().toString()))

View File

@@ -33,9 +33,9 @@ import org.apache.directory.api.ldap.model.filter.EqualityNode;
import org.apache.directory.api.ldap.model.filter.ExprNode; 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.filter.NotNode;
import org.apache.directory.api.ldap.model.name.Dn; import org.apache.directory.api.ldap.model.name.Dn;
import org.apache.directory.ldap.client.api.LdapNetworkConnection;
import org.apache.guacamole.auth.ldap.conf.MemberAttributeType; import org.apache.guacamole.auth.ldap.conf.MemberAttributeType;
import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.ldap.ConnectedLDAPConfiguration;
import org.apache.guacamole.auth.ldap.ObjectQueryService; import org.apache.guacamole.auth.ldap.ObjectQueryService;
import org.apache.guacamole.auth.ldap.conf.LDAPConfiguration; import org.apache.guacamole.auth.ldap.conf.LDAPConfiguration;
import org.apache.guacamole.auth.ldap.user.LDAPAuthenticatedUser; import org.apache.guacamole.auth.ldap.user.LDAPAuthenticatedUser;
@@ -98,17 +98,12 @@ public class UserGroupService {
} }
/** /**
* Returns all Guacamole user groups accessible to the user currently bound * Returns all Guacamole user groups accessible to the given user.
* under the given LDAP connection.
* *
* @param user * @param user
* The AuthenticatedUser object associated with the user who is * The AuthenticatedUser object associated with the user who is
* currently authenticated with Guacamole. * currently authenticated with Guacamole.
* *
* @param ldapConnection
* The current connection to the LDAP server, associated with the
* current user.
*
* @return * @return
* All user groups accessible to the user currently bound under the * All user groups accessible to the user currently bound under the
* given LDAP connection, as a map of user group identifier to * given LDAP connection, as a map of user group identifier to
@@ -117,10 +112,10 @@ public class UserGroupService {
* @throws GuacamoleException * @throws GuacamoleException
* If an error occurs preventing retrieval of user groups. * If an error occurs preventing retrieval of user groups.
*/ */
public Map<String, UserGroup> getUserGroups(LDAPAuthenticatedUser user, public Map<String, UserGroup> getUserGroups(LDAPAuthenticatedUser user)
LdapNetworkConnection ldapConnection) throws GuacamoleException { throws GuacamoleException {
LDAPConfiguration config = user.getLDAPConfiguration(); ConnectedLDAPConfiguration config = user.getLDAPConfiguration();
// Do not return any user groups if base DN is not specified // Do not return any user groups if base DN is not specified
Dn groupBaseDN = config.getGroupBaseDN(); Dn groupBaseDN = config.getGroupBaseDN();
@@ -136,7 +131,7 @@ public class UserGroupService {
Collection<String> attributes = config.getGroupNameAttributes(); Collection<String> attributes = config.getGroupNameAttributes();
List<Entry> results = queryService.search( List<Entry> results = queryService.search(
config, config,
ldapConnection, config.getLDAPConnection(),
groupBaseDN, groupBaseDN,
getGroupSearchFilter(config), getGroupSearchFilter(config),
attributes, attributes,
@@ -175,10 +170,6 @@ public class UserGroupService {
* @param config * @param config
* The configuration of the LDAP server being queried. * The configuration of the LDAP server being queried.
* *
* @param ldapConnection
* The current connection to the LDAP server, associated with the
* current user.
*
* @param userDN * @param userDN
* The DN of the user whose group membership should be retrieved. * The DN of the user whose group membership should be retrieved.
* *
@@ -189,8 +180,7 @@ public class UserGroupService {
* @throws GuacamoleException * @throws GuacamoleException
* If an error occurs preventing retrieval of user groups. * If an error occurs preventing retrieval of user groups.
*/ */
public List<Entry> getParentUserGroupEntries(LDAPConfiguration config, public List<Entry> getParentUserGroupEntries(ConnectedLDAPConfiguration config, Dn userDN)
LdapNetworkConnection ldapConnection, Dn userDN)
throws GuacamoleException { throws GuacamoleException {
// Do not return any user groups if base DN is not specified // Do not return any user groups if base DN is not specified
@@ -206,7 +196,7 @@ public class UserGroupService {
// Retrieve user objects with userDN // Retrieve user objects with userDN
List<Entry> userEntries = queryService.search( List<Entry> userEntries = queryService.search(
config, config,
ldapConnection, config.getLDAPConnection(),
userDN, userDN,
config.getUserSearchFilter(), config.getUserSearchFilter(),
0, 0,
@@ -240,7 +230,7 @@ public class UserGroupService {
// excluding guacConfigGroups // excluding guacConfigGroups
return queryService.search( return queryService.search(
config, config,
ldapConnection, config.getLDAPConnection(),
groupBaseDN, groupBaseDN,
getGroupSearchFilter(config), getGroupSearchFilter(config),
Collections.singleton(memberAttribute), Collections.singleton(memberAttribute),
@@ -258,10 +248,6 @@ public class UserGroupService {
* @param config * @param config
* The configuration of the LDAP server being queried. * The configuration of the LDAP server being queried.
* *
* @param ldapConnection
* The current connection to the LDAP server, associated with the
* current user.
*
* @param userDN * @param userDN
* The DN of the user whose group membership should be retrieved. * The DN of the user whose group membership should be retrieved.
* *
@@ -272,12 +258,11 @@ public class UserGroupService {
* @throws GuacamoleException * @throws GuacamoleException
* If an error occurs preventing retrieval of user groups. * If an error occurs preventing retrieval of user groups.
*/ */
public Set<String> getParentUserGroupIdentifiers(LDAPConfiguration config, public Set<String> getParentUserGroupIdentifiers(ConnectedLDAPConfiguration config, Dn userDN)
LdapNetworkConnection ldapConnection, Dn userDN)
throws GuacamoleException { throws GuacamoleException {
Collection<String> attributes = config.getGroupNameAttributes(); Collection<String> attributes = config.getGroupNameAttributes();
List<Entry> userGroups = getParentUserGroupEntries(config, ldapConnection, userDN); List<Entry> userGroups = getParentUserGroupEntries(config, userDN);
Set<String> identifiers = new HashSet<>(userGroups.size()); Set<String> identifiers = new HashSet<>(userGroups.size());
userGroups.forEach(entry -> { userGroups.forEach(entry -> {

View File

@@ -24,7 +24,7 @@ import java.util.Collections;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import org.apache.directory.api.ldap.model.name.Dn; import org.apache.directory.api.ldap.model.name.Dn;
import org.apache.guacamole.auth.ldap.conf.LDAPConfiguration; import org.apache.guacamole.auth.ldap.ConnectedLDAPConfiguration;
import org.apache.guacamole.net.auth.AbstractAuthenticatedUser; import org.apache.guacamole.net.auth.AbstractAuthenticatedUser;
import org.apache.guacamole.net.auth.AuthenticationProvider; import org.apache.guacamole.net.auth.AuthenticationProvider;
import org.apache.guacamole.net.auth.Credentials; import org.apache.guacamole.net.auth.Credentials;
@@ -68,11 +68,11 @@ public class LDAPAuthenticatedUser extends AbstractAuthenticatedUser {
* The configuration of the LDAP server that should be used for all queries * The configuration of the LDAP server that should be used for all queries
* related to this AuthenticatedUser. * related to this AuthenticatedUser.
*/ */
private LDAPConfiguration config; private ConnectedLDAPConfiguration config;
/** /**
* Initializes this AuthenticatedUser with the given credentials, * Initializes this AuthenticatedUser with the given credentials,
* connection parameter tokens. and set of effective user groups. * connection parameter tokens, and set of effective user groups.
* *
* @param config * @param config
* The configuration of the LDAP server that should be used for all * The configuration of the LDAP server that should be used for all
@@ -88,17 +88,14 @@ public class LDAPAuthenticatedUser extends AbstractAuthenticatedUser {
* @param effectiveGroups * @param effectiveGroups
* The unique identifiers of all user groups which affect the * The unique identifiers of all user groups which affect the
* permissions available to this user. * permissions available to this user.
*
* @param bindDn
* The LDAP DN used to bind this user.
*/ */
public void init(LDAPConfiguration config, Credentials credentials, public void init(ConnectedLDAPConfiguration config, Credentials credentials,
Map<String, String> tokens, Set<String> effectiveGroups, Dn bindDn) { Map<String, String> tokens, Set<String> effectiveGroups) {
this.config = config; this.config = config;
this.credentials = credentials; this.credentials = credentials;
this.tokens = Collections.unmodifiableMap(tokens); this.tokens = Collections.unmodifiableMap(tokens);
this.effectiveGroups = effectiveGroups; this.effectiveGroups = effectiveGroups;
this.bindDn = bindDn; this.bindDn = config.getBindDN();
setIdentifier(credentials.getUsername()); setIdentifier(credentials.getUsername());
} }
@@ -134,7 +131,7 @@ public class LDAPAuthenticatedUser extends AbstractAuthenticatedUser {
* The configuration of the LDAP server related to this * The configuration of the LDAP server related to this
* AuthenticatedUser. * AuthenticatedUser.
*/ */
public LDAPConfiguration getLDAPConfiguration() { public ConnectedLDAPConfiguration getLDAPConfiguration() {
return config; return config;
} }
@@ -153,4 +150,9 @@ public class LDAPAuthenticatedUser extends AbstractAuthenticatedUser {
return effectiveGroups; return effectiveGroups;
} }
@Override
public void invalidate() {
config.close();
}
} }

View File

@@ -21,7 +21,6 @@ package org.apache.guacamole.auth.ldap.user;
import com.google.inject.Inject; import com.google.inject.Inject;
import java.util.Collections; import java.util.Collections;
import org.apache.directory.ldap.client.api.LdapNetworkConnection;
import org.apache.guacamole.auth.ldap.connection.ConnectionService; import org.apache.guacamole.auth.ldap.connection.ConnectionService;
import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.ldap.LDAPAuthenticationProvider; import org.apache.guacamole.auth.ldap.LDAPAuthenticationProvider;
@@ -100,38 +99,32 @@ public class LDAPUserContext extends AbstractUserContext {
private ConnectionGroup rootGroup; private ConnectionGroup rootGroup;
/** /**
* Initializes this UserContext using the provided AuthenticatedUser and * Initializes this UserContext using the provided AuthenticatedUser.
* LdapNetworkConnection.
* *
* @param user * @param user
* The AuthenticatedUser representing the user that authenticated. This * The AuthenticatedUser representing the user that authenticated. This
* user will always have been authenticated via LDAP, as LDAP data is * user will always have been authenticated via LDAP, as LDAP data is
* not provided to non-LDAP users. * not provided to non-LDAP users.
* *
* @param ldapConnection
* The connection to the LDAP server to use when querying accessible
* Guacamole users and connections.
*
* @throws GuacamoleException * @throws GuacamoleException
* If associated data stored within the LDAP directory cannot be * If associated data stored within the LDAP directory cannot be
* queried due to an error. * queried due to an error.
*/ */
public void init(LDAPAuthenticatedUser user, LdapNetworkConnection ldapConnection) public void init(LDAPAuthenticatedUser user) throws GuacamoleException {
throws GuacamoleException {
// Query all accessible users // Query all accessible users
userDirectory = new SimpleDirectory<>( userDirectory = new SimpleDirectory<>(
userService.getUsers(user, ldapConnection) userService.getUsers(user)
); );
// Query all accessible user groups // Query all accessible user groups
userGroupDirectory = new SimpleDirectory<>( userGroupDirectory = new SimpleDirectory<>(
userGroupService.getUserGroups(user, ldapConnection) userGroupService.getUserGroups(user)
); );
// Query all accessible connections // Query all accessible connections
connectionDirectory = new SimpleDirectory<>( connectionDirectory = new SimpleDirectory<>(
connectionService.getConnections(user, ldapConnection) connectionService.getConnections(user)
); );
// Root group contains only connections // Root group contains only connections

View File

@@ -34,6 +34,7 @@ import org.apache.directory.api.ldap.model.name.Rdn;
import org.apache.directory.ldap.client.api.LdapNetworkConnection; import org.apache.directory.ldap.client.api.LdapNetworkConnection;
import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.GuacamoleServerException; import org.apache.guacamole.GuacamoleServerException;
import org.apache.guacamole.auth.ldap.ConnectedLDAPConfiguration;
import org.apache.guacamole.auth.ldap.conf.LDAPGuacamoleProperties; import org.apache.guacamole.auth.ldap.conf.LDAPGuacamoleProperties;
import org.apache.guacamole.auth.ldap.ObjectQueryService; import org.apache.guacamole.auth.ldap.ObjectQueryService;
import org.apache.guacamole.auth.ldap.conf.LDAPConfiguration; import org.apache.guacamole.auth.ldap.conf.LDAPConfiguration;
@@ -60,17 +61,12 @@ public class UserService {
private ObjectQueryService queryService; private ObjectQueryService queryService;
/** /**
* Returns all Guacamole users accessible to the user currently bound under * Returns all Guacamole users accessible to the given user.
* the given LDAP connection.
* *
* @param user * @param user
* The AuthenticatedUser object associated with the user who is * The AuthenticatedUser object associated with the user who is
* currently authenticated with Guacamole. * currently authenticated with Guacamole.
* *
* @param ldapConnection
* The current connection to the LDAP server, associated with the
* current user.
*
* @return * @return
* All users accessible to the user currently bound under the given * All users accessible to the user currently bound under the given
* LDAP connection, as a map of connection identifier to corresponding * LDAP connection, as a map of connection identifier to corresponding
@@ -79,21 +75,24 @@ public class UserService {
* @throws GuacamoleException * @throws GuacamoleException
* If an error occurs preventing retrieval of users. * If an error occurs preventing retrieval of users.
*/ */
public Map<String, User> getUsers(LDAPAuthenticatedUser user, public Map<String, User> getUsers(LDAPAuthenticatedUser user)
LdapNetworkConnection ldapConnection) throws GuacamoleException { throws GuacamoleException {
LDAPConfiguration config = user.getLDAPConfiguration(); ConnectedLDAPConfiguration config = user.getLDAPConfiguration();
// Retrieve all visible user objects // Retrieve all visible user objects
Collection<String> usernameAttrs = config.getUsernameAttributes(); Collection<String> usernameAttrs = config.getUsernameAttributes();
Collection<String> attributes = new HashSet<>(usernameAttrs); Collection<String> attributes = new HashSet<>(usernameAttrs);
attributes.addAll(config.getAttributes()); attributes.addAll(config.getAttributes());
List<Entry> results = queryService.search(config, ldapConnection, List<Entry> results = queryService.search(
config,
config.getLDAPConnection(),
config.getUserBaseDN(), config.getUserBaseDN(),
config.getUserSearchFilter(), config.getUserSearchFilter(),
usernameAttrs, usernameAttrs,
null, null,
attributes); attributes
);
// Convert retrieved users to map of identifier to Guacamole user object // Convert retrieved users to map of identifier to Guacamole user object
return queryService.asMap(results, entry -> { return queryService.asMap(results, entry -> {