From 5362bc6708d10c56a66071c5adcfdb7e2ae816ad Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sat, 3 Nov 2018 10:07:47 -0700 Subject: [PATCH 01/12] GUACAMOLE-220: Add SimpleUserGroup (read-only UserGroup implementation with no members). --- .../net/auth/simple/SimpleUserGroup.java | 118 ++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleUserGroup.java diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleUserGroup.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleUserGroup.java new file mode 100644 index 000000000..222320667 --- /dev/null +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleUserGroup.java @@ -0,0 +1,118 @@ +/* + * 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.net.auth.simple; + +import java.util.Collections; +import java.util.Map; +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.net.auth.AbstractIdentifiable; +import org.apache.guacamole.net.auth.RelatedObjectSet; +import org.apache.guacamole.net.auth.UserGroup; +import org.apache.guacamole.net.auth.permission.ObjectPermissionSet; +import org.apache.guacamole.net.auth.permission.SystemPermissionSet; + +/** + * A read-only UserGroup implementation which has no members. + */ +public class SimpleUserGroup extends AbstractIdentifiable implements UserGroup { + + /** + * Creates a completely uninitialized SimpleUserGroup. + */ + public SimpleUserGroup() { + } + + /** + * Creates a new SimpleUserGroup having the given identifier. + * + * @param identifier + * The identifier to assign to this SimpleUserGroup. + */ + public SimpleUserGroup(String identifier) { + super.setIdentifier(identifier); + } + + @Override + public Map getAttributes() { + return Collections.emptyMap(); + } + + @Override + public void setAttributes(Map attributes) { + // Do nothing - there are no attributes + } + + @Override + public SystemPermissionSet getSystemPermissions() + throws GuacamoleException { + return new SimpleSystemPermissionSet(); + } + + @Override + public ObjectPermissionSet getConnectionPermissions() + throws GuacamoleException { + return new SimpleObjectPermissionSet(); + } + + @Override + public ObjectPermissionSet getConnectionGroupPermissions() + throws GuacamoleException { + return new SimpleObjectPermissionSet(); + } + + @Override + public ObjectPermissionSet getUserPermissions() + throws GuacamoleException { + return new SimpleObjectPermissionSet(); + } + + @Override + public ObjectPermissionSet getUserGroupPermissions() + throws GuacamoleException { + return new SimpleObjectPermissionSet(); + } + + @Override + public ObjectPermissionSet getActiveConnectionPermissions() + throws GuacamoleException { + return new SimpleObjectPermissionSet(); + } + + @Override + public ObjectPermissionSet getSharingProfilePermissions() { + return new SimpleObjectPermissionSet(); + } + + @Override + public RelatedObjectSet getUserGroups() throws GuacamoleException { + return new SimpleRelatedObjectSet(); + } + + @Override + public RelatedObjectSet getMemberUsers() throws GuacamoleException { + return new SimpleRelatedObjectSet(); + } + + @Override + public RelatedObjectSet getMemberUserGroups() throws GuacamoleException { + return new SimpleRelatedObjectSet(); + } + +} From 929c7de2c9a50d8b7727f5fc107bdc2b355c3f8f Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sat, 3 Nov 2018 10:09:14 -0700 Subject: [PATCH 02/12] GUACAMOLE-220: Add user group permissions to SimpleUser. --- .../guacamole/net/auth/simple/SimpleUser.java | 66 +++++++++++++++---- 1 file changed, 52 insertions(+), 14 deletions(-) diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleUser.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleUser.java index 61fce20f4..302150e5a 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleUser.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleUser.java @@ -41,22 +41,24 @@ import org.apache.guacamole.net.auth.permission.SystemPermissionSet; public class SimpleUser extends AbstractUser { /** - * All connection permissions granted to this user. + * All user permissions granted to this user. */ - private final Set userPermissions = - new HashSet(); + private final Set userPermissions = new HashSet<>(); + + /** + * All user group permissions granted to this user. + */ + private final Set userGroupPermissions = new HashSet<>(); /** * All connection permissions granted to this user. */ - private final Set connectionPermissions = - new HashSet(); + private final Set connectionPermissions = new HashSet<>(); /** * All connection group permissions granted to this user. */ - private final Set connectionGroupPermissions = - new HashSet(); + private final Set connectionGroupPermissions = new HashSet<>(); /** * Creates a completely uninitialized SimpleUser. @@ -73,7 +75,7 @@ public class SimpleUser extends AbstractUser { public SimpleUser(String username) { // Set username - setIdentifier(username); + super.setIdentifier(username); } @@ -92,18 +94,17 @@ public class SimpleUser extends AbstractUser { Collection identifiers) { // Add a READ permission to the set for each identifier given - for (String identifier : identifiers) { - permissions.add(new ObjectPermission ( + identifiers.forEach(identifier -> + permissions.add(new ObjectPermission( ObjectPermission.Type.READ, - identifier + identifier) )); - } } - + /** * Creates a new SimpleUser having the given username and READ access to - * the connections and groups having the given identifiers. + * the connections and connection groups having the given identifiers. * * @param username * The username to assign to this SimpleUser. @@ -127,6 +128,43 @@ public class SimpleUser extends AbstractUser { } + /** + * Creates a new SimpleUser having the given username and READ access to + * the users, user groups, connections, and connection groups having the + * given identifiers. + * + * @param username + * The username to assign to this SimpleUser. + * + * @param userIdentifiers + * The identifiers of all users this user has READ access to. + * + * @param userGroupIdentifiers + * The identifiers of all user groups this user has READ access to. + * + * @param connectionIdentifiers + * The identifiers of all connections this user has READ access to. + * + * @param connectionGroupIdentifiers + * The identifiers of all connection groups this user has READ access + * to. + */ + public SimpleUser(String username, + Collection userIdentifiers, + Collection userGroupIdentifiers, + Collection connectionIdentifiers, + Collection connectionGroupIdentifiers) { + + this(username); + + // Add permissions + addReadPermissions(userPermissions, userIdentifiers); + addReadPermissions(userGroupPermissions, userGroupIdentifiers); + addReadPermissions(connectionPermissions, connectionIdentifiers); + addReadPermissions(connectionGroupPermissions, connectionGroupIdentifiers); + + } + /** * Creates a new SimpleUser having the given username and READ access to * the users, connections, and groups having the given identifiers. From 7c57b448bbd6a76018a3fd531950b952ac94dca0 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 2 Nov 2018 15:03:56 -0700 Subject: [PATCH 03/12] GUACAMOLE-220: Define generic service for executing LDAP queries. Refactor existing services to remove common code. --- .../LDAPAuthenticationProviderModule.java | 1 + .../auth/ldap/ObjectQueryService.java | 326 ++++++++++++++++++ .../ldap/connection/ConnectionService.java | 123 +++---- .../guacamole/auth/ldap/user/UserService.java | 230 ++---------- 4 files changed, 409 insertions(+), 271 deletions(-) create mode 100644 extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/ObjectQueryService.java diff --git a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/LDAPAuthenticationProviderModule.java b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/LDAPAuthenticationProviderModule.java index ead26bcd2..547808060 100644 --- a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/LDAPAuthenticationProviderModule.java +++ b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/LDAPAuthenticationProviderModule.java @@ -77,6 +77,7 @@ public class LDAPAuthenticationProviderModule extends AbstractModule { bind(ConnectionService.class); bind(EscapingService.class); bind(LDAPConnectionService.class); + bind(ObjectQueryService.class); bind(UserService.class); } diff --git a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/ObjectQueryService.java b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/ObjectQueryService.java new file mode 100644 index 000000000..89a43c327 --- /dev/null +++ b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/ObjectQueryService.java @@ -0,0 +1,326 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.guacamole.auth.ldap; + +import com.google.inject.Inject; +import com.novell.ldap.LDAPAttribute; +import com.novell.ldap.LDAPConnection; +import com.novell.ldap.LDAPEntry; +import com.novell.ldap.LDAPException; +import com.novell.ldap.LDAPReferralException; +import com.novell.ldap.LDAPSearchResults; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.GuacamoleServerException; +import org.apache.guacamole.net.auth.Identifiable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Service for executing queries against an LDAP directory intended to retrieve + * Guacamole-related objects. Referrals are automatically handled. Convenience + * functions are provided for generating the LDAP queries typically required + * for retrieving Guacamole objects, as well as for converting the results of a + * query into a Map of Guacamole objects. + */ +public class ObjectQueryService { + + /** + * Logger for this class. + */ + private final Logger logger = LoggerFactory.getLogger(ObjectQueryService.class); + + /** + * Service for escaping parts of LDAP queries. + */ + @Inject + private EscapingService escapingService; + + /** + * Service for retrieving LDAP server configuration information. + */ + @Inject + private ConfigurationService confService; + + /** + * Returns the identifier of the object represented by the given LDAP + * entry. Multiple attributes may be declared as containing the identifier + * of the object when present on an LDAP entry. If multiple such attributes + * are present on the same LDAP entry, the value of the attribute with + * highest priority is used. If multiple copies of the same attribute are + * present on the same LDAPentry, the first value of that attribute is + * used. + * + * @param entry + * The entry representing the Guacamole object whose unique identifier + * should be determined. + * + * @param attributes + * A collection of all attributes which may be used to specify the + * unique identifier of the Guacamole object represented by an LDAP + * entry, in order of decreasing priority. + * + * @return + * The identifier of the object represented by the given LDAP entry, or + * null if no attributes declared as containing the identifier of the + * object are present on the entry. + */ + public String getIdentifier(LDAPEntry entry, Collection attributes) { + + // Retrieve the first value of the highest priority identifier attribute + for (String identifierAttribute : attributes) { + LDAPAttribute identifier = entry.getAttribute(identifierAttribute); + if (identifier != null) + return identifier.getStringValue(); + } + + // No identifier attribute is present on the entry + return null; + + } + + /** + * Generates a properly-escaped LDAP query which finds all objects which + * match the given LDAP filter and which have at least one of the given + * attributes set to the specified value. + * + * @param filter + * The LDAP filter to apply to reduce the results of the query in + * addition to testing the values of the given attributes. + * + * @param attributes + * A collection of all attributes to test for equivalence to the given + * value, in order of decreasing priority. + * + * @param attributeValue + * The value that the resulting LDAP query should search for within the + * attributes of objects within the LDAP directory. If null, the + * resulting LDAP query will search for the presence of at least one of + * the given attributes on each object, regardless of the value of + * those attributes. + * + * @return + * An LDAP query which will search for arbitrary LDAP objects having at + * least one of the given attributes set to the specified value. + */ + public String generateQuery(String filter, + Collection attributes, String attributeValue) { + + // Build LDAP query for objects having at least one attribute and with + // the given search filter + StringBuilder ldapQuery = new StringBuilder(); + ldapQuery.append("(&"); + ldapQuery.append(filter); + + // Include all attributes within OR clause if there are more than one + if (attributes.size() > 1) + ldapQuery.append("(|"); + + // Add equality comparison for each possible attribute + for (String attribute : attributes) { + ldapQuery.append("("); + ldapQuery.append(escapingService.escapeLDAPSearchFilter(attribute)); + + if (attributeValue != null) { + ldapQuery.append("="); + ldapQuery.append(escapingService.escapeLDAPSearchFilter(attributeValue)); + ldapQuery.append(")"); + } + else + ldapQuery.append("=*)"); + + } + + // Close OR clause, if any + if (attributes.size() > 1) + ldapQuery.append(")"); + + // Close overall query (AND clause) + ldapQuery.append(")"); + + return ldapQuery.toString(); + + } + + /** + * Executes an arbitrary LDAP query using the given connection, returning a + * list of all results. Only objects beneath the given base DN are + * included in the search. + * + * @param ldapConnection + * The current connection to the LDAP server, associated with the + * current user. + * + * @param baseDN + * The base DN to search using the given LDAP query. + * + * @param query + * The LDAP query to execute. + * + * @return + * A list of all results accessible to the user currently bound under + * the given LDAP connection. + * + * @throws GuacamoleException + * If an error occurs executing the query, or if configuration + * information required to execute the query cannot be read from + * guacamole.properties. + */ + public List search(LDAPConnection ldapConnection, + String baseDN, String query) throws GuacamoleException { + + logger.debug("Searching \"{}\" for objects matching \"{}\".", baseDN, query); + + try { + + // Search within subtree of given base DN + LDAPSearchResults results = ldapConnection.search(baseDN, + LDAPConnection.SCOPE_SUB, query, null, false, + confService.getLDAPSearchConstraints()); + + // Produce list of all entries in the search result, automatically + // following referrals if configured to do so + List entries = new ArrayList<>(results.getCount()); + while (results.hasMore()) { + + try { + entries.add(results.next()); + } + + // Warn if referrals cannot be followed + catch (LDAPReferralException e) { + if (confService.getFollowReferrals()) { + logger.error("Could not follow referral: {}", e.getFailedReferral()); + logger.debug("Error encountered trying to follow referral.", e); + throw new GuacamoleServerException("Could not follow LDAP referral.", e); + } + else { + logger.warn("Given a referral, but referrals are disabled. Error was: {}", e.getMessage()); + logger.debug("Got a referral, but configured to not follow them.", e); + } + } + + } + + return entries; + + } + catch (LDAPException | GuacamoleException e) { + throw new GuacamoleServerException("Unable to query list of " + + "objects from LDAP directory.", e); + } + + } + + /** + * Executes the query which would be returned by generateQuery() using the + * given connection, returning a list of all results. Only objects beneath + * the given base DN are included in the search. + * + * @param ldapConnection + * The current connection to the LDAP server, associated with the + * current user. + * + * @param baseDN + * The base DN to search using the given LDAP query. + * + * @param filter + * The LDAP filter to apply to reduce the results of the query in + * addition to testing the values of the given attributes. + * + * @param attributes + * A collection of all attributes to test for equivalence to the given + * value, in order of decreasing priority. + * + * @param attributeValue + * The value that should be searched search for within the attributes + * of objects within the LDAP directory. If null, the search will test + * only for the presence of at least one of the given attributes on + * each object, regardless of the value of those attributes. + * + * @return + * A list of all results accessible to the user currently bound under + * the given LDAP connection. + * + * @throws GuacamoleException + * If an error occurs executing the query, or if configuration + * information required to execute the query cannot be read from + * guacamole.properties. + */ + public List search(LDAPConnection ldapConnection, String baseDN, + String filter, Collection attributes, String attributeValue) + throws GuacamoleException { + String query = generateQuery(filter, attributes, attributeValue); + return search(ldapConnection, baseDN, query); + } + + /** + * Converts a given list of LDAP entries to a map of Guacamole objects + * stored by their identifiers. + * + * @param + * The type of object to store within the map. + * + * @param entries + * A list of LDAP entries to convert to Guacamole objects. + * + * @param mapper + * A mapping function which converts a given LDAP entry to its + * corresponding Guacamole object. If the LDAP entry cannot be + * converted, null should be returned. + * + * @return + * A new map containing Guacamole object versions of each of the given + * LDAP entries, where each object is stored within the map under its + * corresponding identifier. + */ + public Map + asMap(List entries, Function mapper) { + + // Convert each entry to the corresponding Guacamole API object + Map objects = new HashMap<>(entries.size()); + for (LDAPEntry entry : entries) { + + ObjectType object = mapper.apply(entry); + if (object == null) { + logger.debug("Ignoring object \"{}\".", entry.getDN()); + continue; + } + + // Attempt to add object to map, warning if the object appears + // to be a duplicate + String identifier = object.getIdentifier(); + if (objects.putIfAbsent(identifier, object) != null) + logger.warn("Multiple objects ambiguously map to the " + + "same identifier (\"{}\"). Ignoring \"{}\" as " + + "a duplicate.", identifier, entry.getDN()); + + } + + return objects; + + } + +} 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 3ce00e3f2..78100a0fe 100644 --- a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/connection/ConnectionService.java +++ b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/connection/ConnectionService.java @@ -28,13 +28,14 @@ import com.novell.ldap.LDAPReferralException; import com.novell.ldap.LDAPSearchResults; import java.util.Collections; import java.util.Enumeration; -import java.util.HashMap; +import java.util.List; import java.util.Map; import org.apache.guacamole.auth.ldap.LDAPAuthenticationProvider; import org.apache.guacamole.auth.ldap.ConfigurationService; import org.apache.guacamole.auth.ldap.EscapingService; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleServerException; +import org.apache.guacamole.auth.ldap.ObjectQueryService; import org.apache.guacamole.net.auth.AuthenticatedUser; import org.apache.guacamole.net.auth.Connection; import org.apache.guacamole.net.auth.simple.SimpleConnection; @@ -67,6 +68,12 @@ public class ConnectionService { @Inject private ConfigurationService confService; + /** + * Service for executing LDAP queries. + */ + @Inject + private ObjectQueryService queryService; + /** * Returns all Guacamole connections accessible to the user currently bound * under the given LDAP connection. @@ -113,101 +120,71 @@ public class ConnectionService { // looking for direct membership in the guacConfigGroup // and possibly any groups the user is a member of that are // referred to in the seeAlso attribute of the guacConfigGroup. - LDAPSearchResults results = ldapConnection.search( - configurationBaseDN, - LDAPConnection.SCOPE_SUB, - connectionSearchFilter, - null, - false, - confService.getLDAPSearchConstraints() - ); + List results = queryService.search(ldapConnection, configurationBaseDN, connectionSearchFilter); // Build token filter containing credential tokens TokenFilter tokenFilter = new TokenFilter(); StandardTokens.addStandardTokens(tokenFilter, user); - // Produce connections for each readable configuration - Map connections = new HashMap(); - while (results.hasMore()) { + // Return a map of all readable connections + return queryService.asMap(results, (entry) -> { - try { + // Get common name (CN) + LDAPAttribute cn = entry.getAttribute("cn"); + if (cn == null) { + logger.warn("guacConfigGroup is missing a cn."); + return null; + } - LDAPEntry entry = results.next(); + // Get associated protocol + LDAPAttribute protocol = entry.getAttribute("guacConfigProtocol"); + if (protocol == null) { + logger.warn("guacConfigGroup \"{}\" is missing the " + + "required \"guacConfigProtocol\" attribute.", + cn.getStringValue()); + return null; + } - // Get common name (CN) - LDAPAttribute cn = entry.getAttribute("cn"); - if (cn == null) { - logger.warn("guacConfigGroup is missing a cn."); - continue; - } + // Set protocol + GuacamoleConfiguration config = new GuacamoleConfiguration(); + config.setProtocol(protocol.getStringValue()); - // Get associated protocol - LDAPAttribute protocol = entry.getAttribute("guacConfigProtocol"); - if (protocol == null) { - logger.warn("guacConfigGroup \"{}\" is missing the " - + "required \"guacConfigProtocol\" attribute.", - cn.getStringValue()); - continue; - } + // Get parameters, if any + LDAPAttribute parameterAttribute = entry.getAttribute("guacConfigParameter"); + if (parameterAttribute != null) { - // Set protocol - GuacamoleConfiguration config = new GuacamoleConfiguration(); - config.setProtocol(protocol.getStringValue()); + // For each parameter + Enumeration parameters = parameterAttribute.getStringValues(); + while (parameters.hasMoreElements()) { - // Get parameters, if any - LDAPAttribute parameterAttribute = entry.getAttribute("guacConfigParameter"); - if (parameterAttribute != null) { + String parameter = (String) parameters.nextElement(); - // For each parameter - Enumeration parameters = parameterAttribute.getStringValues(); - while (parameters.hasMoreElements()) { + // Parse parameter + int equals = parameter.indexOf('='); + if (equals != -1) { - String parameter = (String) parameters.nextElement(); + // Parse name + String name = parameter.substring(0, equals); + String value = parameter.substring(equals+1); - // Parse parameter - int equals = parameter.indexOf('='); - if (equals != -1) { - - // Parse name - String name = parameter.substring(0, equals); - String value = parameter.substring(equals+1); - - config.setParameter(name, value); - - } + config.setParameter(name, value); } } - // Filter the configuration, substituting all defined tokens - tokenFilter.filterValues(config.getParameters()); - - // Store connection using cn for both identifier and name - String name = cn.getStringValue(); - Connection connection = new SimpleConnection(name, name, config); - connection.setParentIdentifier(LDAPAuthenticationProvider.ROOT_CONNECTION_GROUP); - connections.put(name, connection); - } - // Deal with issues following LDAP referrals - catch (LDAPReferralException e) { - if (confService.getFollowReferrals()) { - logger.error("Could not follow referral: {}", e.getFailedReferral()); - logger.debug("Error encountered trying to follow referral.", e); - throw new GuacamoleServerException("Could not follow LDAP referral.", e); - } - else { - logger.warn("Given a referral, but referrals are disabled. Error was: {}", e.getMessage()); - logger.debug("Got a referral, but configured to not follow them.", e); - } - } + // Filter the configuration, substituting all defined tokens + tokenFilter.filterValues(config.getParameters()); - } + // Store connection using cn for both identifier and name + String name = cn.getStringValue(); + Connection connection = new SimpleConnection(name, name, config); + connection.setParentIdentifier(LDAPAuthenticationProvider.ROOT_CONNECTION_GROUP); + return connection; - // Return map of all connections - return connections; + }); } catch (LDAPException e) { 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 9d27f1e00..3f12ae829 100644 --- a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/user/UserService.java +++ b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/user/UserService.java @@ -20,21 +20,17 @@ package org.apache.guacamole.auth.ldap.user; import com.google.inject.Inject; -import com.novell.ldap.LDAPAttribute; import com.novell.ldap.LDAPConnection; import com.novell.ldap.LDAPEntry; -import com.novell.ldap.LDAPException; -import com.novell.ldap.LDAPReferralException; -import com.novell.ldap.LDAPSearchResults; import java.util.ArrayList; -import java.util.HashMap; +import java.util.Collection; import java.util.List; import java.util.Map; import org.apache.guacamole.auth.ldap.ConfigurationService; import org.apache.guacamole.auth.ldap.EscapingService; import org.apache.guacamole.GuacamoleException; -import org.apache.guacamole.GuacamoleServerException; import org.apache.guacamole.auth.ldap.LDAPGuacamoleProperties; +import org.apache.guacamole.auth.ldap.ObjectQueryService; import org.apache.guacamole.net.auth.User; import org.apache.guacamole.net.auth.simple.SimpleUser; import org.slf4j.Logger; @@ -64,89 +60,10 @@ public class UserService { private ConfigurationService confService; /** - * Adds all Guacamole users accessible to the user currently bound under - * the given LDAP connection to the provided map. Only users with the - * specified attribute are added. If the same username is encountered - * multiple times, warnings about possible ambiguity will be logged. - * - * @param ldapConnection - * The current connection to the LDAP server, associated with the - * current user. - * - * @return - * All users accessible to the user currently bound under the given - * LDAP connection, as a map of connection identifier to corresponding - * user object. - * - * @throws GuacamoleException - * If an error occurs preventing retrieval of users. + * Service for executing LDAP queries. */ - private void putAllUsers(Map users, LDAPConnection ldapConnection, - String usernameAttribute) throws GuacamoleException { - - try { - - // Build a filter using the configured or default user search filter - // to find all user objects in the LDAP tree - StringBuilder userSearchFilter = new StringBuilder(); - userSearchFilter.append("(&"); - userSearchFilter.append(confService.getUserSearchFilter()); - userSearchFilter.append("("); - userSearchFilter.append(escapingService.escapeLDAPSearchFilter(usernameAttribute)); - userSearchFilter.append("=*))"); - - // Find all Guacamole users underneath base DN - LDAPSearchResults results = ldapConnection.search( - confService.getUserBaseDN(), - LDAPConnection.SCOPE_SUB, - userSearchFilter.toString(), - null, - false, - confService.getLDAPSearchConstraints() - ); - - // Read all visible users - while (results.hasMore()) { - - try { - - LDAPEntry entry = results.next(); - - // Get username from record - LDAPAttribute username = entry.getAttribute(usernameAttribute); - if (username == null) { - logger.warn("Queried user is missing the username attribute \"{}\".", usernameAttribute); - continue; - } - - // Store user using their username as the identifier - String identifier = username.getStringValue(); - if (users.put(identifier, new SimpleUser(identifier)) != null) - logger.warn("Possibly ambiguous user account: \"{}\".", identifier); - - } - - // Deal with errors trying to follow referrals - catch (LDAPReferralException e) { - if (confService.getFollowReferrals()) { - logger.error("Could not follow referral: {}", e.getFailedReferral()); - logger.debug("Error encountered trying to follow referral.", e); - throw new GuacamoleServerException("Could not follow LDAP referral.", e); - } - else { - logger.warn("Given a referral, but referrals are disabled. Error was: {}", e.getMessage()); - logger.debug("Got a referral, but configured to not follow them.", e); - } - } - - } - - } - catch (LDAPException e) { - throw new GuacamoleServerException("Error while querying users.", e); - } - - } + @Inject + private ObjectQueryService queryService; /** * Returns all Guacamole users accessible to the user currently bound under @@ -167,80 +84,28 @@ public class UserService { public Map getUsers(LDAPConnection ldapConnection) throws GuacamoleException { - // Build map of users by querying each username attribute separately - Map users = new HashMap(); - for (String usernameAttribute : confService.getUsernameAttributes()) { + // Retrieve all visible user objects + Collection attributes = confService.getUsernameAttributes(); + List results = queryService.search(ldapConnection, + confService.getUserBaseDN(), + confService.getUserSearchFilter(), + attributes, + null); - // Attempt to pull all users with given attribute - try { - putAllUsers(users, ldapConnection, usernameAttribute); + // Convert retrieved users to map of identifier to Guacamole user object + return queryService.asMap(results, entry -> { + + // Get username from record + String username = queryService.getIdentifier(entry, attributes); + if (username == null) { + logger.warn("User \"{}\" is missing a username attribute " + + "and will be ignored.", entry.getDN()); + return null; } - // Log any errors non-fatally - catch (GuacamoleException e) { - logger.warn("Could not query list of all users for attribute \"{}\": {}", - usernameAttribute, e.getMessage()); - logger.debug("Error querying list of all users.", e); - } + return new SimpleUser(username); - } - - // Return map of all users - return users; - - } - - /** - * Generates a properly-escaped LDAP query which finds all objects having - * at least one username attribute set to the specified username, where - * the possible username attributes are defined within - * guacamole.properties. - * - * @param username - * The username that the resulting LDAP query should search for within - * objects within the LDAP directory. - * - * @return - * An LDAP query which will search for arbitrary LDAP objects - * containing at least one username attribute set to the specified - * username. - * - * @throws GuacamoleException - * If the LDAP query cannot be generated because the list of username - * attributes cannot be parsed from guacamole.properties. - */ - private String generateLDAPQuery(String username) - throws GuacamoleException { - - List usernameAttributes = confService.getUsernameAttributes(); - - // Build LDAP query for users having at least one username attribute - // and with the configured or default search filter - StringBuilder ldapQuery = new StringBuilder(); - ldapQuery.append("(&"); - ldapQuery.append(confService.getUserSearchFilter()); - - // Include all attributes within OR clause if there are more than one - if (usernameAttributes.size() > 1) - ldapQuery.append("(|"); - - // Add equality comparison for each possible username attribute - for (String usernameAttribute : usernameAttributes) { - ldapQuery.append("("); - ldapQuery.append(escapingService.escapeLDAPSearchFilter(usernameAttribute)); - ldapQuery.append("="); - ldapQuery.append(escapingService.escapeLDAPSearchFilter(username)); - ldapQuery.append(")"); - } - - // Close OR clause, if any - if (usernameAttributes.size() > 1) - ldapQuery.append(")"); - - // Close overall query (AND clause) - ldapQuery.append(")"); - - return ldapQuery.toString(); + }); } @@ -268,49 +133,18 @@ public class UserService { public List getUserDNs(LDAPConnection ldapConnection, String username) throws GuacamoleException { - try { - - List userDNs = new ArrayList(); - - // Find all Guacamole users underneath base DN and matching the - // specified username - LDAPSearchResults results = ldapConnection.search( + // Retrieve user objects having a matching username + List results = queryService.search(ldapConnection, confService.getUserBaseDN(), - LDAPConnection.SCOPE_SUB, - generateLDAPQuery(username), - null, - false, - confService.getLDAPSearchConstraints() - ); + confService.getUserSearchFilter(), + confService.getUsernameAttributes(), + username); - // Add all DNs for found users - while (results.hasMore()) { - try { - LDAPEntry entry = results.next(); - userDNs.add(entry.getDN()); - } - - // Deal with errors following referrals - catch (LDAPReferralException e) { - if (confService.getFollowReferrals()) { - logger.error("Error trying to follow a referral: {}", e.getFailedReferral()); - logger.debug("Encountered an error trying to follow a referral.", e); - throw new GuacamoleServerException("Failed while trying to follow referrals.", e); - } - else { - logger.warn("Given a referral, not following it. Error was: {}", e.getMessage()); - logger.debug("Given a referral, but configured to not follow them.", e); - } - } - } + // Build list of all DNs for retrieved users + List userDNs = new ArrayList<>(results.size()); + results.forEach(entry -> userDNs.add(entry.getDN())); - // Return all discovered DNs (if any) - return userDNs; - - } - catch (LDAPException e) { - throw new GuacamoleServerException("Error while query user DNs.", e); - } + return userDNs; } From bdc792603db1706e8126ba027d8d86f203ab3171 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sat, 3 Nov 2018 10:10:19 -0700 Subject: [PATCH 04/12] GUACAMOLE-220: Add configuration property for setting the attributes which uniquely identify user groups within LDAP. --- .../auth/ldap/ConfigurationService.java | 18 ++++++++++++++++++ .../auth/ldap/LDAPGuacamoleProperties.java | 12 ++++++++++++ 2 files changed, 30 insertions(+) diff --git a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/ConfigurationService.java b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/ConfigurationService.java index 2ab7aadf6..6a4b8c0c5 100644 --- a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/ConfigurationService.java +++ b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/ConfigurationService.java @@ -138,6 +138,24 @@ public class ConfigurationService { ); } + /** + * 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 diff --git a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/LDAPGuacamoleProperties.java b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/LDAPGuacamoleProperties.java index 0d3823fed..340cbf551 100644 --- a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/LDAPGuacamoleProperties.java +++ b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/LDAPGuacamoleProperties.java @@ -86,6 +86,18 @@ public class LDAPGuacamoleProperties { }; + /** + * The attribute or attributes which identify user groups. One of these + * attributes must be present within each Guacamole user group's record in + * the LDAP directory for that group to be visible. + */ + public static final StringListProperty LDAP_GROUP_NAME_ATTRIBUTE = new StringListProperty() { + + @Override + public String getName() { return "ldap-group-name-attribute"; } + + }; + /** * The port on the LDAP server to connect to when authenticating users. */ From aa0c65423146929a46ceeb1beb7573815c0e4513 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sat, 3 Nov 2018 12:34:04 -0700 Subject: [PATCH 05/12] GUACAMOLE-220: Retrieve user groups from LDAP. Take immediate group membership into account. --- .../ldap/AuthenticationProviderService.java | 15 +- .../LDAPAuthenticationProviderModule.java | 2 + .../ldap/connection/ConnectionService.java | 52 +--- .../auth/ldap/group/UserGroupService.java | 224 ++++++++++++++++++ .../auth/ldap/user/AuthenticatedUser.java | 22 +- .../guacamole/auth/ldap/user/UserContext.java | 29 ++- 6 files changed, 300 insertions(+), 44 deletions(-) create mode 100644 extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/group/UserGroupService.java 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 a25c697e6..4a746f11a 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 @@ -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 effectiveGroups = + userGroupService.getParentUserGroupIdentifiers(ldapConnection, + ldapConnection.getAuthenticationDN()); + // Return AuthenticatedUser if bind succeeds AuthenticatedUser authenticatedUser = authenticatedUserProvider.get(); - authenticatedUser.init(credentials); + authenticatedUser.init(credentials, effectiveGroups); return authenticatedUser; } diff --git a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/LDAPAuthenticationProviderModule.java b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/LDAPAuthenticationProviderModule.java index 547808060..23decec6d 100644 --- a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/LDAPAuthenticationProviderModule.java +++ b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/LDAPAuthenticationProviderModule.java @@ -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); } 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 78100a0fe..bae1da813 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 @@ -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 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. 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 new file mode 100644 index 000000000..dfdd9fd92 --- /dev/null +++ b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/group/UserGroupService.java @@ -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 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.emptyMap(); + + // Retrieve all visible user groups which are not guacConfigGroups + Collection attributes = confService.getGroupNameAttributes(); + List 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 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.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 getParentUserGroupIdentifiers(LDAPConnection ldapConnection, + String userDN) throws GuacamoleException { + + Collection attributes = confService.getGroupNameAttributes(); + List userGroups = getParentUserGroupEntries(ldapConnection, userDN); + + Set 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; + + } + +} diff --git a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/user/AuthenticatedUser.java b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/user/AuthenticatedUser.java index 669efcd54..85f004b09 100644 --- a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/user/AuthenticatedUser.java +++ b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/user/AuthenticatedUser.java @@ -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 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 effectiveGroups) { this.credentials = credentials; + this.effectiveGroups = effectiveGroups; setIdentifier(credentials.getUsername()); } @@ -63,4 +76,9 @@ public class AuthenticatedUser extends AbstractAuthenticatedUser { return credentials; } + @Override + public Set getEffectiveUserGroups() { + return effectiveGroups; + } + } diff --git a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/user/UserContext.java b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/user/UserContext.java index 26ea6b319..7c520d314 100644 --- a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/user/UserContext.java +++ b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/user/UserContext.java @@ -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 userDirectory; + /** + * Directory containing all UserGroup objects accessible to the user + * associated with this UserContext. + */ + private Directory 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( + userDirectory = new SimpleDirectory<>( userService.getUsers(ldapConnection) ); + // Query all accessible user groups + userGroupDirectory = new SimpleDirectory<>( + userGroupService.getUserGroups(ldapConnection) + ); + // Query all accessible connections - connectionDirectory = new SimpleDirectory( + 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 getUserGroupDirectory() throws GuacamoleException { + return userGroupDirectory; + } + @Override public Directory getConnectionDirectory() throws GuacamoleException { From d10256e15112bb476f22f28f878e3972bc83e34c Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sat, 3 Nov 2018 13:58:50 -0700 Subject: [PATCH 06/12] GUACAMOLE-220: Deprecate built-in support for storage of permissions in SimpleUser. Add convenience constructors for SimpleObjectPermissionSet. --- .../guacamole/auth/ldap/user/UserContext.java | 32 +++++++--- .../quickconnect/QuickConnectUserContext.java | 19 ++++-- .../simple/SimpleObjectPermissionSet.java | 61 +++++++++++++++++++ .../guacamole/net/auth/simple/SimpleUser.java | 58 +++++------------- .../net/auth/simple/SimpleUserContext.java | 23 ++++--- 5 files changed, 128 insertions(+), 65 deletions(-) diff --git a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/user/UserContext.java b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/user/UserContext.java index 7c520d314..826b4ec65 100644 --- a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/user/UserContext.java +++ b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/user/UserContext.java @@ -34,8 +34,10 @@ 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.permission.ObjectPermissionSet; import org.apache.guacamole.net.auth.simple.SimpleConnectionGroup; import org.apache.guacamole.net.auth.simple.SimpleDirectory; +import org.apache.guacamole.net.auth.simple.SimpleObjectPermissionSet; import org.apache.guacamole.net.auth.simple.SimpleUser; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -149,13 +151,29 @@ public class UserContext extends AbstractUserContext { ); // Init self with basic permissions - self = new SimpleUser( - user.getIdentifier(), - userDirectory.getIdentifiers(), - userGroupDirectory.getIdentifiers(), - connectionDirectory.getIdentifiers(), - Collections.singleton(LDAPAuthenticationProvider.ROOT_CONNECTION_GROUP) - ); + self = new SimpleUser(user.getIdentifier()) { + + @Override + public ObjectPermissionSet getUserPermissions() throws GuacamoleException { + return new SimpleObjectPermissionSet(userDirectory.getIdentifiers()); + } + + @Override + public ObjectPermissionSet getUserGroupPermissions() throws GuacamoleException { + return new SimpleObjectPermissionSet(userGroupDirectory.getIdentifiers()); + } + + @Override + public ObjectPermissionSet getConnectionPermissions() throws GuacamoleException { + return new SimpleObjectPermissionSet(connectionDirectory.getIdentifiers()); + } + + @Override + public ObjectPermissionSet getConnectionGroupPermissions() throws GuacamoleException { + return new SimpleObjectPermissionSet(Collections.singleton(LDAPAuthenticationProvider.ROOT_CONNECTION_GROUP)); + } + + }; } diff --git a/extensions/guacamole-auth-quickconnect/src/main/java/org/apache/guacamole/auth/quickconnect/QuickConnectUserContext.java b/extensions/guacamole-auth-quickconnect/src/main/java/org/apache/guacamole/auth/quickconnect/QuickConnectUserContext.java index d7e23edb4..dad050556 100644 --- a/extensions/guacamole-auth-quickconnect/src/main/java/org/apache/guacamole/auth/quickconnect/QuickConnectUserContext.java +++ b/extensions/guacamole-auth-quickconnect/src/main/java/org/apache/guacamole/auth/quickconnect/QuickConnectUserContext.java @@ -26,6 +26,8 @@ import org.apache.guacamole.net.auth.AbstractUserContext; import org.apache.guacamole.net.auth.AuthenticationProvider; import org.apache.guacamole.net.auth.ConnectionGroup; import org.apache.guacamole.net.auth.User; +import org.apache.guacamole.net.auth.permission.ObjectPermissionSet; +import org.apache.guacamole.net.auth.simple.SimpleObjectPermissionSet; import org.apache.guacamole.net.auth.simple.SimpleUser; /** @@ -93,10 +95,19 @@ public class QuickConnectUserContext extends AbstractUserContext { // Initialize the user to a SimpleUser with the provided username, // no connections, and the single root group. - this.self = new SimpleUser(username, - connectionDirectory.getIdentifiers(), - Collections.singleton(ROOT_IDENTIFIER) - ); + this.self = new SimpleUser(username) { + + @Override + public ObjectPermissionSet getConnectionPermissions() throws GuacamoleException { + return new SimpleObjectPermissionSet(connectionDirectory.getIdentifiers()); + } + + @Override + public ObjectPermissionSet getConnectionGroupPermissions() throws GuacamoleException { + return new SimpleObjectPermissionSet(Collections.singleton(ROOT_IDENTIFIER)); + } + + }; // Set the authProvider to the calling authProvider object. this.authProvider = authProvider; diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleObjectPermissionSet.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleObjectPermissionSet.java index 7cf54bd2d..53a30ce19 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleObjectPermissionSet.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleObjectPermissionSet.java @@ -22,6 +22,7 @@ package org.apache.guacamole.net.auth.simple; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.HashSet; import java.util.Set; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleSecurityException; @@ -45,6 +46,66 @@ public class SimpleObjectPermissionSet implements ObjectPermissionSet { public SimpleObjectPermissionSet() { } + /** + * Creates a new set of ObjectPermissions for each possible combination of + * the given identifiers and permission types. + * + * @param identifiers + * The identifiers which should have one ObjectPermission for each of + * the given permission types. + * + * @param types + * The permissions which should be granted for each of the given + * identifiers. + * + * @return + * A new set of ObjectPermissions containing one ObjectPermission for + * each possible combination of the given identifiers and permission + * types. + */ + private static Set createPermissions(Collection identifiers, + Collection types) { + + // Add a permission of each type to the set for each identifier given + Set permissions = new HashSet<>(identifiers.size()); + types.forEach(type -> { + identifiers.forEach(identifier -> permissions.add(new ObjectPermission(type, identifier))); + }); + + return permissions; + + } + + /** + * Creates a new SimpleObjectPermissionSet which contains permissions for + * all possible unique combinations of the given identifiers and permission + * types. + * + * @param identifiers + * The identifiers which should be associated permissions having each + * of the given permission types. + * + * @param types + * The types of permissions which should be granted for each of the + * given identifiers. + */ + public SimpleObjectPermissionSet(Collection identifiers, + Collection types) { + this(createPermissions(identifiers, types)); + } + + /** + * Creates a new SimpleObjectPermissionSet which contains only READ + * permissions for each of the given identifiers. + * + * @param identifiers + * The identifiers which should each be associated with READ + * permission. + */ + public SimpleObjectPermissionSet(Collection identifiers) { + this(identifiers, Collections.singletonList(ObjectPermission.Type.READ)); + } + /** * Creates a new SimpleObjectPermissionSet which contains the permissions * within the given Set. diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleUser.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleUser.java index 302150e5a..cce8bf01b 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleUser.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleUser.java @@ -45,11 +45,6 @@ public class SimpleUser extends AbstractUser { */ private final Set userPermissions = new HashSet<>(); - /** - * All user group permissions granted to this user. - */ - private final Set userGroupPermissions = new HashSet<>(); - /** * All connection permissions granted to this user. */ @@ -115,7 +110,15 @@ public class SimpleUser extends AbstractUser { * @param connectionGroupIdentifiers * The identifiers of all connection groups this user has READ access * to. + * + * @deprecated + * Extend and override the applicable permission set getters instead, + * relying on SimpleUser to expose no permissions by default for all + * permission sets that aren't overridden. See {@link SimpleObjectPermissionSet} + * for convenient methods of providing a read-only permission set with + * specific permissions. */ + @Deprecated public SimpleUser(String username, Collection connectionIdentifiers, Collection connectionGroupIdentifiers) { @@ -128,43 +131,6 @@ public class SimpleUser extends AbstractUser { } - /** - * Creates a new SimpleUser having the given username and READ access to - * the users, user groups, connections, and connection groups having the - * given identifiers. - * - * @param username - * The username to assign to this SimpleUser. - * - * @param userIdentifiers - * The identifiers of all users this user has READ access to. - * - * @param userGroupIdentifiers - * The identifiers of all user groups this user has READ access to. - * - * @param connectionIdentifiers - * The identifiers of all connections this user has READ access to. - * - * @param connectionGroupIdentifiers - * The identifiers of all connection groups this user has READ access - * to. - */ - public SimpleUser(String username, - Collection userIdentifiers, - Collection userGroupIdentifiers, - Collection connectionIdentifiers, - Collection connectionGroupIdentifiers) { - - this(username); - - // Add permissions - addReadPermissions(userPermissions, userIdentifiers); - addReadPermissions(userGroupPermissions, userGroupIdentifiers); - addReadPermissions(connectionPermissions, connectionIdentifiers); - addReadPermissions(connectionGroupPermissions, connectionGroupIdentifiers); - - } - /** * Creates a new SimpleUser having the given username and READ access to * the users, connections, and groups having the given identifiers. @@ -181,7 +147,15 @@ public class SimpleUser extends AbstractUser { * @param connectionGroupIdentifiers * The identifiers of all connection groups this user has READ access * to. + * + * @deprecated + * Extend and override the applicable permission set getters instead, + * relying on SimpleUser to expose no permissions by default for all + * permission sets that aren't overridden. See {@link SimpleObjectPermissionSet} + * for convenient methods of providing a read-only permission set with + * specific permissions. */ + @Deprecated public SimpleUser(String username, Collection userIdentifiers, Collection connectionIdentifiers, diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleUserContext.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleUserContext.java index 26978e9fb..03e94fbac 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleUserContext.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleUserContext.java @@ -19,7 +19,6 @@ package org.apache.guacamole.net.auth.simple; -import java.util.Collections; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.apache.guacamole.GuacamoleException; @@ -29,6 +28,7 @@ import org.apache.guacamole.net.auth.AuthenticationProvider; import org.apache.guacamole.net.auth.Connection; import org.apache.guacamole.net.auth.Directory; import org.apache.guacamole.net.auth.User; +import org.apache.guacamole.net.auth.permission.ObjectPermissionSet; import org.apache.guacamole.protocol.GuacamoleConfiguration; /** @@ -113,20 +113,19 @@ public class SimpleUserContext extends AbstractUserContext { @Override public User self() { + return new SimpleUser(username) { - try { - return new SimpleUser(username, - getConnectionDirectory().getIdentifiers(), - getConnectionGroupDirectory().getIdentifiers() - ); - } + @Override + public ObjectPermissionSet getConnectionGroupPermissions() throws GuacamoleException { + return new SimpleObjectPermissionSet(getConnectionDirectory().getIdentifiers()); + } - catch (GuacamoleException e) { - return new SimpleUser(username, - Collections.emptySet(), - Collections.emptySet()); - } + @Override + public ObjectPermissionSet getConnectionPermissions() throws GuacamoleException { + return new SimpleObjectPermissionSet(getConnectionGroupDirectory().getIdentifiers()); + } + }; } @Override From d533de118f26000cba4eefd6571964ba6614810b Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 2 Nov 2018 16:01:48 -0700 Subject: [PATCH 07/12] GUACAMOLE-220: Add EMPTY_SET convenience constant to all core set interfaces. --- .../auth/jdbc/sharing/user/SharedUser.java | 13 ++--- .../guacamole/net/auth/RelatedObjectSet.java | 26 ++++++++++ .../auth/permission/ObjectPermissionSet.java | 52 +++++++++++++++++++ .../auth/permission/SystemPermissionSet.java | 46 ++++++++++++++++ .../simple/SimpleObjectPermissionSet.java | 5 +- .../auth/simple/SimpleRelatedObjectSet.java | 5 +- .../simple/SimpleSystemPermissionSet.java | 5 +- .../guacamole/net/auth/simple/SimpleUser.java | 10 ++-- .../net/auth/simple/SimpleUserGroup.java | 20 +++---- 9 files changed, 156 insertions(+), 26 deletions(-) diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/user/SharedUser.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/user/SharedUser.java index 697b2ca7f..f0a48b87d 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/user/SharedUser.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/user/SharedUser.java @@ -35,9 +35,6 @@ import org.apache.guacamole.net.auth.RelatedObjectSet; import org.apache.guacamole.net.auth.User; import org.apache.guacamole.net.auth.permission.ObjectPermissionSet; import org.apache.guacamole.net.auth.permission.SystemPermissionSet; -import org.apache.guacamole.net.auth.simple.SimpleObjectPermissionSet; -import org.apache.guacamole.net.auth.simple.SimpleRelatedObjectSet; -import org.apache.guacamole.net.auth.simple.SimpleSystemPermissionSet; /** * An immutable implementation of User which defines READ permission for each of @@ -122,7 +119,7 @@ public class SharedUser implements User { @Override public SystemPermissionSet getSystemPermissions() throws GuacamoleException { - return new SimpleSystemPermissionSet(); + return SystemPermissionSet.EMPTY_SET; } @Override @@ -145,22 +142,22 @@ public class SharedUser implements User { @Override public ObjectPermissionSet getUserGroupPermissions() throws GuacamoleException { - return new SimpleObjectPermissionSet(); + return ObjectPermissionSet.EMPTY_SET; } @Override public ObjectPermissionSet getSharingProfilePermissions() throws GuacamoleException { - return new SimpleObjectPermissionSet(); + return ObjectPermissionSet.EMPTY_SET; } @Override public ObjectPermissionSet getActiveConnectionPermissions() throws GuacamoleException { - return new SimpleObjectPermissionSet(); + return ObjectPermissionSet.EMPTY_SET; } @Override public RelatedObjectSet getUserGroups() throws GuacamoleException { - return new SimpleRelatedObjectSet(); + return RelatedObjectSet.EMPTY_SET; } @Override diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/RelatedObjectSet.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/RelatedObjectSet.java index 58bee372e..f49876dfb 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/RelatedObjectSet.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/RelatedObjectSet.java @@ -19,8 +19,10 @@ package org.apache.guacamole.net.auth; +import java.util.Collections; import java.util.Set; import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.GuacamoleSecurityException; /** * An arbitrary set of existing objects sharing some common relation. Unlike a @@ -75,4 +77,28 @@ public interface RelatedObjectSet { */ void removeObjects(Set identifiers) throws GuacamoleException; + /** + * An immutable instance of RelatedObjectSEt which contains no objects. + */ + static final RelatedObjectSet EMPTY_SET = new RelatedObjectSet() { + + @Override + public Set getObjects() throws GuacamoleException { + return Collections.emptySet(); + } + + @Override + public void addObjects(Set identifiers) + throws GuacamoleException { + throw new GuacamoleSecurityException("Permission denied."); + } + + @Override + public void removeObjects(Set identifiers) + throws GuacamoleException { + throw new GuacamoleSecurityException("Permission denied."); + } + + }; + } diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/permission/ObjectPermissionSet.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/permission/ObjectPermissionSet.java index 3c3e910b5..d9ce66616 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/permission/ObjectPermissionSet.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/permission/ObjectPermissionSet.java @@ -20,8 +20,10 @@ package org.apache.guacamole.net.auth.permission; import java.util.Collection; +import java.util.Collections; import java.util.Set; import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.GuacamoleSecurityException; /** @@ -126,4 +128,54 @@ public interface ObjectPermissionSet extends PermissionSet { void removePermissions(Set permissions) throws GuacamoleException; + /** + * An immutable instance of ObjectPermissionSet which contains no + * permissions. + */ + static final ObjectPermissionSet EMPTY_SET = new ObjectPermissionSet() { + + @Override + public boolean hasPermission(ObjectPermission.Type permission, + String identifier) throws GuacamoleException { + return false; + } + + @Override + public void addPermission(ObjectPermission.Type permission, + String identifier) throws GuacamoleException { + throw new GuacamoleSecurityException("Permission denied."); + } + + @Override + public void removePermission(ObjectPermission.Type permission, + String identifier) throws GuacamoleException { + throw new GuacamoleSecurityException("Permission denied."); + } + + @Override + public Collection getAccessibleObjects(Collection permissions, + Collection identifiers) throws GuacamoleException { + return Collections.emptySet(); + } + + @Override + public Set getPermissions() + throws GuacamoleException { + return Collections.emptySet(); + } + + @Override + public void addPermissions(Set permissions) + throws GuacamoleException { + throw new GuacamoleSecurityException("Permission denied."); + } + + @Override + public void removePermissions(Set permissions) + throws GuacamoleException { + throw new GuacamoleSecurityException("Permission denied."); + } + + }; + } diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/permission/SystemPermissionSet.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/permission/SystemPermissionSet.java index 3f504db48..b58baa609 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/permission/SystemPermissionSet.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/permission/SystemPermissionSet.java @@ -19,8 +19,10 @@ package org.apache.guacamole.net.auth.permission; +import java.util.Collections; import java.util.Set; import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.GuacamoleSecurityException; /** @@ -81,4 +83,48 @@ public interface SystemPermissionSet extends PermissionSet { void removePermissions(Set permissions) throws GuacamoleException; + /** + * An immutable instance of SystemPermissionSet which contains no + * permissions. + */ + static final SystemPermissionSet EMPTY_SET = new SystemPermissionSet() { + + @Override + public boolean hasPermission(SystemPermission.Type permission) + throws GuacamoleException { + return false; + } + + @Override + public void addPermission(SystemPermission.Type permission) + throws GuacamoleException { + throw new GuacamoleSecurityException("Permission denied."); + } + + @Override + public void removePermission(SystemPermission.Type permission) + throws GuacamoleException { + throw new GuacamoleSecurityException("Permission denied."); + } + + @Override + public Set getPermissions() + throws GuacamoleException { + return Collections.emptySet(); + } + + @Override + public void addPermissions(Set permissions) + throws GuacamoleException { + throw new GuacamoleSecurityException("Permission denied."); + } + + @Override + public void removePermissions(Set permissions) + throws GuacamoleException { + throw new GuacamoleSecurityException("Permission denied."); + } + + }; + } diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleObjectPermissionSet.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleObjectPermissionSet.java index 53a30ce19..1fd9ac721 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleObjectPermissionSet.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleObjectPermissionSet.java @@ -41,7 +41,10 @@ public class SimpleObjectPermissionSet implements ObjectPermissionSet { private Set permissions = Collections.emptySet(); /** - * Creates a new empty SimpleObjectPermissionSet. + * Creates a new empty SimpleObjectPermissionSet. If you are not extending + * SimpleObjectPermissionSet and only need an immutable, empty + * ObjectPermissionSet, consider using {@link ObjectPermissionSet#EMPTY_SET} + * instead. */ public SimpleObjectPermissionSet() { } diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleRelatedObjectSet.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleRelatedObjectSet.java index 72f2da9f1..e1352cdcd 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleRelatedObjectSet.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleRelatedObjectSet.java @@ -37,7 +37,10 @@ public class SimpleRelatedObjectSet implements RelatedObjectSet { private Set identifiers = Collections.emptySet(); /** - * Creates a new empty SimpleObjectPermissionSet. + * Creates a new empty SimpleRelatedObjectSet. If you are not extending + * SimpleRelatedObjectSet and only need an immutable, empty + * RelatedObjectSet, consider using {@link RelatedObjectSet#EMPTY_SET} + * instead. */ public SimpleRelatedObjectSet() { } diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleSystemPermissionSet.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleSystemPermissionSet.java index ac82c838b..dd0479bea 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleSystemPermissionSet.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleSystemPermissionSet.java @@ -38,7 +38,10 @@ public class SimpleSystemPermissionSet implements SystemPermissionSet { private Set permissions = Collections.emptySet(); /** - * Creates a new empty SimpleSystemPermissionSet. + * Creates a new empty SimpleSystemPermissionSet. If you are not extending + * SimpleSystemPermissionSet and only need an immutable, empty + * SystemPermissionSet, consider using {@link SystemPermissionSet#EMPTY_SET} + * instead. */ public SimpleSystemPermissionSet() { } diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleUser.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleUser.java index cce8bf01b..953039243 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleUser.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleUser.java @@ -193,7 +193,7 @@ public class SimpleUser extends AbstractUser { @Override public SystemPermissionSet getSystemPermissions() throws GuacamoleException { - return new SimpleSystemPermissionSet(); + return SystemPermissionSet.EMPTY_SET; } @Override @@ -217,23 +217,23 @@ public class SimpleUser extends AbstractUser { @Override public ObjectPermissionSet getUserGroupPermissions() throws GuacamoleException { - return new SimpleObjectPermissionSet(); + return ObjectPermissionSet.EMPTY_SET; } @Override public ObjectPermissionSet getActiveConnectionPermissions() throws GuacamoleException { - return new SimpleObjectPermissionSet(); + return ObjectPermissionSet.EMPTY_SET; } @Override public ObjectPermissionSet getSharingProfilePermissions() { - return new SimpleObjectPermissionSet(); + return ObjectPermissionSet.EMPTY_SET; } @Override public RelatedObjectSet getUserGroups() throws GuacamoleException { - return new SimpleRelatedObjectSet(); + return RelatedObjectSet.EMPTY_SET; } @Override diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleUserGroup.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleUserGroup.java index 222320667..83b7ce91f 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleUserGroup.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleUserGroup.java @@ -62,57 +62,57 @@ public class SimpleUserGroup extends AbstractIdentifiable implements UserGroup { @Override public SystemPermissionSet getSystemPermissions() throws GuacamoleException { - return new SimpleSystemPermissionSet(); + return SystemPermissionSet.EMPTY_SET; } @Override public ObjectPermissionSet getConnectionPermissions() throws GuacamoleException { - return new SimpleObjectPermissionSet(); + return ObjectPermissionSet.EMPTY_SET; } @Override public ObjectPermissionSet getConnectionGroupPermissions() throws GuacamoleException { - return new SimpleObjectPermissionSet(); + return ObjectPermissionSet.EMPTY_SET; } @Override public ObjectPermissionSet getUserPermissions() throws GuacamoleException { - return new SimpleObjectPermissionSet(); + return ObjectPermissionSet.EMPTY_SET; } @Override public ObjectPermissionSet getUserGroupPermissions() throws GuacamoleException { - return new SimpleObjectPermissionSet(); + return ObjectPermissionSet.EMPTY_SET; } @Override public ObjectPermissionSet getActiveConnectionPermissions() throws GuacamoleException { - return new SimpleObjectPermissionSet(); + return ObjectPermissionSet.EMPTY_SET; } @Override public ObjectPermissionSet getSharingProfilePermissions() { - return new SimpleObjectPermissionSet(); + return ObjectPermissionSet.EMPTY_SET; } @Override public RelatedObjectSet getUserGroups() throws GuacamoleException { - return new SimpleRelatedObjectSet(); + return RelatedObjectSet.EMPTY_SET; } @Override public RelatedObjectSet getMemberUsers() throws GuacamoleException { - return new SimpleRelatedObjectSet(); + return RelatedObjectSet.EMPTY_SET; } @Override public RelatedObjectSet getMemberUserGroups() throws GuacamoleException { - return new SimpleRelatedObjectSet(); + return RelatedObjectSet.EMPTY_SET; } } From 90a6d8e371181e8e4e626a2cee5aed9fd0b0b678 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sat, 3 Nov 2018 14:14:04 -0700 Subject: [PATCH 08/12] GUACAMOLE-220: Clarify usage of SimpleUser and SimpleUserGroup. --- .../org/apache/guacamole/net/auth/simple/SimpleUser.java | 6 ++++-- .../apache/guacamole/net/auth/simple/SimpleUserGroup.java | 4 +++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleUser.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleUser.java index 953039243..d85cc9763 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleUser.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleUser.java @@ -36,7 +36,9 @@ import org.apache.guacamole.net.auth.permission.ObjectPermissionSet; import org.apache.guacamole.net.auth.permission.SystemPermissionSet; /** - * An extremely basic User implementation. + * A read-only User implementation which has no permissions. Implementations + * that need to define permissions should extend this class and override the + * associated getters. */ public class SimpleUser extends AbstractUser { @@ -62,7 +64,7 @@ public class SimpleUser extends AbstractUser { } /** - * Creates a new SimpleUser having the given username and no permissions. + * Creates a new SimpleUser having the given username. * * @param username * The username to assign to this SimpleUser. diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleUserGroup.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleUserGroup.java index 83b7ce91f..77b374149 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleUserGroup.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleUserGroup.java @@ -29,7 +29,9 @@ import org.apache.guacamole.net.auth.permission.ObjectPermissionSet; import org.apache.guacamole.net.auth.permission.SystemPermissionSet; /** - * A read-only UserGroup implementation which has no members. + * A read-only UserGroup implementation which has no members and no + * permissions. Implementations that need to define members or permissions + * should extend this class and override the associated getters. */ public class SimpleUserGroup extends AbstractIdentifiable implements UserGroup { From 2d6ba84a3de4bdd126a033aa357a6ea3593cb85f Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sat, 3 Nov 2018 14:52:24 -0700 Subject: [PATCH 09/12] GUACAMOLE-220: Refactor default behavior of SimpleUser and SimpleUserGroup into AbstractUser and AbstractUserGroup. --- .../guacamole/net/auth/AbstractUser.java | 171 +++++++++++++++- .../guacamole/net/auth/AbstractUserGroup.java | 183 ++++++++++++++++++ .../guacamole/net/auth/simple/SimpleUser.java | 64 ------ .../net/auth/simple/SimpleUserGroup.java | 77 +------- 4 files changed, 354 insertions(+), 141 deletions(-) create mode 100644 guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AbstractUserGroup.java diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AbstractUser.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AbstractUser.java index 2dae70235..0a1acbfc6 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AbstractUser.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AbstractUser.java @@ -19,10 +19,17 @@ package org.apache.guacamole.net.auth; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Map; +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.net.auth.permission.ObjectPermissionSet; +import org.apache.guacamole.net.auth.permission.SystemPermissionSet; /** - * Basic implementation of a Guacamole user which uses the username to - * determine equality. Username comparison is case-sensitive. + * Base implementation of User which provides default implementations of + * most functions. */ public abstract class AbstractUser extends AbstractIdentifiable implements User { @@ -44,4 +51,164 @@ public abstract class AbstractUser extends AbstractIdentifiable this.password = password; } + /** + * {@inheritDoc} + * + *

This implementation simply an immutable, empty map. Implementations + * that wish to expose custom attributes should override this function. + */ + @Override + public Map getAttributes() { + return Collections.emptyMap(); + } + + /** + * {@inheritDoc} + * + *

This implementation simply ignores all attributes given. + * Implementations that wish to support modification of custom attributes + * should override this function. + */ + @Override + public void setAttributes(Map attributes) { + // Ignore all attributes by default + } + + /** + * {@inheritDoc} + * + *

This implementation simply returns {@code null}. Implementations that + * wish to expose the date and time that a user was last active should + * override this function. + */ + @Override + public Date getLastActive() { + return null; + } + + /** + * {@inheritDoc} + * + *

This implementation simply an immutable, empty list. Implementations + * that wish to expose user login history should override this function. + */ + @Override + public List getHistory() throws GuacamoleException { + return Collections.emptyList(); + } + + /** + * {@inheritDoc} + * + *

This implementation simply an immutable, empty permission set. + * Implementations that wish to expose permissions should override this + * function. + */ + @Override + public SystemPermissionSet getSystemPermissions() + throws GuacamoleException { + return SystemPermissionSet.EMPTY_SET; + } + + /** + * {@inheritDoc} + * + *

This implementation simply an immutable, empty permission set. + * Implementations that wish to expose permissions should override this + * function. + */ + @Override + public ObjectPermissionSet getConnectionPermissions() + throws GuacamoleException { + return ObjectPermissionSet.EMPTY_SET; + } + + /** + * {@inheritDoc} + * + *

This implementation simply an immutable, empty permission set. + * Implementations that wish to expose permissions should override this + * function. + */ + @Override + public ObjectPermissionSet getConnectionGroupPermissions() + throws GuacamoleException { + return ObjectPermissionSet.EMPTY_SET; + } + + /** + * {@inheritDoc} + * + *

This implementation simply an immutable, empty permission set. + * Implementations that wish to expose permissions should override this + * function. + */ + @Override + public ObjectPermissionSet getUserPermissions() + throws GuacamoleException { + return ObjectPermissionSet.EMPTY_SET; + } + + /** + * {@inheritDoc} + * + *

This implementation simply an immutable, empty permission set. + * Implementations that wish to expose permissions should override this + * function. + */ + @Override + public ObjectPermissionSet getUserGroupPermissions() + throws GuacamoleException { + return ObjectPermissionSet.EMPTY_SET; + } + + /** + * {@inheritDoc} + * + *

This implementation simply an immutable, empty permission set. + * Implementations that wish to expose permissions should override this + * function. + */ + @Override + public ObjectPermissionSet getActiveConnectionPermissions() + throws GuacamoleException { + return ObjectPermissionSet.EMPTY_SET; + } + + /** + * {@inheritDoc} + * + *

This implementation simply an immutable, empty permission set. + * Implementations that wish to expose permissions should override this + * function. + */ + @Override + public ObjectPermissionSet getSharingProfilePermissions() { + return ObjectPermissionSet.EMPTY_SET; + } + + /** + * {@inheritDoc} + * + *

This implementation simply an immutable, empty related object set. + * Implementations that wish to expose group membership should override + * this function. + */ + @Override + public RelatedObjectSet getUserGroups() throws GuacamoleException { + return RelatedObjectSet.EMPTY_SET; + } + + /** + * {@inheritDoc} + * + *

This implementation simply returns {@code this}. Implementations that + * wish to expose permissions which apply indirectly (such as through + * group inheritance) should override this function. + */ + @Override + public Permissions getEffectivePermissions() throws GuacamoleException { + return this; + } + } diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AbstractUserGroup.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AbstractUserGroup.java new file mode 100644 index 000000000..725984d6f --- /dev/null +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AbstractUserGroup.java @@ -0,0 +1,183 @@ +/* + * 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.net.auth; + +import java.util.Collections; +import java.util.Map; +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.net.auth.permission.ObjectPermissionSet; +import org.apache.guacamole.net.auth.permission.SystemPermissionSet; + +/** + * Base implementation of UserGroup which provides default implementations of + * most functions. + */ +public class AbstractUserGroup extends AbstractIdentifiable implements UserGroup { + + /** + * {@inheritDoc} + * + *

This implementation simply an immutable, empty map. Implementations + * that wish to expose custom attributes should override this function. + */ + @Override + public Map getAttributes() { + return Collections.emptyMap(); + } + + /** + * {@inheritDoc} + * + *

This implementation simply ignores all attributes given. + * Implementations that wish to support modification of custom attributes + * should override this function. + */ + @Override + public void setAttributes(Map attributes) { + // Ignore all attributes by default + } + + /** + * {@inheritDoc} + * + *

This implementation simply an immutable, empty permission set. + * Implementations that wish to expose permissions should override this + * function. + */ + @Override + public SystemPermissionSet getSystemPermissions() + throws GuacamoleException { + return SystemPermissionSet.EMPTY_SET; + } + + /** + * {@inheritDoc} + * + *

This implementation simply an immutable, empty permission set. + * Implementations that wish to expose permissions should override this + * function. + */ + @Override + public ObjectPermissionSet getConnectionPermissions() + throws GuacamoleException { + return ObjectPermissionSet.EMPTY_SET; + } + + /** + * {@inheritDoc} + * + *

This implementation simply an immutable, empty permission set. + * Implementations that wish to expose permissions should override this + * function. + */ + @Override + public ObjectPermissionSet getConnectionGroupPermissions() + throws GuacamoleException { + return ObjectPermissionSet.EMPTY_SET; + } + + /** + * {@inheritDoc} + * + *

This implementation simply an immutable, empty permission set. + * Implementations that wish to expose permissions should override this + * function. + */ + @Override + public ObjectPermissionSet getUserPermissions() + throws GuacamoleException { + return ObjectPermissionSet.EMPTY_SET; + } + + /** + * {@inheritDoc} + * + *

This implementation simply an immutable, empty permission set. + * Implementations that wish to expose permissions should override this + * function. + */ + @Override + public ObjectPermissionSet getUserGroupPermissions() + throws GuacamoleException { + return ObjectPermissionSet.EMPTY_SET; + } + + /** + * {@inheritDoc} + * + *

This implementation simply an immutable, empty permission set. + * Implementations that wish to expose permissions should override this + * function. + */ + @Override + public ObjectPermissionSet getActiveConnectionPermissions() + throws GuacamoleException { + return ObjectPermissionSet.EMPTY_SET; + } + + /** + * {@inheritDoc} + * + *

This implementation simply an immutable, empty permission set. + * Implementations that wish to expose permissions should override this + * function. + */ + @Override + public ObjectPermissionSet getSharingProfilePermissions() { + return ObjectPermissionSet.EMPTY_SET; + } + + /** + * {@inheritDoc} + * + *

This implementation simply an immutable, empty related object set. + * Implementations that wish to expose group membership should override + * this function. + */ + @Override + public RelatedObjectSet getUserGroups() throws GuacamoleException { + return RelatedObjectSet.EMPTY_SET; + } + + /** + * {@inheritDoc} + * + *

This implementation simply an immutable, empty related object set. + * Implementations that wish to expose group membership should override + * this function. + */ + @Override + public RelatedObjectSet getMemberUsers() throws GuacamoleException { + return RelatedObjectSet.EMPTY_SET; + } + + /** + * {@inheritDoc} + * + *

This implementation simply an immutable, empty related object set. + * Implementations that wish to expose group membership should override + * this function. + */ + @Override + public RelatedObjectSet getMemberUserGroups() throws GuacamoleException { + return RelatedObjectSet.EMPTY_SET; + } + +} diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleUser.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleUser.java index d85cc9763..8e349e27c 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleUser.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleUser.java @@ -20,20 +20,12 @@ package org.apache.guacamole.net.auth.simple; import java.util.Collection; -import java.util.Collections; -import java.util.Date; import java.util.HashSet; -import java.util.List; -import java.util.Map; import java.util.Set; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.net.auth.AbstractUser; -import org.apache.guacamole.net.auth.ActivityRecord; -import org.apache.guacamole.net.auth.Permissions; -import org.apache.guacamole.net.auth.RelatedObjectSet; import org.apache.guacamole.net.auth.permission.ObjectPermission; import org.apache.guacamole.net.auth.permission.ObjectPermissionSet; -import org.apache.guacamole.net.auth.permission.SystemPermissionSet; /** * A read-only User implementation which has no permissions. Implementations @@ -70,10 +62,7 @@ public class SimpleUser extends AbstractUser { * The username to assign to this SimpleUser. */ public SimpleUser(String username) { - - // Set username super.setIdentifier(username); - } /** @@ -172,32 +161,6 @@ public class SimpleUser extends AbstractUser { } - @Override - public Map getAttributes() { - return Collections.emptyMap(); - } - - @Override - public void setAttributes(Map attributes) { - // Do nothing - there are no attributes - } - - @Override - public Date getLastActive() { - return null; - } - - @Override - public List getHistory() throws GuacamoleException { - return Collections.emptyList(); - } - - @Override - public SystemPermissionSet getSystemPermissions() - throws GuacamoleException { - return SystemPermissionSet.EMPTY_SET; - } - @Override public ObjectPermissionSet getConnectionPermissions() throws GuacamoleException { @@ -216,31 +179,4 @@ public class SimpleUser extends AbstractUser { return new SimpleObjectPermissionSet(userPermissions); } - @Override - public ObjectPermissionSet getUserGroupPermissions() - throws GuacamoleException { - return ObjectPermissionSet.EMPTY_SET; - } - - @Override - public ObjectPermissionSet getActiveConnectionPermissions() - throws GuacamoleException { - return ObjectPermissionSet.EMPTY_SET; - } - - @Override - public ObjectPermissionSet getSharingProfilePermissions() { - return ObjectPermissionSet.EMPTY_SET; - } - - @Override - public RelatedObjectSet getUserGroups() throws GuacamoleException { - return RelatedObjectSet.EMPTY_SET; - } - - @Override - public Permissions getEffectivePermissions() throws GuacamoleException { - return this; - } - } diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleUserGroup.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleUserGroup.java index 77b374149..be52d1642 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleUserGroup.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleUserGroup.java @@ -19,21 +19,14 @@ package org.apache.guacamole.net.auth.simple; -import java.util.Collections; -import java.util.Map; -import org.apache.guacamole.GuacamoleException; -import org.apache.guacamole.net.auth.AbstractIdentifiable; -import org.apache.guacamole.net.auth.RelatedObjectSet; -import org.apache.guacamole.net.auth.UserGroup; -import org.apache.guacamole.net.auth.permission.ObjectPermissionSet; -import org.apache.guacamole.net.auth.permission.SystemPermissionSet; +import org.apache.guacamole.net.auth.AbstractUserGroup; /** * A read-only UserGroup implementation which has no members and no * permissions. Implementations that need to define members or permissions * should extend this class and override the associated getters. */ -public class SimpleUserGroup extends AbstractIdentifiable implements UserGroup { +public class SimpleUserGroup extends AbstractUserGroup { /** * Creates a completely uninitialized SimpleUserGroup. @@ -51,70 +44,4 @@ public class SimpleUserGroup extends AbstractIdentifiable implements UserGroup { super.setIdentifier(identifier); } - @Override - public Map getAttributes() { - return Collections.emptyMap(); - } - - @Override - public void setAttributes(Map attributes) { - // Do nothing - there are no attributes - } - - @Override - public SystemPermissionSet getSystemPermissions() - throws GuacamoleException { - return SystemPermissionSet.EMPTY_SET; - } - - @Override - public ObjectPermissionSet getConnectionPermissions() - throws GuacamoleException { - return ObjectPermissionSet.EMPTY_SET; - } - - @Override - public ObjectPermissionSet getConnectionGroupPermissions() - throws GuacamoleException { - return ObjectPermissionSet.EMPTY_SET; - } - - @Override - public ObjectPermissionSet getUserPermissions() - throws GuacamoleException { - return ObjectPermissionSet.EMPTY_SET; - } - - @Override - public ObjectPermissionSet getUserGroupPermissions() - throws GuacamoleException { - return ObjectPermissionSet.EMPTY_SET; - } - - @Override - public ObjectPermissionSet getActiveConnectionPermissions() - throws GuacamoleException { - return ObjectPermissionSet.EMPTY_SET; - } - - @Override - public ObjectPermissionSet getSharingProfilePermissions() { - return ObjectPermissionSet.EMPTY_SET; - } - - @Override - public RelatedObjectSet getUserGroups() throws GuacamoleException { - return RelatedObjectSet.EMPTY_SET; - } - - @Override - public RelatedObjectSet getMemberUsers() throws GuacamoleException { - return RelatedObjectSet.EMPTY_SET; - } - - @Override - public RelatedObjectSet getMemberUserGroups() throws GuacamoleException { - return RelatedObjectSet.EMPTY_SET; - } - } From 83033cad6501008e64dca6457397f603b38797db Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Thu, 8 Nov 2018 19:45:25 -0800 Subject: [PATCH 10/12] GUACAMOLE-220: Use JavaDoc "{@link Map}" to point to Map interface. --- .../guacamole/auth/ldap/ObjectQueryService.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) 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 89a43c327..94550a2a5 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 @@ -43,7 +43,7 @@ import org.slf4j.LoggerFactory; * Guacamole-related objects. Referrals are automatically handled. Convenience * functions are provided for generating the LDAP queries typically required * for retrieving Guacamole objects, as well as for converting the results of a - * query into a Map of Guacamole objects. + * query into a {@link Map} of Guacamole objects. */ public class ObjectQueryService { @@ -277,11 +277,11 @@ public class ObjectQueryService { } /** - * Converts a given list of LDAP entries to a map of Guacamole objects - * stored by their identifiers. + * Converts a given list of LDAP entries to a {@link Map} of Guacamole + * objects stored by their identifiers. * * @param - * The type of object to store within the map. + * The type of object to store within the {@link Map}. * * @param entries * A list of LDAP entries to convert to Guacamole objects. @@ -292,9 +292,9 @@ public class ObjectQueryService { * converted, null should be returned. * * @return - * A new map containing Guacamole object versions of each of the given - * LDAP entries, where each object is stored within the map under its - * corresponding identifier. + * A new {@link Map} containing Guacamole object versions of each of + * the given LDAP entries, where each object is stored within the + * {@link Map} under its corresponding identifier. */ public Map asMap(List entries, Function mapper) { From a4d6f62df0f4be0ca7f97571e7c3e83c4be5a69a Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Thu, 8 Nov 2018 19:55:49 -0800 Subject: [PATCH 11/12] GUACAMOLE-220: Reword description of getGroupSearchFilter() to be less brain-meltingly difficult to read. --- .../apache/guacamole/auth/ldap/group/UserGroupService.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 dfdd9fd92..fbfeaaf32 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 @@ -62,9 +62,9 @@ public class UserGroupService { /** * 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. + * guacConfigGroup object class may not work as expected if it is not + * defined (may always return zero results), 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. From b355106c77387973842cd4122d62201eaa22a7dd Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Thu, 8 Nov 2018 19:56:17 -0800 Subject: [PATCH 12/12] GUACAMOLE-220: Remove unnecessary explicit type parameters. --- .../apache/guacamole/auth/ldap/group/UserGroupService.java | 4 ++-- .../main/java/org/apache/guacamole/net/auth/AbstractUser.java | 4 ++-- .../java/org/apache/guacamole/net/auth/AbstractUserGroup.java | 2 +- .../java/org/apache/guacamole/net/auth/RelatedObjectSet.java | 2 +- .../guacamole/net/auth/permission/ObjectPermissionSet.java | 4 ++-- .../guacamole/net/auth/permission/SystemPermissionSet.java | 2 +- .../guacamole/net/auth/simple/SimpleObjectPermissionSet.java | 2 +- .../guacamole/net/auth/simple/SimpleRelatedObjectSet.java | 2 +- .../guacamole/net/auth/simple/SimpleSystemPermissionSet.java | 2 +- 9 files changed, 12 insertions(+), 12 deletions(-) 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 fbfeaaf32..14cfd8e3a 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 @@ -108,7 +108,7 @@ public class UserGroupService { // Do not return any user groups if base DN is not specified String groupBaseDN = confService.getGroupBaseDN(); if (groupBaseDN == null) - return Collections.emptyMap(); + return Collections.emptyMap(); // Retrieve all visible user groups which are not guacConfigGroups Collection attributes = confService.getGroupNameAttributes(); @@ -163,7 +163,7 @@ public class UserGroupService { // Do not return any user groups if base DN is not specified String groupBaseDN = confService.getGroupBaseDN(); if (groupBaseDN == null) - return Collections.emptyList(); + return Collections.emptyList(); // Get all groups the user is a member of starting at the groupBaseDN, // excluding guacConfigGroups diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AbstractUser.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AbstractUser.java index 0a1acbfc6..b502f7a1b 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AbstractUser.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AbstractUser.java @@ -59,7 +59,7 @@ public abstract class AbstractUser extends AbstractIdentifiable */ @Override public Map getAttributes() { - return Collections.emptyMap(); + return Collections.emptyMap(); } /** @@ -94,7 +94,7 @@ public abstract class AbstractUser extends AbstractIdentifiable */ @Override public List getHistory() throws GuacamoleException { - return Collections.emptyList(); + return Collections.emptyList(); } /** diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AbstractUserGroup.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AbstractUserGroup.java index 725984d6f..f63bfae7f 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AbstractUserGroup.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AbstractUserGroup.java @@ -39,7 +39,7 @@ public class AbstractUserGroup extends AbstractIdentifiable implements UserGroup */ @Override public Map getAttributes() { - return Collections.emptyMap(); + return Collections.emptyMap(); } /** diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/RelatedObjectSet.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/RelatedObjectSet.java index f49876dfb..05f4c249e 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/RelatedObjectSet.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/RelatedObjectSet.java @@ -84,7 +84,7 @@ public interface RelatedObjectSet { @Override public Set getObjects() throws GuacamoleException { - return Collections.emptySet(); + return Collections.emptySet(); } @Override diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/permission/ObjectPermissionSet.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/permission/ObjectPermissionSet.java index d9ce66616..638f95246 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/permission/ObjectPermissionSet.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/permission/ObjectPermissionSet.java @@ -155,13 +155,13 @@ public interface ObjectPermissionSet extends PermissionSet { @Override public Collection getAccessibleObjects(Collection permissions, Collection identifiers) throws GuacamoleException { - return Collections.emptySet(); + return Collections.emptySet(); } @Override public Set getPermissions() throws GuacamoleException { - return Collections.emptySet(); + return Collections.emptySet(); } @Override diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/permission/SystemPermissionSet.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/permission/SystemPermissionSet.java index b58baa609..52b62cd6e 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/permission/SystemPermissionSet.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/permission/SystemPermissionSet.java @@ -110,7 +110,7 @@ public interface SystemPermissionSet extends PermissionSet { @Override public Set getPermissions() throws GuacamoleException { - return Collections.emptySet(); + return Collections.emptySet(); } @Override diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleObjectPermissionSet.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleObjectPermissionSet.java index 1fd9ac721..cbb298e90 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleObjectPermissionSet.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleObjectPermissionSet.java @@ -38,7 +38,7 @@ public class SimpleObjectPermissionSet implements ObjectPermissionSet { /** * The set of all permissions currently granted. */ - private Set permissions = Collections.emptySet(); + private Set permissions = Collections.emptySet(); /** * Creates a new empty SimpleObjectPermissionSet. If you are not extending diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleRelatedObjectSet.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleRelatedObjectSet.java index e1352cdcd..3796c43fa 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleRelatedObjectSet.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleRelatedObjectSet.java @@ -34,7 +34,7 @@ public class SimpleRelatedObjectSet implements RelatedObjectSet { /** * A set containing the identifiers of all objects currently present. */ - private Set identifiers = Collections.emptySet(); + private Set identifiers = Collections.emptySet(); /** * Creates a new empty SimpleRelatedObjectSet. If you are not extending diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleSystemPermissionSet.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleSystemPermissionSet.java index dd0479bea..24c6d2d71 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleSystemPermissionSet.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleSystemPermissionSet.java @@ -35,7 +35,7 @@ public class SimpleSystemPermissionSet implements SystemPermissionSet { /** * The set of all permissions currently granted. */ - private Set permissions = Collections.emptySet(); + private Set permissions = Collections.emptySet(); /** * Creates a new empty SimpleSystemPermissionSet. If you are not extending