GUACAMOLE-462: Generate consistent history record UUIDs based on database record IDs.

This commit is contained in:
Michael Jumper
2021-12-02 16:01:16 -08:00
parent a5c58e221b
commit 32c7ab03ad
11 changed files with 108 additions and 5 deletions

View File

@@ -19,8 +19,9 @@
package org.apache.guacamole.auth.jdbc.base;
import java.nio.ByteBuffer;
import java.util.Date;
import java.util.UUID;
import org.apache.guacamole.net.auth.ActivityRecord;
/**
@@ -33,16 +34,72 @@ public class ModeledActivityRecord implements ActivityRecord {
*/
private final ActivityRecordModel model;
/**
* The UUID uniquely identifies this record, or null if no such unique
* identifier exists.
*/
private final UUID uuid;
/**
* Generates a UUID that uniquely identifies the record represented by the
* given ActivityRecordModel. The UUID generated is a type 3 name UUID and
* is guaranteed to be unique so long as the provided UUID namespace
* corresponds to the namespace of the record ID within the model.
* <p>
* IMPORTANT: Changing this function such that different UUIDs will be
* generated for the same records relative to past releases can potentially
* break compatibility with established history record associations. Any
* such change should be made with great care to avoid breaking history
* functionality that may be provided by third-party extensions.
*
* @param namespace
* The UUID namespace of the type 3 name UUID to generate. This
* namespace should correspond to the source of IDs for the model
* such that the combination of this namespace with the numeric record
* ID will always be unique and deterministic across all activity
* records, regardless of record type.
*
* @param model
* The model object representing the activity record.
*
* @return
* The UUID uniquely identifies the record represented by the given
* model, or null if no such unique identifier can be generated (there
* is no corresponding record ID).
*/
private static UUID getUUID(UUID namespace, ActivityRecordModel model) {
Integer id = model.getRecordID();
if (id == null)
return null;
// Convert record ID to a name UUID in the given namespace
return UUID.nameUUIDFromBytes(ByteBuffer.allocate(24)
.putLong(namespace.getMostSignificantBits())
.putLong(namespace.getLeastSignificantBits())
.putLong(id)
.array());
}
/**
* Creates a new ModeledActivityRecord backed by the given model object.
* Changes to this record will affect the backing model object, and changes
* to the backing model object will affect this record.
*
* @param namespace
* The UUID namespace of the type 3 name UUID to generate for the
* record. This namespace should correspond to the source of IDs for
* the model such that the combination of this namespace with the
* numeric record ID will always be unique and deterministic across all
* activity records, regardless of record type.
*
* @param model
* The model object to use to back this activity record.
*/
public ModeledActivityRecord(ActivityRecordModel model) {
public ModeledActivityRecord(UUID namespace, ActivityRecordModel model) {
this.model = model;
this.uuid = getUUID(namespace, model);
}
@Override
@@ -70,4 +127,9 @@ public class ModeledActivityRecord implements ActivityRecord {
return false;
}
@Override
public UUID getUUID() {
return uuid;
}
}

View File

@@ -23,6 +23,7 @@ import com.google.inject.Inject;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.jdbc.base.ActivityRecordSearchTerm;
import org.apache.guacamole.auth.jdbc.base.ActivityRecordSortPredicate;
@@ -38,6 +39,15 @@ import org.apache.guacamole.net.auth.ConnectionRecord;
*/
public class ConnectionRecordSet extends ModeledActivityRecordSet<ConnectionRecord> {
/**
* The namespace for the type 3 UUIDs generated for connection history
* records. This UUID namespace is itself a type 3 UUID within the "ns:OID"
* namespace for the OID "1.3.6.1.4.1.18060.18.2.1.2", which has been
* specifically allocated for Apache Guacamole database connection
* history records.
*/
public static final UUID UUID_NAMESPACE = UUID.fromString("8b55f070-95f4-3d31-93ee-9c5845e7aa40");
/**
* Service for managing connection objects.
*/

View File

@@ -43,7 +43,7 @@ public class ModeledConnectionRecord extends ModeledActivityRecord
* The model object to use to back this connection record.
*/
public ModeledConnectionRecord(ConnectionRecordModel model) {
super(model);
super(ConnectionRecordSet.UUID_NAMESPACE, model);
this.model = model;
}

View File

@@ -23,6 +23,7 @@ import com.google.inject.Inject;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.jdbc.base.ActivityRecordSearchTerm;
import org.apache.guacamole.auth.jdbc.base.ActivityRecordSortPredicate;
@@ -38,6 +39,15 @@ import org.apache.guacamole.net.auth.AuthenticatedUser;
*/
public class UserRecordSet extends ModeledActivityRecordSet<ActivityRecord> {
/**
* The namespace for the type 3 UUIDs generated for user history records.
* This UUID namespace is itself a type 3 UUID within the "ns:OID"
* namespace for the OID "1.3.6.1.4.1.18060.18.2.1.1", which has been
* specifically allocated for Apache Guacamole database user history
* records.
*/
public static final UUID UUID_NAMESPACE = UUID.fromString("e104741a-c949-3947-8d79-f3f2cdce7d6f");
/**
* Service for managing user objects.
*/

View File

@@ -546,7 +546,7 @@ public class UserService extends ModeledDirectoryObjectService<ModeledUser, User
* A connection record object which is backed by the given model.
*/
protected ActivityRecord getObjectInstance(ActivityRecordModel model) {
return new ModeledActivityRecord(model);
return new ModeledActivityRecord(UserRecordSet.UUID_NAMESPACE, model);
}
/**

View File

@@ -25,6 +25,7 @@
<!-- Result mapper for system permissions -->
<resultMap id="ConnectionRecordResultMap" type="org.apache.guacamole.auth.jdbc.connection.ConnectionRecordModel">
<id column="history_id" property="recordID" jdbcType="INTEGER"/>
<result column="connection_id" property="connectionIdentifier" jdbcType="INTEGER"/>
<result column="connection_name" property="connectionName" jdbcType="VARCHAR"/>
<result column="remote_host" property="remoteHost" jdbcType="VARCHAR"/>
@@ -40,6 +41,7 @@
<select id="select" resultMap="ConnectionRecordResultMap">
SELECT
guacamole_connection_history.history_id,
guacamole_connection_history.connection_id,
guacamole_connection_history.connection_name,
guacamole_connection_history.remote_host,
@@ -94,6 +96,7 @@
<select id="search" resultMap="ConnectionRecordResultMap">
SELECT
guacamole_connection_history.history_id,
guacamole_connection_history.connection_id,
guacamole_connection_history.connection_name,
guacamole_connection_history.remote_host,
@@ -159,6 +162,7 @@
<select id="searchReadable" resultMap="ConnectionRecordResultMap">
SELECT
guacamole_connection_history.history_id,
guacamole_connection_history.connection_id,
guacamole_connection_history.connection_name,
guacamole_connection_history.remote_host,

View File

@@ -37,6 +37,7 @@
<select id="select" resultMap="UserRecordResultMap">
SELECT
guacamole_user_history.history_id,
guacamole_user_history.remote_host,
guacamole_user_history.user_id,
guacamole_user_history.username,
@@ -97,6 +98,7 @@
<select id="search" resultMap="UserRecordResultMap">
SELECT
guacamole_user_history.history_id,
guacamole_user_history.remote_host,
guacamole_user_history.user_id,
guacamole_user_history.username,
@@ -153,6 +155,7 @@
<select id="searchReadable" resultMap="UserRecordResultMap">
SELECT
guacamole_user_history.history_id,
guacamole_user_history.remote_host,
guacamole_user_history.user_id,
guacamole_user_history.username,

View File

@@ -25,6 +25,7 @@
<!-- Result mapper for system permissions -->
<resultMap id="ConnectionRecordResultMap" type="org.apache.guacamole.auth.jdbc.connection.ConnectionRecordModel">
<id column="history_id" property="recordID" jdbcType="INTEGER"/>
<result column="connection_id" property="connectionIdentifier" jdbcType="INTEGER"/>
<result column="connection_name" property="connectionName" jdbcType="VARCHAR"/>
<result column="remote_host" property="remoteHost" jdbcType="VARCHAR"/>
@@ -40,6 +41,7 @@
<select id="select" resultMap="ConnectionRecordResultMap">
SELECT
guacamole_connection_history.history_id,
guacamole_connection_history.connection_id,
guacamole_connection_history.connection_name,
guacamole_connection_history.remote_host,
@@ -94,6 +96,7 @@
<select id="search" resultMap="ConnectionRecordResultMap">
SELECT
guacamole_connection_history.history_id,
guacamole_connection_history.connection_id,
guacamole_connection_history.connection_name,
guacamole_connection_history.remote_host,
@@ -157,6 +160,7 @@
<select id="searchReadable" resultMap="ConnectionRecordResultMap">
SELECT
guacamole_connection_history.history_id,
guacamole_connection_history.connection_id,
guacamole_connection_history.connection_name,
guacamole_connection_history.remote_host,

View File

@@ -37,6 +37,7 @@
<select id="select" resultMap="UserRecordResultMap">
SELECT
guacamole_user_history.history_id,
guacamole_user_history.remote_host,
guacamole_user_history.user_id,
guacamole_user_history.username,
@@ -97,6 +98,7 @@
<select id="search" resultMap="UserRecordResultMap">
SELECT
guacamole_user_history.history_id,
guacamole_user_history.remote_host,
guacamole_user_history.user_id,
guacamole_user_history.username,
@@ -153,6 +155,7 @@
<select id="searchReadable" resultMap="UserRecordResultMap">
SELECT
guacamole_user_history.history_id,
guacamole_user_history.remote_host,
guacamole_user_history.user_id,
guacamole_user_history.username,

View File

@@ -25,6 +25,7 @@
<!-- Result mapper for system permissions -->
<resultMap id="ConnectionRecordResultMap" type="org.apache.guacamole.auth.jdbc.connection.ConnectionRecordModel">
<id column="history_id" property="recordID" jdbcType="INTEGER"/>
<result column="connection_id" property="connectionIdentifier" jdbcType="INTEGER"/>
<result column="connection_name" property="connectionName" jdbcType="VARCHAR"/>
<result column="remote_host" property="remoteHost" jdbcType="VARCHAR"/>
@@ -40,6 +41,7 @@
<select id="select" resultMap="ConnectionRecordResultMap">
SELECT
[guacamole_connection_history].history_id,
[guacamole_connection_history].connection_id,
[guacamole_connection_history].connection_name,
[guacamole_connection_history].remote_host,
@@ -94,6 +96,7 @@
<select id="search" resultMap="ConnectionRecordResultMap">
SELECT TOP (#{limit,jdbcType=INTEGER})
[guacamole_connection_history].history_id,
[guacamole_connection_history].connection_id,
[guacamole_connection_history].connection_name,
[guacamole_connection_history].remote_host,
@@ -155,6 +158,7 @@
<select id="searchReadable" resultMap="ConnectionRecordResultMap">
SELECT TOP (#{limit,jdbcType=INTEGER})
[guacamole_connection_history].history_id,
[guacamole_connection_history].connection_id,
[guacamole_connection_history].connection_name,
[guacamole_connection_history].remote_host,

View File

@@ -37,6 +37,7 @@
<select id="select" resultMap="UserRecordResultMap">
SELECT
[guacamole_user_history].history_id,
[guacamole_user_history].remote_host,
[guacamole_user_history].user_id,
[guacamole_user_history].username,
@@ -97,6 +98,7 @@
<select id="search" resultMap="UserRecordResultMap">
SELECT TOP (#{limit,jdbcType=INTEGER})
[guacamole_user_history].history_id,
[guacamole_user_history].remote_host,
[guacamole_user_history].user_id,
[guacamole_user_history].username,
@@ -151,6 +153,7 @@
<select id="searchReadable" resultMap="UserRecordResultMap">
SELECT TOP (#{limit,jdbcType=INTEGER})
[guacamole_user_history].history_id,
[guacamole_user_history].remote_host,
[guacamole_user_history].user_id,
[guacamole_user_history].username,