diff --git a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLConnection.java b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLConnection.java index 1ca51a0ad..b01563f0a 100644 --- a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLConnection.java +++ b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLConnection.java @@ -30,7 +30,23 @@ public class MySQLConnection implements Connection { */ MySQLConnection() { connection = new net.sourceforge.guacamole.net.auth.mysql.model.Connection(); - } + } + + /** + * Get the ID of the underlying connection record. + * @return the ID of the underlying connection + */ + public int getConnectionID() { + return connection.getConnection_id(); + } + + /** + * Get the underlying connection database record. + * @return the underlying connection record. + */ + public net.sourceforge.guacamole.net.auth.mysql.model.Connection getConnection() { + return connection; + } /** * Load an existing connection by name. @@ -82,4 +98,10 @@ public class MySQLConnection implements Connection { throw new UnsupportedOperationException("Not supported yet."); } + @Override + public boolean equals(Object other) { + if(!(other instanceof MySQLConnection)) + return false; + return ((MySQLConnection)other).getConnectionID() == this.getConnectionID(); + } } diff --git a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLUser.java b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLUser.java index 7fe4bd554..ad4e36ca7 100644 --- a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLUser.java +++ b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLUser.java @@ -202,4 +202,11 @@ public class MySQLUser implements User { public void removePermission(Permission permission) throws GuacamoleException { permissions.remove(permission); } + + @Override + public boolean equals(Object other) { + if(!(other instanceof MySQLUser)) + return false; + return ((MySQLUser)other).getUserID() == this.getUserID(); + } } diff --git a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/UserDirectory.java b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/UserDirectory.java index 6690d82eb..6adfa2d96 100644 --- a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/UserDirectory.java +++ b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/UserDirectory.java @@ -155,7 +155,7 @@ public class UserDirectory implements Directory { @Override public Set getIdentifiers() throws GuacamoleException { Set userNameSet = new HashSet(); - List users = permissionCheckUtility.getReadableUsers(user.getUserID()); + Set users = permissionCheckUtility.getReadableUsers(user.getUserID()); for(MySQLUser mySQLUser : users) { userNameSet.add(mySQLUser.getUsername()); } @@ -189,6 +189,11 @@ public class UserDirectory implements Directory { List connectionPermissions = new ArrayList(); List systemPermissions = new ArrayList(); + // Get the list of all the users and connections that the user performing the user save action has. + // Need to make sure the user saving this user has permission to administrate all the objects in the permission list. + Set administerableUsers = permissionCheckUtility.getAdministerableUserIDs(this.user.getUserID()); + Set administerableConnections = permissionCheckUtility.getAdministerableConnectionIDs(this.user.getUserID()); + for(Permission permission : user.getPermissions()) { if(permission instanceof UserPermission) userPermissions.add((UserPermission)permission); @@ -198,8 +203,8 @@ public class UserDirectory implements Directory { systemPermissions.add((SystemPermission)permission); } - updateUserPermissions(userPermissions, user); - updateConnectionPermissions(connectionPermissions, user); + updateUserPermissions(userPermissions, user, administerableUsers); + updateConnectionPermissions(connectionPermissions, user, administerableConnections); updateSystemPermissions(systemPermissions, user); } @@ -208,7 +213,7 @@ public class UserDirectory implements Directory { * @param permissions * @param user */ - private void updateUserPermissions(Iterable permissions, MySQLUser user) throws GuacamoleException { + private void updateUserPermissions(Iterable permissions, MySQLUser user, Set administerableUsers) throws GuacamoleException { List usernames = new ArrayList(); for(UserPermission permission : permissions) { @@ -239,6 +244,14 @@ public class UserDirectory implements Directory { // delete any permissions that are not in the provided list userPermissionExample.clear(); userPermissionExample.createCriteria().andAffected_user_idNotIn(userIDs); + List permissionsToDelete = userPermissionDAO.selectByExample(userPermissionExample); + + // verify that the user actually has permission to administrate every one of these users + for(UserPermissionKey permissionToDelete : permissionsToDelete) { + if(!administerableUsers.contains(permissionToDelete.getAffected_user_id())) + throw new GuacamolePermissionException("User '" + this.user.getUsername() + "' does not have permission to administrate user " + permissionToDelete.getAffected_user_id()); + } + userPermissionDAO.deleteByExample(userPermissionExample); // finally, insert the new permissions @@ -251,6 +264,11 @@ public class UserDirectory implements Directory { if(existingUserIDs.contains(dbAffectedUser.getUser_id())) continue; + + // verify that the user actually has permission to administrate every one of these users + if(!administerableUsers.contains(dbAffectedUser.getUser_id())) + throw new GuacamolePermissionException("User '" + this.user.getUsername() + "' does not have permission to administrate user " + dbAffectedUser.getUser_id()); + UserPermissionKey newPermission = new UserPermissionKey(); newPermission.setAffected_user_id(dbAffectedUser.getUser_id()); newPermission.setPermission(permission.getType().name()); @@ -264,7 +282,7 @@ public class UserDirectory implements Directory { * @param permissions * @param user */ - private void updateConnectionPermissions(Iterable permissions, MySQLUser user) throws GuacamoleException { + private void updateConnectionPermissions(Iterable permissions, MySQLUser user, Set administerableConnections) throws GuacamoleException { List connectionnames = new ArrayList(); for(ConnectionPermission permission : permissions) { @@ -295,6 +313,15 @@ public class UserDirectory implements Directory { // delete any permissions that are not in the provided list connectionPermissionExample.clear(); connectionPermissionExample.createCriteria().andConnection_idNotIn(connectionIDs); + + //make sure the user has permission to administrate each of these connections + List connectionPermissionsToDelete = connectionPermissionDAO.selectByExample(connectionPermissionExample); + + for(ConnectionPermissionKey connectionPermissionToDelete : connectionPermissionsToDelete) { + if(!administerableConnections.contains(connectionPermissionToDelete.getConnection_id())) + throw new GuacamolePermissionException("User '" + this.user.getUsername() + "' does not have permission to administrate connection " + connectionPermissionToDelete.getConnection_id()); + } + connectionPermissionDAO.deleteByExample(connectionPermissionExample); // finally, insert the new permissions @@ -307,6 +334,10 @@ public class UserDirectory implements Directory { if(existingConnectionIDs.contains(dbConnection.getConnection_id())) continue; + if(!administerableConnections.contains(dbConnection.getConnection_id())) + throw new GuacamolePermissionException("User '" + this.user.getUsername() + "' does not have permission to administrate connection " + dbConnection.getConnection_id()); + + ConnectionPermissionKey newPermission = new ConnectionPermissionKey(); newPermission.setConnection_id(dbConnection.getConnection_id()); newPermission.setPermission(permission.getType().name()); diff --git a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/utility/PermissionCheckUtility.java b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/utility/PermissionCheckUtility.java index 0c126fc09..07745c098 100644 --- a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/utility/PermissionCheckUtility.java +++ b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/utility/PermissionCheckUtility.java @@ -35,6 +35,7 @@ * ***** END LICENSE BLOCK ***** */ package net.sourceforge.guacamole.net.auth.mysql.utility; +import com.google.common.collect.Lists; import com.google.inject.Inject; import com.google.inject.Provider; import java.util.ArrayList; @@ -294,12 +295,48 @@ public class PermissionCheckUtility { return count > 0; } + /** + * Find the list of all user IDs a user has permission to administer. + * @param userID + * @return the list of all user IDs this user has administer access to + */ + public Set getAdministerableUserIDs(int userID) { + return getUserIDs(userID, MySQLConstants.USER_ADMINISTER); + } + + /** + * Find the list of all user IDs a user has permission to delete. + * @param userID + * @return the list of all user IDs this user has delete access to + */ + public Set getDeletableUserIDs(int userID) { + return getUserIDs(userID, MySQLConstants.USER_DELETE); + } + + /** + * Find the list of all user IDs a user has permission to write. + * @param userID + * @return the list of all user IDs this user has write access to + */ + public Set getUpdateableUserIDs(int userID) { + return getUserIDs(userID, MySQLConstants.USER_UPDATE); + } + + /** + * Find the list of all user IDs a user has permission to read. + * @param userID + * @return the list of all user IDs this user has read access to + */ + public Set getReadableUserIDs(int userID) { + return getUserIDs(userID, MySQLConstants.USER_READ); + } + /** * Find the list of all users a user has permission to administer. * @param userID * @return the list of all users this user has administer access to */ - public List getAdministerableUsers(int userID) { + public Set getAdministerableUsers(int userID) { return getUsers(userID, MySQLConstants.USER_ADMINISTER); } @@ -308,7 +345,7 @@ public class PermissionCheckUtility { * @param userID * @return the list of all users this user has delete access to */ - public List getDeletableUsers(int userID) { + public Set getDeletableUsers(int userID) { return getUsers(userID, MySQLConstants.USER_DELETE); } @@ -317,7 +354,7 @@ public class PermissionCheckUtility { * @param userID * @return the list of all users this user has write access to */ - public List getUpdateableUsers(int userID) { + public Set getUpdateableUsers(int userID) { return getUsers(userID, MySQLConstants.USER_UPDATE); } @@ -326,7 +363,7 @@ public class PermissionCheckUtility { * @param userID * @return the list of all users this user read has access to */ - public List getReadableUsers(int userID) { + public Set getReadableUsers(int userID) { return getUsers(userID, MySQLConstants.USER_READ); } @@ -337,12 +374,12 @@ public class PermissionCheckUtility { * @param permissionType * @return the list of all users this user has access to */ - private List getUsers(int userID, String permissionType) { - List affectedUserIDs = getUserIDs(userID, permissionType); + private Set getUsers(int userID, String permissionType) { + Set affectedUserIDs = getUserIDs(userID, permissionType); UserExample example = new UserExample(); - example.createCriteria().andUser_idIn(affectedUserIDs); + example.createCriteria().andUser_idIn(Lists.newArrayList(affectedUserIDs)); List userDBOjects = userDAO.selectByExampleWithBLOBs(example); - List affectedUsers = new ArrayList(); + Set affectedUsers = new HashSet(); for(UserWithBLOBs affectedUser : userDBOjects) { MySQLUser mySQLUser = mySQLUserProvider.get(); mySQLUser.init(affectedUser); @@ -359,8 +396,8 @@ public class PermissionCheckUtility { * @param permissionType * @return the list of all user IDs this user has access to */ - private List getUserIDs(int userID, String permissionType) { - List userIDs = new ArrayList(); + private Set getUserIDs(int userID, String permissionType) { + Set userIDs = new HashSet(); UserPermissionExample example = new UserPermissionExample(); example.createCriteria().andUser_idEqualTo(userID).andPermissionEqualTo(permissionType); List userPermissions = userPermissionDAO.selectByExample(example); @@ -568,38 +605,74 @@ public class PermissionCheckUtility { } /** - * Find the list of all connections a user has permission to administer. - * @param connectionID - * @return the list of all connections this connection has administer access to + * Find the list of all connection IDs a user has permission to administer. + * @param userID + * @return the list of all connection IDs this user has administer access to */ - public List getAdministerableConnections(int userID) { + public Set getAdministerableConnectionIDs(int userID) { + return getConnectionIDs(userID, MySQLConstants.CONNECTION_ADMINISTER); + } + + /** + * Find the list of all connection IDs a user has permission to delete. + * @param userID + * @return the list of all connection IDs this user has delete access to + */ + public Set getDeletableConnectionIDs(int userID) { + return getConnectionIDs(userID, MySQLConstants.CONNECTION_DELETE); + } + + /** + * Find the list of all connection IDs a user has permission to write. + * @param userID + * @return the list of all connection IDs this user has write access to + */ + public Set getUpdateableConnectionIDs(int userID) { + return getConnectionIDs(userID, MySQLConstants.CONNECTION_UPDATE); + } + + /** + * Find the list of all connection IDs a user has permission to read. + * @param userID + * @return the list of all connection IDs this user has ready access to + */ + public Set getReadableConnectionIDs(int userID) { + return getConnectionIDs(userID, MySQLConstants.CONNECTION_READ); + } + + /** + * Find the list of all connections a user has permission to administer. + * @param userID + * @return the list of all connections this user has administer access to + */ + public Set getAdministerableConnections(int userID) { return getConnections(userID, MySQLConstants.CONNECTION_ADMINISTER); } /** * Find the list of all connections a user has permission to delete. - * @param connectionID - * @return the list of all connections this connection has delete access to + * @param userID + * @return the list of all connections this user has delete access to */ - public List getDeletableConnections(int userID) { + public Set getDeletableConnections(int userID) { return getConnections(userID, MySQLConstants.CONNECTION_DELETE); } /** * Find the list of all connections a user has permission to write. - * @param connectionID - * @return the list of all connections this connection has write access to + * @param userID + * @return the list of all connections this user has write access to */ - public List getUpdateableConnections(int userID) { + public Set getUpdateableConnections(int userID) { return getConnections(userID, MySQLConstants.CONNECTION_UPDATE); } /** * Find the list of all connections a user has permission to read. - * @param connectionID - * @return the list of all connections this connection read has access to + * @param userID + * @return the list of all connections this user has read access to */ - public List getReadableConnections(int userID) { + public Set getReadableConnections(int userID) { return getConnections(userID, MySQLConstants.CONNECTION_READ); } @@ -610,12 +683,12 @@ public class PermissionCheckUtility { * @param permissionType * @return the list of all connections this user has access to */ - private List getConnections(int userID, String permissionType) { - List affectedConnectionIDs = getConnectionIDs(userID, permissionType); + private Set getConnections(int userID, String permissionType) { + Set affectedConnectionIDs = getConnectionIDs(userID, permissionType); ConnectionExample example = new ConnectionExample(); - example.createCriteria().andConnection_idIn(affectedConnectionIDs); + example.createCriteria().andConnection_idIn(Lists.newArrayList(affectedConnectionIDs)); List connectionDBOjects = connectionDAO.selectByExample(example); - List affectedConnections = new ArrayList(); + Set affectedConnections = new HashSet(); for(Connection affectedConnection : connectionDBOjects) { MySQLConnection mySQLConnection = mySQLConnectionProvider.get(); mySQLConnection.init(affectedConnection); @@ -632,8 +705,8 @@ public class PermissionCheckUtility { * @param permissionType * @return the list of all connection IDs this user has access to */ - private List getConnectionIDs(int userID, String permissionType) { - List connectionIDs = new ArrayList(); + private Set getConnectionIDs(int userID, String permissionType) { + Set connectionIDs = new HashSet(); ConnectionPermissionExample example = new ConnectionPermissionExample(); example.createCriteria().andUser_idEqualTo(userID).andPermissionEqualTo(permissionType); List connectionPermissions = connectionPermissionDAO.selectByExample(example);