mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-06 05:07:41 +00:00
GUACAMOLE-220: Retrieve user groups from LDAP. Take immediate group membership into account.
This commit is contained in:
@@ -23,9 +23,11 @@ import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
import com.novell.ldap.LDAPConnection;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import org.apache.guacamole.auth.ldap.user.AuthenticatedUser;
|
||||
import org.apache.guacamole.auth.ldap.user.UserContext;
|
||||
import org.apache.guacamole.GuacamoleException;
|
||||
import org.apache.guacamole.auth.ldap.group.UserGroupService;
|
||||
import org.apache.guacamole.auth.ldap.user.UserService;
|
||||
import org.apache.guacamole.net.auth.Credentials;
|
||||
import org.apache.guacamole.net.auth.credentials.CredentialsInfo;
|
||||
@@ -62,6 +64,12 @@ public class AuthenticationProviderService {
|
||||
@Inject
|
||||
private UserService userService;
|
||||
|
||||
/**
|
||||
* Service for retrieving user groups.
|
||||
*/
|
||||
@Inject
|
||||
private UserGroupService userGroupService;
|
||||
|
||||
/**
|
||||
* Provider for AuthenticatedUser objects.
|
||||
*/
|
||||
@@ -222,9 +230,14 @@ public class AuthenticationProviderService {
|
||||
|
||||
try {
|
||||
|
||||
// Retrieve group membership of the user that just authenticated
|
||||
Set<String> effectiveGroups =
|
||||
userGroupService.getParentUserGroupIdentifiers(ldapConnection,
|
||||
ldapConnection.getAuthenticationDN());
|
||||
|
||||
// Return AuthenticatedUser if bind succeeds
|
||||
AuthenticatedUser authenticatedUser = authenticatedUserProvider.get();
|
||||
authenticatedUser.init(credentials);
|
||||
authenticatedUser.init(credentials, effectiveGroups);
|
||||
return authenticatedUser;
|
||||
|
||||
}
|
||||
|
@@ -23,6 +23,7 @@ import com.google.inject.AbstractModule;
|
||||
import org.apache.guacamole.auth.ldap.connection.ConnectionService;
|
||||
import org.apache.guacamole.auth.ldap.user.UserService;
|
||||
import org.apache.guacamole.GuacamoleException;
|
||||
import org.apache.guacamole.auth.ldap.group.UserGroupService;
|
||||
import org.apache.guacamole.environment.Environment;
|
||||
import org.apache.guacamole.environment.LocalEnvironment;
|
||||
import org.apache.guacamole.net.auth.AuthenticationProvider;
|
||||
@@ -78,6 +79,7 @@ public class LDAPAuthenticationProviderModule extends AbstractModule {
|
||||
bind(EscapingService.class);
|
||||
bind(LDAPConnectionService.class);
|
||||
bind(ObjectQueryService.class);
|
||||
bind(UserGroupService.class);
|
||||
bind(UserService.class);
|
||||
|
||||
}
|
||||
|
@@ -24,8 +24,6 @@ import com.novell.ldap.LDAPAttribute;
|
||||
import com.novell.ldap.LDAPConnection;
|
||||
import com.novell.ldap.LDAPEntry;
|
||||
import com.novell.ldap.LDAPException;
|
||||
import com.novell.ldap.LDAPReferralException;
|
||||
import com.novell.ldap.LDAPSearchResults;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
@@ -36,6 +34,7 @@ import org.apache.guacamole.auth.ldap.EscapingService;
|
||||
import org.apache.guacamole.GuacamoleException;
|
||||
import org.apache.guacamole.GuacamoleServerException;
|
||||
import org.apache.guacamole.auth.ldap.ObjectQueryService;
|
||||
import org.apache.guacamole.auth.ldap.group.UserGroupService;
|
||||
import org.apache.guacamole.net.auth.AuthenticatedUser;
|
||||
import org.apache.guacamole.net.auth.Connection;
|
||||
import org.apache.guacamole.net.auth.simple.SimpleConnection;
|
||||
@@ -74,6 +73,12 @@ public class ConnectionService {
|
||||
@Inject
|
||||
private ObjectQueryService queryService;
|
||||
|
||||
/**
|
||||
* Service for retrieving user groups.
|
||||
*/
|
||||
@Inject
|
||||
private UserGroupService userGroupService;
|
||||
|
||||
/**
|
||||
* Returns all Guacamole connections accessible to the user currently bound
|
||||
* under the given LDAP connection.
|
||||
@@ -226,43 +231,12 @@ public class ConnectionService {
|
||||
connectionSearchFilter.append(escapingService.escapeLDAPSearchFilter(userDN));
|
||||
connectionSearchFilter.append(")");
|
||||
|
||||
// If group base DN is specified search for user groups
|
||||
String groupBaseDN = confService.getGroupBaseDN();
|
||||
if (groupBaseDN != null) {
|
||||
|
||||
// Get all groups the user is a member of starting at the groupBaseDN, excluding guacConfigGroups
|
||||
LDAPSearchResults userRoleGroupResults = ldapConnection.search(
|
||||
groupBaseDN,
|
||||
LDAPConnection.SCOPE_SUB,
|
||||
"(&(!(objectClass=guacConfigGroup))(member=" + escapingService.escapeLDAPSearchFilter(userDN) + "))",
|
||||
null,
|
||||
false,
|
||||
confService.getLDAPSearchConstraints()
|
||||
);
|
||||
|
||||
// Append the additional user groups to the LDAP filter
|
||||
// Now the filter will also look for guacConfigGroups that refer
|
||||
// to groups the user is a member of
|
||||
// The guacConfig group uses the seeAlso attribute to refer
|
||||
// to these other groups
|
||||
while (userRoleGroupResults.hasMore()) {
|
||||
try {
|
||||
LDAPEntry entry = userRoleGroupResults.next();
|
||||
connectionSearchFilter.append("(seeAlso=").append(escapingService.escapeLDAPSearchFilter(entry.getDN())).append(")");
|
||||
}
|
||||
|
||||
catch (LDAPReferralException e) {
|
||||
if (confService.getFollowReferrals()) {
|
||||
logger.error("Could not follow referral: {}", e.getFailedReferral());
|
||||
logger.debug("Error encountered trying to follow referral.", e);
|
||||
throw new GuacamoleServerException("Could not follow LDAP referral.", e);
|
||||
}
|
||||
else {
|
||||
logger.warn("Given a referral, but referrals are disabled. Error was: {}", e.getMessage());
|
||||
logger.debug("Got a referral, but configured to not follow them.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Additionally filter by group membership if the current user is a
|
||||
// member of any user groups
|
||||
List<LDAPEntry> userGroups = userGroupService.getParentUserGroupEntries(ldapConnection, userDN);
|
||||
if (!userGroups.isEmpty()) {
|
||||
for (LDAPEntry entry : userGroups)
|
||||
connectionSearchFilter.append("(seeAlso=").append(escapingService.escapeLDAPSearchFilter(entry.getDN())).append(")");
|
||||
}
|
||||
|
||||
// Complete the search filter.
|
||||
|
@@ -0,0 +1,224 @@
|
||||
/*
|
||||
* 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.group;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import com.novell.ldap.LDAPConnection;
|
||||
import com.novell.ldap.LDAPEntry;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import org.apache.guacamole.auth.ldap.ConfigurationService;
|
||||
import org.apache.guacamole.GuacamoleException;
|
||||
import org.apache.guacamole.auth.ldap.ObjectQueryService;
|
||||
import org.apache.guacamole.net.auth.UserGroup;
|
||||
import org.apache.guacamole.net.auth.simple.SimpleUserGroup;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Service for querying user group membership and retrieving user groups
|
||||
* visible to a particular Guacamole user.
|
||||
*/
|
||||
public class UserGroupService {
|
||||
|
||||
/**
|
||||
* Logger for this class.
|
||||
*/
|
||||
private final Logger logger = LoggerFactory.getLogger(UserGroupService.class);
|
||||
|
||||
/**
|
||||
* Service for retrieving LDAP server configuration information.
|
||||
*/
|
||||
@Inject
|
||||
private ConfigurationService confService;
|
||||
|
||||
/**
|
||||
* Service for executing LDAP queries.
|
||||
*/
|
||||
@Inject
|
||||
private ObjectQueryService queryService;
|
||||
|
||||
/**
|
||||
* Returns the base search filter which should be used to retrieve user
|
||||
* groups which do not represent Guacamole connections. As excluding the
|
||||
* guacConfigGroup object class may not work as expected (may always return
|
||||
* zero results) if guacConfigGroup object class is not defined, it should
|
||||
* only be explicitly excluded if it is expected to have been defined.
|
||||
*
|
||||
* @return
|
||||
* The base search filter which should be used to retrieve user groups.
|
||||
*
|
||||
* @throws GuacamoleException
|
||||
* If guacamole.properties cannot be parsed.
|
||||
*/
|
||||
private String getGroupSearchFilter() throws GuacamoleException {
|
||||
|
||||
// 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)
|
||||
return "(!(objectClass=guacConfigGroup))";
|
||||
|
||||
// Read any object as a group if LDAP is not being used for connection
|
||||
// storage (guacConfigGroup)
|
||||
return "(objectClass=*)";
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all Guacamole user groups accessible to the user currently bound
|
||||
* under the given LDAP connection.
|
||||
*
|
||||
* @param ldapConnection
|
||||
* The current connection to the LDAP server, associated with the
|
||||
* current user.
|
||||
*
|
||||
* @return
|
||||
* All user groups accessible to the user currently bound under the
|
||||
* given LDAP connection, as a map of user group identifier to
|
||||
* corresponding UserGroup object.
|
||||
*
|
||||
* @throws GuacamoleException
|
||||
* If an error occurs preventing retrieval of user groups.
|
||||
*/
|
||||
public Map<String, UserGroup> getUserGroups(LDAPConnection ldapConnection)
|
||||
throws GuacamoleException {
|
||||
|
||||
// Do not return any user groups if base DN is not specified
|
||||
String groupBaseDN = confService.getGroupBaseDN();
|
||||
if (groupBaseDN == null)
|
||||
return Collections.<String, UserGroup>emptyMap();
|
||||
|
||||
// Retrieve all visible user groups which are not guacConfigGroups
|
||||
Collection<String> attributes = confService.getGroupNameAttributes();
|
||||
List<LDAPEntry> results = queryService.search(
|
||||
ldapConnection,
|
||||
groupBaseDN,
|
||||
getGroupSearchFilter(),
|
||||
attributes,
|
||||
null
|
||||
);
|
||||
|
||||
// Convert retrieved user groups to map of identifier to Guacamole
|
||||
// user group object
|
||||
return queryService.asMap(results, entry -> {
|
||||
|
||||
// Translate entry into UserGroup object having proper identifier
|
||||
String name = queryService.getIdentifier(entry, attributes);
|
||||
if (name != null)
|
||||
return new SimpleUserGroup(name);
|
||||
|
||||
// Ignore user groups which lack a name attribute
|
||||
logger.debug("User group \"{}\" is missing a name attribute "
|
||||
+ "and will be ignored.", entry.getDN());
|
||||
return null;
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the LDAP entries representing all user groups that the given
|
||||
* user is a member of. Only user groups which are readable by the current
|
||||
* user will be retrieved.
|
||||
*
|
||||
* @param ldapConnection
|
||||
* The current connection to the LDAP server, associated with the
|
||||
* current user.
|
||||
*
|
||||
* @param userDN
|
||||
* The DN of the user whose group membership should be retrieved.
|
||||
*
|
||||
* @return
|
||||
* The LDAP entries representing all readable parent user groups of the
|
||||
* user having the given DN.
|
||||
*
|
||||
* @throws GuacamoleException
|
||||
* If an error occurs preventing retrieval of user groups.
|
||||
*/
|
||||
public List<LDAPEntry> getParentUserGroupEntries(LDAPConnection ldapConnection,
|
||||
String userDN) throws GuacamoleException {
|
||||
|
||||
// Do not return any user groups if base DN is not specified
|
||||
String groupBaseDN = confService.getGroupBaseDN();
|
||||
if (groupBaseDN == null)
|
||||
return Collections.<LDAPEntry>emptyList();
|
||||
|
||||
// Get all groups the user is a member of starting at the groupBaseDN,
|
||||
// excluding guacConfigGroups
|
||||
return queryService.search(
|
||||
ldapConnection,
|
||||
groupBaseDN,
|
||||
getGroupSearchFilter(),
|
||||
Collections.singleton("member"),
|
||||
userDN
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the identifiers of all user groups that the given user is a
|
||||
* member of. Only identifiers of user groups which are readable by the
|
||||
* current user will be retrieved.
|
||||
*
|
||||
* @param ldapConnection
|
||||
* The current connection to the LDAP server, associated with the
|
||||
* current user.
|
||||
*
|
||||
* @param userDN
|
||||
* The DN of the user whose group membership should be retrieved.
|
||||
*
|
||||
* @return
|
||||
* The identifiers of all readable parent user groups of the user
|
||||
* having the given DN.
|
||||
*
|
||||
* @throws GuacamoleException
|
||||
* If an error occurs preventing retrieval of user groups.
|
||||
*/
|
||||
public Set<String> getParentUserGroupIdentifiers(LDAPConnection ldapConnection,
|
||||
String userDN) throws GuacamoleException {
|
||||
|
||||
Collection<String> attributes = confService.getGroupNameAttributes();
|
||||
List<LDAPEntry> userGroups = getParentUserGroupEntries(ldapConnection, userDN);
|
||||
|
||||
Set<String> identifiers = new HashSet<>(userGroups.size());
|
||||
userGroups.forEach(entry -> {
|
||||
|
||||
// Determine unique identifier for user group
|
||||
String name = queryService.getIdentifier(entry, attributes);
|
||||
if (name != null)
|
||||
identifiers.add(name);
|
||||
|
||||
// Ignore user groups which lack a name attribute
|
||||
else
|
||||
logger.debug("User group \"{}\" is missing a name attribute "
|
||||
+ "and will be ignored.", entry.getDN());
|
||||
|
||||
});
|
||||
|
||||
return identifiers;
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -20,6 +20,7 @@
|
||||
package org.apache.guacamole.auth.ldap.user;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import java.util.Set;
|
||||
import org.apache.guacamole.net.auth.AbstractAuthenticatedUser;
|
||||
import org.apache.guacamole.net.auth.AuthenticationProvider;
|
||||
import org.apache.guacamole.net.auth.Credentials;
|
||||
@@ -43,13 +44,25 @@ public class AuthenticatedUser extends AbstractAuthenticatedUser {
|
||||
private Credentials credentials;
|
||||
|
||||
/**
|
||||
* Initializes this AuthenticatedUser using the given credentials.
|
||||
* The unique identifiers of all user groups which affect the permissions
|
||||
* available to this user.
|
||||
*/
|
||||
private Set<String> effectiveGroups;
|
||||
|
||||
/**
|
||||
* Initializes this AuthenticatedUser with the given credentials and set of
|
||||
* effective user groups.
|
||||
*
|
||||
* @param credentials
|
||||
* The credentials provided when this user was authenticated.
|
||||
*
|
||||
* @param effectiveGroups
|
||||
* The unique identifiers of all user groups which affect the
|
||||
* permissions available to this user.
|
||||
*/
|
||||
public void init(Credentials credentials) {
|
||||
public void init(Credentials credentials, Set<String> effectiveGroups) {
|
||||
this.credentials = credentials;
|
||||
this.effectiveGroups = effectiveGroups;
|
||||
setIdentifier(credentials.getUsername());
|
||||
}
|
||||
|
||||
@@ -63,4 +76,9 @@ public class AuthenticatedUser extends AbstractAuthenticatedUser {
|
||||
return credentials;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getEffectiveUserGroups() {
|
||||
return effectiveGroups;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -25,6 +25,7 @@ import java.util.Collections;
|
||||
import org.apache.guacamole.auth.ldap.connection.ConnectionService;
|
||||
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;
|
||||
@@ -32,6 +33,7 @@ import org.apache.guacamole.net.auth.Connection;
|
||||
import org.apache.guacamole.net.auth.ConnectionGroup;
|
||||
import org.apache.guacamole.net.auth.Directory;
|
||||
import org.apache.guacamole.net.auth.User;
|
||||
import org.apache.guacamole.net.auth.UserGroup;
|
||||
import org.apache.guacamole.net.auth.simple.SimpleConnectionGroup;
|
||||
import org.apache.guacamole.net.auth.simple.SimpleDirectory;
|
||||
import org.apache.guacamole.net.auth.simple.SimpleUser;
|
||||
@@ -61,6 +63,12 @@ public class UserContext extends AbstractUserContext {
|
||||
@Inject
|
||||
private UserService userService;
|
||||
|
||||
/**
|
||||
* Service for retrieving user groups.
|
||||
*/
|
||||
@Inject
|
||||
private UserGroupService userGroupService;
|
||||
|
||||
/**
|
||||
* Reference to the AuthenticationProvider associated with this
|
||||
* UserContext.
|
||||
@@ -80,6 +88,12 @@ public class UserContext extends AbstractUserContext {
|
||||
*/
|
||||
private Directory<User> userDirectory;
|
||||
|
||||
/**
|
||||
* Directory containing all UserGroup objects accessible to the user
|
||||
* associated with this UserContext.
|
||||
*/
|
||||
private Directory<UserGroup> userGroupDirectory;
|
||||
|
||||
/**
|
||||
* Directory containing all Connection objects accessible to the user
|
||||
* associated with this UserContext.
|
||||
@@ -112,12 +126,17 @@ public class UserContext extends AbstractUserContext {
|
||||
throws GuacamoleException {
|
||||
|
||||
// Query all accessible users
|
||||
userDirectory = new SimpleDirectory<User>(
|
||||
userDirectory = new SimpleDirectory<>(
|
||||
userService.getUsers(ldapConnection)
|
||||
);
|
||||
|
||||
// Query all accessible user groups
|
||||
userGroupDirectory = new SimpleDirectory<>(
|
||||
userGroupService.getUserGroups(ldapConnection)
|
||||
);
|
||||
|
||||
// Query all accessible connections
|
||||
connectionDirectory = new SimpleDirectory<Connection>(
|
||||
connectionDirectory = new SimpleDirectory<>(
|
||||
connectionService.getConnections(user, ldapConnection)
|
||||
);
|
||||
|
||||
@@ -133,6 +152,7 @@ public class UserContext extends AbstractUserContext {
|
||||
self = new SimpleUser(
|
||||
user.getIdentifier(),
|
||||
userDirectory.getIdentifiers(),
|
||||
userGroupDirectory.getIdentifiers(),
|
||||
connectionDirectory.getIdentifiers(),
|
||||
Collections.singleton(LDAPAuthenticationProvider.ROOT_CONNECTION_GROUP)
|
||||
);
|
||||
@@ -154,6 +174,11 @@ public class UserContext extends AbstractUserContext {
|
||||
return userDirectory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Directory<UserGroup> getUserGroupDirectory() throws GuacamoleException {
|
||||
return userGroupDirectory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Directory<Connection> getConnectionDirectory()
|
||||
throws GuacamoleException {
|
||||
|
Reference in New Issue
Block a user