GUAC-1101: Extract common base classes from user and connection. Add ID to connection.

This commit is contained in:
Michael Jumper
2015-02-26 22:29:34 -08:00
parent 1ac9f92206
commit 9dffabfd23
15 changed files with 265 additions and 260 deletions

View File

@@ -22,69 +22,29 @@
package net.sourceforge.guacamole.net.auth.mysql;
import net.sourceforge.guacamole.net.auth.mysql.model.ObjectModel;
import org.glyptodon.guacamole.net.auth.Identifiable;
/**
* Common interface for objects that will ultimately be made available through
* Common base class for objects that will ultimately be made available through
* the Directory class. All such objects will need the same base set of queries
* to fulfill the needs of the Directory class.
*
* @author Michael Jumper
* @param <ModelType>
* The type of object contained within the directory whose objects are
* mapped by this mapper.
* The type of model object that corresponds to this object.
*/
public interface DirectoryObject<ModelType> extends Identifiable {
public abstract class DirectoryObject<ModelType extends ObjectModel>
extends RestrictedObject<ModelType> implements Identifiable {
/**
* Initializes this object, associating it with the current authenticated
* user and populating it with data from the given model object
*
* @param currentUser
* The user that created or retrieved this object.
*
* @param model
* The backing model object.
*/
public void init(AuthenticatedUser currentUser, ModelType model);
@Override
public String getIdentifier() {
return getModel().getIdentifier();
}
/**
* Returns the user that created or queried this object. This user's
* permissions dictate what operations can be performed on or through this
* object.
*
* @return
* The user that created or queried this object.
*/
public AuthenticatedUser getCurrentUser();
@Override
public void setIdentifier(String identifier) {
getModel().setIdentifier(identifier);
}
/**
* Sets the user that created or queried this object. This user's
* permissions dictate what operations can be performed on or through this
* object.
*
* @param currentUser
* The user that created or queried this object.
*/
public void setCurrentUser(AuthenticatedUser currentUser);
/**
* Returns the backing model object. Changes to the model object will
* affect this object, and changes to this object will affect the model
* object.
*
* @return
* The user model object backing this MySQLUser.
*/
public ModelType getModel();
/**
* Sets the backing model object. This will effectively replace all data
* contained within this object.
*
* @param model
* The backing model object.
*/
public void setModel(ModelType model);
}
}

View File

@@ -39,19 +39,8 @@ import org.glyptodon.guacamole.protocol.GuacamoleConfiguration;
* A MySQL based implementation of the Connection object.
* @author James Muehlner
*/
public class MySQLConnection implements Connection, DirectoryObject<ConnectionModel> {
/**
* The user this connection belongs to. Access is based on his/her permission
* settings.
*/
private AuthenticatedUser currentUser;
/**
* The internal model object containing the values which represent this
* connection in the database.
*/
private ConnectionModel connectionModel;
public class MySQLConnection extends DirectoryObject<ConnectionModel>
implements Connection {
/**
* Service for managing connections.
@@ -82,58 +71,21 @@ public class MySQLConnection implements Connection, DirectoryObject<ConnectionMo
public MySQLConnection() {
}
@Override
public void init(AuthenticatedUser currentUser, ConnectionModel connectionModel) {
this.currentUser = currentUser;
setModel(connectionModel);
}
@Override
public AuthenticatedUser getCurrentUser() {
return currentUser;
}
@Override
public void setCurrentUser(AuthenticatedUser currentUser) {
this.currentUser = currentUser;
}
@Override
public ConnectionModel getModel() {
return connectionModel;
}
@Override
public void setModel(ConnectionModel connectionModel) {
this.connectionModel = connectionModel;
this.config = null;
}
@Override
public String getIdentifier() {
return connectionModel.getIdentifier();
}
@Override
public void setIdentifier(String identifier) {
connectionModel.setIdentifier(identifier);
}
@Override
public String getName() {
return connectionModel.getName();
return getModel().getName();
}
@Override
public void setName(String name) {
connectionModel.setName(name);
getModel().setName(name);
}
@Override
public String getParentIdentifier() {
// Translate null parent to proper identifier
String parentIdentifier = connectionModel.getParentIdentifier();
String parentIdentifier = getModel().getParentIdentifier();
if (parentIdentifier == null)
return MySQLRootConnectionGroup.IDENTIFIER;
@@ -149,7 +101,7 @@ public class MySQLConnection implements Connection, DirectoryObject<ConnectionMo
&& parentIdentifier.equals(MySQLRootConnectionGroup.IDENTIFIER))
parentIdentifier = null;
connectionModel.setParentID(parentIdentifier);
getModel().setParentIdentifier(parentIdentifier);
}
@@ -162,7 +114,7 @@ public class MySQLConnection implements Connection, DirectoryObject<ConnectionMo
// Otherwise, return permission-controlled configuration
MySQLGuacamoleConfiguration restrictedConfig = configProvider.get();
restrictedConfig.init(currentUser, connectionModel);
restrictedConfig.init(getCurrentUser(), getModel());
return restrictedConfig;
}
@@ -174,18 +126,18 @@ public class MySQLConnection implements Connection, DirectoryObject<ConnectionMo
this.config = config;
// Update model
connectionModel.setProtocol(config.getProtocol());
getModel().setProtocol(config.getProtocol());
}
@Override
public List<? extends ConnectionRecord> getHistory() throws GuacamoleException {
return connectionService.retrieveHistory(currentUser, this);
return connectionService.retrieveHistory(getCurrentUser(), this);
}
@Override
public GuacamoleSocket connect(GuacamoleClientInformation info) throws GuacamoleException {
return connectionService.connect(currentUser, this, info);
return connectionService.connect(getCurrentUser(), this, info);
}
@Override

View File

@@ -38,13 +38,7 @@ import org.glyptodon.guacamole.net.auth.simple.SimpleObjectPermissionSet;
* A MySQL based implementation of the User object.
* @author James Muehlner
*/
public class MySQLUser implements User, DirectoryObject<UserModel> {
/**
* The user this user belongs to. Access is based on his/her permission
* settings.
*/
private AuthenticatedUser currentUser;
public class MySQLUser extends DirectoryObject<UserModel> implements User {
/**
* Service for hashing passwords.
@@ -64,12 +58,6 @@ public class MySQLUser implements User, DirectoryObject<UserModel> {
@Inject
private SystemPermissionService systemPermissionService;
/**
* The internal model object containing the values which represent this
* user in the database.
*/
private UserModel userModel;
/**
* The plaintext password previously set by a call to setPassword(), if
* any. The password of a user cannot be retrieved once saved into the
@@ -85,43 +73,6 @@ public class MySQLUser implements User, DirectoryObject<UserModel> {
public MySQLUser() {
}
@Override
public void init(AuthenticatedUser currentUser, UserModel userModel) {
this.currentUser = currentUser;
setModel(userModel);
}
@Override
public AuthenticatedUser getCurrentUser() {
return currentUser;
}
@Override
public void setCurrentUser(AuthenticatedUser currentUser) {
this.currentUser = currentUser;
}
@Override
public UserModel getModel() {
return userModel;
}
@Override
public void setModel(UserModel userModel) {
this.userModel = userModel;
this.password = null;
}
@Override
public String getIdentifier() {
return userModel.getUsername();
}
@Override
public void setIdentifier(String username) {
userModel.setUsername(username);
}
@Override
public String getPassword() {
return password;
@@ -130,6 +81,8 @@ public class MySQLUser implements User, DirectoryObject<UserModel> {
@Override
public void setPassword(String password) {
UserModel userModel = getModel();
// Store plaintext password internally
this.password = password;

View File

@@ -0,0 +1,109 @@
/*
* Copyright (C) 2015 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package net.sourceforge.guacamole.net.auth.mysql;
/**
* Common base class for objects that are associated with the users that
* query them, and have an underlying model.
*
* @author Michael Jumper
* @param <ModelType>
* The type of model object which corresponds to this object.
*/
public abstract class RestrictedObject<ModelType> {
/**
* The user this object belongs to. Access is based on his/her permission
* settings.
*/
private AuthenticatedUser currentUser;
/**
* The internal model object containing the values which represent this
* object in the database.
*/
private ModelType model;
/**
* Initializes this object, associating it with the current authenticated
* user and populating it with data from the given model object
*
* @param currentUser
* The user that created or retrieved this object.
*
* @param model
* The backing model object.
*/
public void init(AuthenticatedUser currentUser, ModelType model) {
setCurrentUser(currentUser);
setModel(model);
}
/**
* Returns the user that created or queried this object. This user's
* permissions dictate what operations can be performed on or through this
* object.
*
* @return
* The user that created or queried this object.
*/
public AuthenticatedUser getCurrentUser() {
return currentUser;
}
/**
* Sets the user that created or queried this object. This user's
* permissions dictate what operations can be performed on or through this
* object.
*
* @param currentUser
* The user that created or queried this object.
*/
public void setCurrentUser(AuthenticatedUser currentUser) {
this.currentUser = currentUser;
}
/**
* Returns the backing model object. Changes to the model object will
* affect this object, and changes to this object will affect the model
* object.
*
* @return
* The backing model object.
*/
public ModelType getModel() {
return model;
}
/**
* Sets the backing model object. This will effectively replace all data
* contained within this object.
*
* @param model
* The backing model object.
*/
public void setModel(ModelType model) {
this.model = model;
}
}

View File

@@ -28,12 +28,7 @@ package net.sourceforge.guacamole.net.auth.mysql.model;
*
* @author Michael Jumper
*/
public class ConnectionModel {
/**
* The identifier of this connection in the database, if any.
*/
private String identifier;
public class ConnectionModel extends ObjectModel {
/**
* The identifier of the parent connection group in the database, or null
@@ -117,29 +112,26 @@ public class ConnectionModel {
* The identifier of the parent connection group, or null if the parent
* connection group is the root connection group.
*/
public void setParentID(String parentIdentifier) {
public void setParentIdentifier(String parentIdentifier) {
this.parentIdentifier = parentIdentifier;
}
/**
* Returns the identifier of this connection in the database, if it exists.
*
* @return
* The identifier of this connection in the database, or null if this
* connection was not retrieved from the database.
*/
@Override
public String getIdentifier() {
return identifier;
// If no associated ID, then no associated identifier
Integer id = getObjectID();
if (id == null)
return null;
// Otherwise, the identifier is the ID as a string
return id.toString();
}
/**
* Sets the identifier of this connection to the given value.
*
* @param identifier
* The identifier to assign to this connection.
*/
@Override
public void setIdentifier(String identifier) {
this.identifier = identifier;
throw new UnsupportedOperationException("Connection identifiers are derived from IDs. They cannot be set.");
}
}

View File

@@ -0,0 +1,90 @@
/*
* Copyright (C) 2015 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package net.sourceforge.guacamole.net.auth.mysql.model;
/**
* Object representation of a Guacamole object, such as a user or connection,
* as represented in the database.
*
* @author Michael Jumper
*/
public abstract class ObjectModel {
/**
* The ID of this object in the database, if any.
*/
private Integer objectID;
/**
* The unique identifier which identifies this object.
*/
private String identifier;
/**
* Creates a new, empty object.
*/
public ObjectModel() {
}
/**
* Returns the identifier that uniquely identifies this object.
*
* @return
* The identifier that uniquely identifies this object.
*/
public String getIdentifier() {
return identifier;
}
/**
* Sets the identifier that uniquely identifies this object.
*
* @param identifier
* The identifier that uniquely identifies this object.
*/
public void setIdentifier(String identifier) {
this.identifier = identifier;
}
/**
* Returns the ID of this object in the database, if it exists.
*
* @return
* The ID of this object in the database, or null if this object was
* not retrieved from the database.
*/
public Integer getObjectID() {
return objectID;
}
/**
* Sets the ID of this object to the given value.
*
* @param objectID
* The ID to assign to this object.
*/
public void setObjectID(Integer objectID) {
this.objectID = objectID;
}
}

View File

@@ -27,18 +27,8 @@ package net.sourceforge.guacamole.net.auth.mysql.model;
*
* @author Michael Jumper
*/
public class UserModel {
public class UserModel extends ObjectModel {
/**
* The ID of this user in the database, if any.
*/
private Integer userID;
/**
* The unique username which identifies this user.
*/
private String username;
/**
* The SHA-256 hash of the password and salt.
*/
@@ -56,47 +46,6 @@ public class UserModel {
public UserModel() {
}
/**
* Returns the username that uniquely identifies this user.
*
* @return
* The username that uniquely identifies this user.
*/
public String getUsername() {
return username;
}
/**
* Sets the username that uniquely identifies this user.
*
* @param username
* The username that uniquely identifies this user.
*/
public void setUsername(String username) {
this.username = username;
}
/**
* Returns the ID of this user in the database, if it exists.
*
* @return
* The ID of this user in the database, or null if this user was not
* retrieved from the database.
*/
public Integer getUserID() {
return userID;
}
/**
* Sets the ID of this user to the given value.
*
* @param userID
* The ID to assign to this user.
*/
public void setUserID(Integer userID) {
this.userID = userID;
}
/**
* Returns the hash of this user's password and password salt. This may be
* null if the user was not retrieved from the database, and setPassword()

View File

@@ -225,8 +225,8 @@ public abstract class AbstractGuacamoleSocketService implements GuacamoleSocketS
ConnectionRecordModel recordModel = new ConnectionRecordModel();
// Copy user information and timestamps into new record
recordModel.setUserID(userModel.getUserID());
recordModel.setUsername(userModel.getUsername());
recordModel.setUserID(userModel.getObjectID());
recordModel.setUsername(userModel.getIdentifier());
recordModel.setConnectionIdentifier(connection.getIdentifier());
recordModel.setStartDate(activeConnection.getStartDate());
recordModel.setEndDate(new Date());

View File

@@ -112,7 +112,6 @@ public class ConnectionService extends DirectoryObjectService<MySQLConnection, C
MySQLConnection connection = getObjectInstance(currentUser, model);
// Set model contents through MySQLConnection, copying the provided connection
connection.setIdentifier(object.getIdentifier());
connection.setParentIdentifier(object.getParentIdentifier());
connection.setName(object.getName());
connection.setConfiguration(object.getConfiguration());
@@ -145,7 +144,7 @@ public class ConnectionService extends DirectoryObjectService<MySQLConnection, C
throws GuacamoleException {
// Name must not be blank
if (object.getIdentifier().trim().isEmpty())
if (object.getName().trim().isEmpty())
throw new GuacamoleClientException("Connection names must not be blank.");
// FIXME: Do not attempt to create duplicate connections
@@ -157,7 +156,7 @@ public class ConnectionService extends DirectoryObjectService<MySQLConnection, C
MySQLConnection object) throws GuacamoleException {
// Name must not be blank
if (object.getIdentifier().trim().isEmpty())
if (object.getName().trim().isEmpty())
throw new GuacamoleClientException("Connection names must not be blank.");
// FIXME: Check whether such a connection is already present

View File

@@ -29,6 +29,7 @@ import java.util.Set;
import net.sourceforge.guacamole.net.auth.mysql.AuthenticatedUser;
import net.sourceforge.guacamole.net.auth.mysql.DirectoryObject;
import net.sourceforge.guacamole.net.auth.mysql.dao.DirectoryObjectMapper;
import net.sourceforge.guacamole.net.auth.mysql.model.ObjectModel;
import org.glyptodon.guacamole.GuacamoleException;
import org.glyptodon.guacamole.GuacamoleSecurityException;
import org.glyptodon.guacamole.net.auth.permission.ObjectPermission;
@@ -53,7 +54,7 @@ import org.glyptodon.guacamole.net.auth.permission.ObjectPermissionSet;
* database.
*/
public abstract class DirectoryObjectService<InternalType extends DirectoryObject<ModelType>,
ExternalType, ModelType> {
ExternalType, ModelType extends ObjectModel> {
/**
* Returns an instance of a mapper for the type of object used by this

View File

@@ -73,8 +73,8 @@ public class SystemPermissionService
SystemPermissionModel model = new SystemPermissionModel();
// Populate model object with data from user and permission
model.setUserID(targetUser.getModel().getUserID());
model.setUsername(targetUser.getModel().getUsername());
model.setUserID(targetUser.getModel().getObjectID());
model.setUsername(targetUser.getModel().getIdentifier());
model.setType(permission.getType());
return model;

View File

@@ -138,7 +138,7 @@ public class UserService extends DirectoryObjectService<MySQLUser, User, UserMod
UserModel updatedModel = object.getModel();
// Do not rename to existing user
if (!existingModel.getUserID().equals(updatedModel.getUserID()))
if (!existingModel.getObjectID().equals(updatedModel.getObjectID()))
throw new GuacamoleClientException("User \"" + object.getIdentifier() + "\" already exists.");
}

View File

@@ -28,7 +28,7 @@
<!-- Result mapper for connection objects -->
<resultMap id="ConnectionResultMap" type="net.sourceforge.guacamole.net.auth.mysql.model.ConnectionModel" >
<id column="connection_id" property="identifier" jdbcType="INTEGER"/>
<id column="connection_id" property="objectID" jdbcType="INTEGER"/>
<result column="connection_name" property="name" jdbcType="VARCHAR"/>
<result column="parent_id" property="parentIdentifier" jdbcType="INTEGER"/>
<result column="protocol" property="protocol" jdbcType="VARCHAR"/>
@@ -45,7 +45,7 @@
SELECT connection_id
FROM guacamole_connection_permission
WHERE
user_id = #{user.userID,jdbcType=INTEGER}
user_id = #{user.objectID,jdbcType=INTEGER}
AND permission = 'READ'
</select>
@@ -66,7 +66,7 @@
WHERE
<if test="parentIdentifier != null">parent_id = #{parentIdentifier,jdbcType=VARCHAR}</if>
<if test="parentIdentifier == null">parent_id IS NULL</if>
AND user_id = #{user.userID,jdbcType=INTEGER}
AND user_id = #{user.objectID,jdbcType=INTEGER}
AND permission = 'READ'
</select>
@@ -102,7 +102,7 @@
open="(" separator="," close=")">
#{identifier,jdbcType=VARCHAR}
</foreach>
AND user_id = #{user.userID,jdbcType=INTEGER}
AND user_id = #{user.objectID,jdbcType=INTEGER}
AND permission = 'READ'
</select>
@@ -127,7 +127,7 @@
#{object.protocol,jdbcType=VARCHAR}
)
<selectKey resultType="java.lang.String" keyProperty="identifier" order="AFTER">
<selectKey resultType="java.lang.String" keyProperty="objectID" order="AFTER">
SELECT LAST_INSERT_ID()
</selectKey>
@@ -139,7 +139,7 @@
SET connection_name = #{object.name,jdbcType=VARCHAR},
parent_id = #{object.parentIdentifier,jdbcType=VARCHAR},
protocol = #{object.protocol,jdbcType=VARCHAR}
WHERE connection_id = #{object.identifier,jdbcType=VARCHAR}
WHERE connection_id = #{object.objectID,jdbcType=INTEGER}
</update>
</mapper>

View File

@@ -43,7 +43,7 @@
permission
FROM guacamole_system_permission
JOIN guacamole_user ON guacamole_system_permission.user_id = guacamole_user.user_id
WHERE guacamole_system_permission.user_id = #{user.userID,jdbcType=INTEGER}
WHERE guacamole_system_permission.user_id = #{user.objectID,jdbcType=INTEGER}
</select>
@@ -57,7 +57,7 @@
FROM guacamole_system_permission
JOIN guacamole_user ON guacamole_system_permission.user_id = guacamole_user.user_id
WHERE
guacamole_system_permission.user_id = #{user.userID,jdbcType=INTEGER}
guacamole_system_permission.user_id = #{user.objectID,jdbcType=INTEGER}
AND permission = #{type,jdbcType=VARCHAR}
</select>

View File

@@ -28,8 +28,8 @@
<!-- Result mapper for user objects -->
<resultMap id="UserResultMap" type="net.sourceforge.guacamole.net.auth.mysql.model.UserModel" >
<id column="user_id" property="userID" jdbcType="INTEGER"/>
<result column="username" property="username" jdbcType="VARCHAR"/>
<id column="user_id" property="objectID" jdbcType="INTEGER"/>
<result column="username" property="identifier" jdbcType="VARCHAR"/>
<result column="password_hash" property="passwordHash" jdbcType="BINARY"/>
<result column="password_salt" property="passwordSalt" jdbcType="BINARY"/>
</resultMap>
@@ -46,7 +46,7 @@
FROM guacamole_user
JOIN guacamole_user_permission ON affected_user_id = guacamole_user.user_id
WHERE
guacamole_user_permission.user_id = #{user.userID,jdbcType=INTEGER}
guacamole_user_permission.user_id = #{user.objectID,jdbcType=INTEGER}
AND permission = 'read'
</select>
@@ -82,7 +82,7 @@
open="(" separator="," close=")">
#{identifier,jdbcType=VARCHAR}
</foreach>
AND guacamole_user_permission.user_id = #{user.userID,jdbcType=INTEGER}
AND guacamole_user_permission.user_id = #{user.objectID,jdbcType=INTEGER}
AND permission = 'read'
</select>
@@ -115,12 +115,12 @@
password_salt
)
VALUES (
#{object.username,jdbcType=VARCHAR},
#{object.identifier,jdbcType=VARCHAR},
#{object.passwordHash,jdbcType=BINARY},
#{object.passwordSalt,jdbcType=BINARY}
)
<selectKey resultType="java.lang.Integer" keyProperty="userID" order="AFTER">
<selectKey resultType="java.lang.Integer" keyProperty="objectID" order="AFTER">
SELECT LAST_INSERT_ID()
</selectKey>
@@ -131,7 +131,7 @@
UPDATE guacamole_user
SET password_hash = #{object.passwordHash,jdbcType=BINARY},
password_salt = #{object.passwordSalt,jdbcType=BINARY}
WHERE user_id = #{object.userID,jdbcType=VARCHAR}
WHERE user_id = #{object.objectID,jdbcType=VARCHAR}
</update>
</mapper>