mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-07 05:31:22 +00:00
GUACAMOLE-462: Merge add support for associating connection history with logs/recordings.
This commit is contained in:
@@ -0,0 +1,147 @@
|
|||||||
|
/*
|
||||||
|
* 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.jdbc.base;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import org.apache.guacamole.auth.jdbc.user.UserModel;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Common interface for mapping activity records.
|
||||||
|
*
|
||||||
|
* @param <ModelType>
|
||||||
|
* The type of model object representing the activity records mapped by
|
||||||
|
* this mapper.
|
||||||
|
*/
|
||||||
|
public interface ActivityRecordMapper<ModelType> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inserts the given activity record.
|
||||||
|
*
|
||||||
|
* @param record
|
||||||
|
* The activity record to insert.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* The number of rows inserted.
|
||||||
|
*/
|
||||||
|
int insert(@Param("record") ModelType record);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the given activity record in the database, assigning an end
|
||||||
|
* date. No column of the existing activity record is updated except for
|
||||||
|
* the end date. If the record does not actually exist, this operation has
|
||||||
|
* no effect.
|
||||||
|
*
|
||||||
|
* @param record
|
||||||
|
* The activity record to update.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* The number of rows updated.
|
||||||
|
*/
|
||||||
|
int updateEndDate(@Param("record") ModelType record);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Searches for up to <code>limit</code> activity records that contain
|
||||||
|
* the given terms, sorted by the given predicates, regardless of whether
|
||||||
|
* the data they are associated with is readable by any particular user.
|
||||||
|
* This should only be called on behalf of a system administrator. If
|
||||||
|
* records are needed by a non-administrative user who must have explicit
|
||||||
|
* read rights, use {@link searchReadable()} instead.
|
||||||
|
*
|
||||||
|
* @param identifier
|
||||||
|
* The optional identifier of the object whose history is being
|
||||||
|
* retrieved, or null if records related to any such object should be
|
||||||
|
* retrieved.
|
||||||
|
*
|
||||||
|
* @param recordIdentifier
|
||||||
|
* The identifier of the specific history record to retrieve, if not
|
||||||
|
* all matching records. Search terms, etc. will still be applied to
|
||||||
|
* the single record.
|
||||||
|
*
|
||||||
|
* @param terms
|
||||||
|
* The search terms that must match the returned records.
|
||||||
|
*
|
||||||
|
* @param sortPredicates
|
||||||
|
* A list of predicates to sort the returned records by, in order of
|
||||||
|
* priority.
|
||||||
|
*
|
||||||
|
* @param limit
|
||||||
|
* The maximum number of records that should be returned.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* The results of the search performed with the given parameters.
|
||||||
|
*/
|
||||||
|
List<ModelType> search(@Param("identifier") String identifier,
|
||||||
|
@Param("recordIdentifier") String recordIdentifier,
|
||||||
|
@Param("terms") Collection<ActivityRecordSearchTerm> terms,
|
||||||
|
@Param("sortPredicates") List<ActivityRecordSortPredicate> sortPredicates,
|
||||||
|
@Param("limit") int limit);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Searches for up to <code>limit</code> activity records that contain
|
||||||
|
* the given terms, sorted by the given predicates. Only records that are
|
||||||
|
* associated with data explicitly readable by the given user will be
|
||||||
|
* returned. If records are needed by a system administrator (who, by
|
||||||
|
* definition, does not need explicit read rights), use {@link search()}
|
||||||
|
* instead.
|
||||||
|
*
|
||||||
|
* @param identifier
|
||||||
|
* The optional identifier of the object whose history is being
|
||||||
|
* retrieved, or null if records related to any such object should be
|
||||||
|
* retrieved.
|
||||||
|
*
|
||||||
|
* @param user
|
||||||
|
* The user whose permissions should determine whether a record is
|
||||||
|
* returned.
|
||||||
|
*
|
||||||
|
* @param recordIdentifier
|
||||||
|
* The identifier of the specific history record to retrieve, if not
|
||||||
|
* all matching records. Search terms, etc. will still be applied to
|
||||||
|
* the single record.
|
||||||
|
*
|
||||||
|
* @param terms
|
||||||
|
* The search terms that must match the returned records.
|
||||||
|
*
|
||||||
|
* @param sortPredicates
|
||||||
|
* A list of predicates to sort the returned records by, in order of
|
||||||
|
* priority.
|
||||||
|
*
|
||||||
|
* @param limit
|
||||||
|
* The maximum number of records that should be returned.
|
||||||
|
*
|
||||||
|
* @param effectiveGroups
|
||||||
|
* The identifiers of all groups that should be taken into account
|
||||||
|
* when determining the permissions effectively granted to the user. If
|
||||||
|
* no groups are given, only permissions directly granted to the user
|
||||||
|
* will be used.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* The results of the search performed with the given parameters.
|
||||||
|
*/
|
||||||
|
List<ModelType> searchReadable(@Param("identifier") String identifier,
|
||||||
|
@Param("user") UserModel user,
|
||||||
|
@Param("recordIdentifier") String recordIdentifier,
|
||||||
|
@Param("terms") Collection<ActivityRecordSearchTerm> terms,
|
||||||
|
@Param("sortPredicates") List<ActivityRecordSortPredicate> sortPredicates,
|
||||||
|
@Param("limit") int limit,
|
||||||
|
@Param("effectiveGroups") Collection<String> effectiveGroups);
|
||||||
|
|
||||||
|
}
|
@@ -19,8 +19,9 @@
|
|||||||
|
|
||||||
package org.apache.guacamole.auth.jdbc.base;
|
package org.apache.guacamole.auth.jdbc.base;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.UUID;
|
||||||
import org.apache.guacamole.net.auth.ActivityRecord;
|
import org.apache.guacamole.net.auth.ActivityRecord;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -33,16 +34,45 @@ public class ModeledActivityRecord implements ActivityRecord {
|
|||||||
*/
|
*/
|
||||||
private final ActivityRecordModel model;
|
private final ActivityRecordModel model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
private final UUID namespace;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new ModeledActivityRecord backed by the given model object.
|
* Creates a new ModeledActivityRecord backed by the given model object.
|
||||||
* Changes to this record will affect the backing model object, and changes
|
* Changes to this record will affect the backing model object, and changes
|
||||||
* to the backing model object will affect this record.
|
* 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
|
* @param model
|
||||||
* The model object to use to back this activity record.
|
* The model object to use to back this activity record.
|
||||||
*/
|
*/
|
||||||
public ModeledActivityRecord(ActivityRecordModel model) {
|
public ModeledActivityRecord(UUID namespace, ActivityRecordModel model) {
|
||||||
this.model = model;
|
this.model = model;
|
||||||
|
this.namespace = namespace;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the backing model object. Changes to this record will affect the
|
||||||
|
* backing model object, and changes to the backing model object will
|
||||||
|
* affect this record.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* The backing model object.
|
||||||
|
*/
|
||||||
|
public ActivityRecordModel getModel() {
|
||||||
|
return model;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -70,4 +100,31 @@ public class ModeledActivityRecord implements ActivityRecord {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getIdentifier() {
|
||||||
|
|
||||||
|
Integer id = model.getRecordID();
|
||||||
|
if (id == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return id.toString();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UUID getUUID() {
|
||||||
|
|
||||||
|
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());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -29,6 +29,8 @@ import org.apache.guacamole.net.auth.ActivityRecord;
|
|||||||
import org.apache.guacamole.net.auth.ActivityRecordSet;
|
import org.apache.guacamole.net.auth.ActivityRecordSet;
|
||||||
import org.apache.guacamole.net.auth.ActivityRecordSet.SortableProperty;
|
import org.apache.guacamole.net.auth.ActivityRecordSet.SortableProperty;
|
||||||
import org.apache.guacamole.net.auth.AuthenticatedUser;
|
import org.apache.guacamole.net.auth.AuthenticatedUser;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A JDBC implementation of ActivityRecordSet. Calls to asCollection() will
|
* A JDBC implementation of ActivityRecordSet. Calls to asCollection() will
|
||||||
@@ -41,6 +43,11 @@ import org.apache.guacamole.net.auth.AuthenticatedUser;
|
|||||||
public abstract class ModeledActivityRecordSet<RecordType extends ActivityRecord>
|
public abstract class ModeledActivityRecordSet<RecordType extends ActivityRecord>
|
||||||
extends RestrictedObject implements ActivityRecordSet<RecordType> {
|
extends RestrictedObject implements ActivityRecordSet<RecordType> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logger for this class.
|
||||||
|
*/
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(ModeledActivityRecordSet.class);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The set of strings that each must occur somewhere within the returned
|
* The set of strings that each must occur somewhere within the returned
|
||||||
* records, whether within the associated username, an associated date, or
|
* records, whether within the associated username, an associated date, or
|
||||||
@@ -73,6 +80,11 @@ public abstract class ModeledActivityRecordSet<RecordType extends ActivityRecord
|
|||||||
* @param user
|
* @param user
|
||||||
* The user retrieving the history.
|
* The user retrieving the history.
|
||||||
*
|
*
|
||||||
|
* @param recordIdentifier
|
||||||
|
* The identifier of the specific history record to retrieve, if not
|
||||||
|
* all matching records. Search terms, etc. will still be applied to
|
||||||
|
* the single record.
|
||||||
|
*
|
||||||
* @param requiredContents
|
* @param requiredContents
|
||||||
* The search terms that must be contained somewhere within each of the
|
* The search terms that must be contained somewhere within each of the
|
||||||
* returned records.
|
* returned records.
|
||||||
@@ -90,16 +102,35 @@ public abstract class ModeledActivityRecordSet<RecordType extends ActivityRecord
|
|||||||
* @throws GuacamoleException
|
* @throws GuacamoleException
|
||||||
* If permission to read the history records is denied.
|
* If permission to read the history records is denied.
|
||||||
*/
|
*/
|
||||||
protected abstract Collection<RecordType> retrieveHistory(
|
protected abstract List<RecordType> retrieveHistory(
|
||||||
AuthenticatedUser user,
|
AuthenticatedUser user, String recordIdentifier,
|
||||||
Set<ActivityRecordSearchTerm> requiredContents,
|
Set<ActivityRecordSearchTerm> requiredContents,
|
||||||
List<ActivityRecordSortPredicate> sortPredicates,
|
List<ActivityRecordSortPredicate> sortPredicates,
|
||||||
int limit) throws GuacamoleException;
|
int limit) throws GuacamoleException;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RecordType get(String identifier) throws GuacamoleException {
|
||||||
|
|
||||||
|
List<RecordType> records = retrieveHistory(getCurrentUser(),
|
||||||
|
identifier, requiredContents, sortPredicates, limit);
|
||||||
|
|
||||||
|
if (records.isEmpty())
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if (records.size() == 1)
|
||||||
|
return records.get(0);
|
||||||
|
|
||||||
|
logger.warn("Multiple history records match ID \"{}\"! This should "
|
||||||
|
+ "not be possible and may indicate a bug or database "
|
||||||
|
+ "corruption.", identifier);
|
||||||
|
return null;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<RecordType> asCollection()
|
public Collection<RecordType> asCollection()
|
||||||
throws GuacamoleException {
|
throws GuacamoleException {
|
||||||
return retrieveHistory(getCurrentUser(), requiredContents,
|
return retrieveHistory(getCurrentUser(), null, requiredContents,
|
||||||
sortPredicates, limit);
|
sortPredicates, limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -19,113 +19,9 @@
|
|||||||
|
|
||||||
package org.apache.guacamole.auth.jdbc.connection;
|
package org.apache.guacamole.auth.jdbc.connection;
|
||||||
|
|
||||||
import java.util.Collection;
|
import org.apache.guacamole.auth.jdbc.base.ActivityRecordMapper;
|
||||||
import java.util.List;
|
|
||||||
import org.apache.guacamole.auth.jdbc.base.ActivityRecordSearchTerm;
|
|
||||||
import org.apache.guacamole.auth.jdbc.base.ActivityRecordSortPredicate;
|
|
||||||
import org.apache.ibatis.annotations.Param;
|
|
||||||
import org.apache.guacamole.auth.jdbc.user.UserModel;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mapper for connection record objects.
|
* Mapper for connection record objects.
|
||||||
*/
|
*/
|
||||||
public interface ConnectionRecordMapper {
|
public interface ConnectionRecordMapper extends ActivityRecordMapper<ConnectionRecordModel> {}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a collection of all connection records associated with the
|
|
||||||
* connection having the given identifier.
|
|
||||||
*
|
|
||||||
* @param identifier
|
|
||||||
* The identifier of the connection whose records are to be retrieved.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* A collection of all connection records associated with the
|
|
||||||
* connection having the given identifier. This collection will be
|
|
||||||
* empty if no such connection exists.
|
|
||||||
*/
|
|
||||||
List<ConnectionRecordModel> select(@Param("identifier") String identifier);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inserts the given connection record.
|
|
||||||
*
|
|
||||||
* @param record
|
|
||||||
* The connection record to insert.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* The number of rows inserted.
|
|
||||||
*/
|
|
||||||
int insert(@Param("record") ConnectionRecordModel record);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Searches for up to <code>limit</code> connection records that contain
|
|
||||||
* the given terms, sorted by the given predicates, regardless of whether
|
|
||||||
* the data they are associated with is is readable by any particular user.
|
|
||||||
* This should only be called on behalf of a system administrator. If
|
|
||||||
* records are needed by a non-administrative user who must have explicit
|
|
||||||
* read rights, use {@link searchReadable()} instead.
|
|
||||||
*
|
|
||||||
* @param identifier
|
|
||||||
* The optional connection identifier to which records should be limited,
|
|
||||||
* or null if all records should be retrieved.
|
|
||||||
*
|
|
||||||
* @param terms
|
|
||||||
* The search terms that must match the returned records.
|
|
||||||
*
|
|
||||||
* @param sortPredicates
|
|
||||||
* A list of predicates to sort the returned records by, in order of
|
|
||||||
* priority.
|
|
||||||
*
|
|
||||||
* @param limit
|
|
||||||
* The maximum number of records that should be returned.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* The results of the search performed with the given parameters.
|
|
||||||
*/
|
|
||||||
List<ConnectionRecordModel> search(@Param("identifier") String identifier,
|
|
||||||
@Param("terms") Collection<ActivityRecordSearchTerm> terms,
|
|
||||||
@Param("sortPredicates") List<ActivityRecordSortPredicate> sortPredicates,
|
|
||||||
@Param("limit") int limit);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Searches for up to <code>limit</code> connection records that contain
|
|
||||||
* the given terms, sorted by the given predicates. Only records that are
|
|
||||||
* associated with data explicitly readable by the given user will be
|
|
||||||
* returned. If records are needed by a system administrator (who, by
|
|
||||||
* definition, does not need explicit read rights), use {@link search()}
|
|
||||||
* instead.
|
|
||||||
*
|
|
||||||
* @param identifier
|
|
||||||
* The optional connection identifier for which records should be
|
|
||||||
* retrieved, or null if all readable records should be retrieved.
|
|
||||||
*
|
|
||||||
* @param user
|
|
||||||
* The user whose permissions should determine whether a record is
|
|
||||||
* returned.
|
|
||||||
*
|
|
||||||
* @param terms
|
|
||||||
* The search terms that must match the returned records.
|
|
||||||
*
|
|
||||||
* @param sortPredicates
|
|
||||||
* A list of predicates to sort the returned records by, in order of
|
|
||||||
* priority.
|
|
||||||
*
|
|
||||||
* @param limit
|
|
||||||
* The maximum number of records that should be returned.
|
|
||||||
*
|
|
||||||
* @param effectiveGroups
|
|
||||||
* The identifiers of all groups that should be taken into account
|
|
||||||
* when determining the permissions effectively granted to the user. If
|
|
||||||
* no groups are given, only permissions directly granted to the user
|
|
||||||
* will be used.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* The results of the search performed with the given parameters.
|
|
||||||
*/
|
|
||||||
List<ConnectionRecordModel> searchReadable(@Param("identifier") String identifier,
|
|
||||||
@Param("user") UserModel user,
|
|
||||||
@Param("terms") Collection<ActivityRecordSearchTerm> terms,
|
|
||||||
@Param("sortPredicates") List<ActivityRecordSortPredicate> sortPredicates,
|
|
||||||
@Param("limit") int limit,
|
|
||||||
@Param("effectiveGroups") Collection<String> effectiveGroups);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
@@ -20,9 +20,9 @@
|
|||||||
package org.apache.guacamole.auth.jdbc.connection;
|
package org.apache.guacamole.auth.jdbc.connection;
|
||||||
|
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
import org.apache.guacamole.GuacamoleException;
|
import org.apache.guacamole.GuacamoleException;
|
||||||
import org.apache.guacamole.auth.jdbc.base.ActivityRecordSearchTerm;
|
import org.apache.guacamole.auth.jdbc.base.ActivityRecordSearchTerm;
|
||||||
import org.apache.guacamole.auth.jdbc.base.ActivityRecordSortPredicate;
|
import org.apache.guacamole.auth.jdbc.base.ActivityRecordSortPredicate;
|
||||||
@@ -38,6 +38,15 @@ import org.apache.guacamole.net.auth.ConnectionRecord;
|
|||||||
*/
|
*/
|
||||||
public class ConnectionRecordSet extends ModeledActivityRecordSet<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.
|
* Service for managing connection objects.
|
||||||
*/
|
*/
|
||||||
@@ -69,14 +78,15 @@ public class ConnectionRecordSet extends ModeledActivityRecordSet<ConnectionReco
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Collection<ConnectionRecord> retrieveHistory(
|
protected List<ConnectionRecord> retrieveHistory(
|
||||||
AuthenticatedUser user, Set<ActivityRecordSearchTerm> requiredContents,
|
AuthenticatedUser user, String recordIdentifier,
|
||||||
List<ActivityRecordSortPredicate> sortPredicates, int limit)
|
Set<ActivityRecordSearchTerm> requiredContents,
|
||||||
throws GuacamoleException {
|
List<ActivityRecordSortPredicate> sortPredicates,
|
||||||
|
int limit) throws GuacamoleException {
|
||||||
|
|
||||||
// Retrieve history from database
|
// Retrieve history from database
|
||||||
return connectionService.retrieveHistory(identifier, getCurrentUser(),
|
return connectionService.retrieveHistory(identifier, getCurrentUser(),
|
||||||
requiredContents, sortPredicates, limit);
|
recordIdentifier, requiredContents, sortPredicates, limit);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -390,40 +390,6 @@ public class ConnectionService extends ModeledChildDirectoryObjectService<Modele
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves the connection history of the given connection, including any
|
|
||||||
* active connections.
|
|
||||||
*
|
|
||||||
* @param user
|
|
||||||
* The user retrieving the connection history.
|
|
||||||
*
|
|
||||||
* @param connection
|
|
||||||
* The connection whose history is being retrieved.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* The connection history of the given connection, including any
|
|
||||||
* active connections.
|
|
||||||
*
|
|
||||||
* @throws GuacamoleException
|
|
||||||
* If permission to read the connection history is denied.
|
|
||||||
*/
|
|
||||||
public List<ConnectionRecord> retrieveHistory(ModeledAuthenticatedUser user,
|
|
||||||
ModeledConnection connection) throws GuacamoleException {
|
|
||||||
|
|
||||||
String identifier = connection.getIdentifier();
|
|
||||||
|
|
||||||
// Get current active connections.
|
|
||||||
List<ConnectionRecord> records = new ArrayList<>(tunnelService.getActiveConnections(connection));
|
|
||||||
Collections.reverse(records);
|
|
||||||
|
|
||||||
// Add in the history records.
|
|
||||||
records.addAll(retrieveHistory(identifier, user, Collections.emptyList(),
|
|
||||||
Collections.emptyList(), Integer.MAX_VALUE));
|
|
||||||
|
|
||||||
return records;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the connection history records matching the given criteria.
|
* Retrieves the connection history records matching the given criteria.
|
||||||
* Retrieves up to <code>limit</code> connection history records matching
|
* Retrieves up to <code>limit</code> connection history records matching
|
||||||
@@ -437,6 +403,11 @@ public class ConnectionService extends ModeledChildDirectoryObjectService<Modele
|
|||||||
* @param user
|
* @param user
|
||||||
* The user retrieving the connection history.
|
* The user retrieving the connection history.
|
||||||
*
|
*
|
||||||
|
* @param recordIdentifier
|
||||||
|
* The identifier of the specific history record to retrieve, if not
|
||||||
|
* all matching records. Search terms, etc. will still be applied to
|
||||||
|
* the single record.
|
||||||
|
*
|
||||||
* @param requiredContents
|
* @param requiredContents
|
||||||
* The search terms that must be contained somewhere within each of the
|
* The search terms that must be contained somewhere within each of the
|
||||||
* returned records.
|
* returned records.
|
||||||
@@ -456,7 +427,7 @@ public class ConnectionService extends ModeledChildDirectoryObjectService<Modele
|
|||||||
* If permission to read the connection history is denied.
|
* If permission to read the connection history is denied.
|
||||||
*/
|
*/
|
||||||
public List<ConnectionRecord> retrieveHistory(String identifier,
|
public List<ConnectionRecord> retrieveHistory(String identifier,
|
||||||
ModeledAuthenticatedUser user,
|
ModeledAuthenticatedUser user, String recordIdentifier,
|
||||||
Collection<ActivityRecordSearchTerm> requiredContents,
|
Collection<ActivityRecordSearchTerm> requiredContents,
|
||||||
List<ActivityRecordSortPredicate> sortPredicates, int limit)
|
List<ActivityRecordSortPredicate> sortPredicates, int limit)
|
||||||
throws GuacamoleException {
|
throws GuacamoleException {
|
||||||
@@ -465,55 +436,20 @@ public class ConnectionService extends ModeledChildDirectoryObjectService<Modele
|
|||||||
|
|
||||||
// Bypass permission checks if the user is privileged
|
// Bypass permission checks if the user is privileged
|
||||||
if (user.isPrivileged())
|
if (user.isPrivileged())
|
||||||
searchResults = connectionRecordMapper.search(identifier, requiredContents,
|
searchResults = connectionRecordMapper.search(identifier,
|
||||||
sortPredicates, limit);
|
recordIdentifier, requiredContents, sortPredicates, limit);
|
||||||
|
|
||||||
// Otherwise only return explicitly readable history records
|
// Otherwise only return explicitly readable history records
|
||||||
else
|
else
|
||||||
searchResults = connectionRecordMapper.searchReadable(identifier,
|
searchResults = connectionRecordMapper.searchReadable(identifier,
|
||||||
user.getUser().getModel(), requiredContents, sortPredicates,
|
user.getUser().getModel(), recordIdentifier,
|
||||||
limit, user.getEffectiveUserGroups());
|
requiredContents, sortPredicates, limit,
|
||||||
|
user.getEffectiveUserGroups());
|
||||||
|
|
||||||
return getObjectInstances(searchResults);
|
return getObjectInstances(searchResults);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves the connection history records matching the given criteria.
|
|
||||||
* Retrieves up to <code>limit</code> connection history records matching
|
|
||||||
* the given terms and sorted by the given predicates. Only history records
|
|
||||||
* associated with data that the given user can read are returned.
|
|
||||||
*
|
|
||||||
* @param user
|
|
||||||
* The user retrieving the connection history.
|
|
||||||
*
|
|
||||||
* @param requiredContents
|
|
||||||
* The search terms that must be contained somewhere within each of the
|
|
||||||
* returned records.
|
|
||||||
*
|
|
||||||
* @param sortPredicates
|
|
||||||
* A list of predicates to sort the returned records by, in order of
|
|
||||||
* priority.
|
|
||||||
*
|
|
||||||
* @param limit
|
|
||||||
* The maximum number of records that should be returned.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* The connection history of the given connection, including any
|
|
||||||
* active connections.
|
|
||||||
*
|
|
||||||
* @throws GuacamoleException
|
|
||||||
* If permission to read the connection history is denied.
|
|
||||||
*/
|
|
||||||
public List<ConnectionRecord> retrieveHistory(ModeledAuthenticatedUser user,
|
|
||||||
Collection<ActivityRecordSearchTerm> requiredContents,
|
|
||||||
List<ActivityRecordSortPredicate> sortPredicates, int limit)
|
|
||||||
throws GuacamoleException {
|
|
||||||
|
|
||||||
return retrieveHistory(null, user, requiredContents, sortPredicates, limit);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Connects to the given connection as the given user, using the given
|
* Connects to the given connection as the given user, using the given
|
||||||
* client information. If the user does not have permission to read the
|
* client information. If the user does not have permission to read the
|
||||||
|
@@ -43,7 +43,7 @@ public class ModeledConnectionRecord extends ModeledActivityRecord
|
|||||||
* The model object to use to back this connection record.
|
* The model object to use to back this connection record.
|
||||||
*/
|
*/
|
||||||
public ModeledConnectionRecord(ConnectionRecordModel model) {
|
public ModeledConnectionRecord(ConnectionRecordModel model) {
|
||||||
super(model);
|
super(ConnectionRecordSet.UUID_NAMESPACE, model);
|
||||||
this.model = model;
|
this.model = model;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,4 +67,9 @@ public class ModeledConnectionRecord extends ModeledActivityRecord
|
|||||||
return model.getSharingProfileName();
|
return model.getSharingProfileName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConnectionRecordModel getModel() {
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -25,10 +25,12 @@ import java.util.ArrayList;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import org.apache.guacamole.auth.jdbc.user.ModeledAuthenticatedUser;
|
import org.apache.guacamole.auth.jdbc.user.ModeledAuthenticatedUser;
|
||||||
@@ -55,6 +57,7 @@ import org.apache.guacamole.protocol.GuacamoleConfiguration;
|
|||||||
import org.apache.guacamole.token.TokenFilter;
|
import org.apache.guacamole.token.TokenFilter;
|
||||||
import org.mybatis.guice.transactional.Transactional;
|
import org.mybatis.guice.transactional.Transactional;
|
||||||
import org.apache.guacamole.auth.jdbc.connection.ConnectionParameterMapper;
|
import org.apache.guacamole.auth.jdbc.connection.ConnectionParameterMapper;
|
||||||
|
import org.apache.guacamole.auth.jdbc.sharing.SharedConnectionMap;
|
||||||
import org.apache.guacamole.auth.jdbc.sharing.connection.SharedConnectionDefinition;
|
import org.apache.guacamole.auth.jdbc.sharing.connection.SharedConnectionDefinition;
|
||||||
import org.apache.guacamole.auth.jdbc.sharingprofile.ModeledSharingProfile;
|
import org.apache.guacamole.auth.jdbc.sharingprofile.ModeledSharingProfile;
|
||||||
import org.apache.guacamole.auth.jdbc.sharingprofile.SharingProfileParameterMapper;
|
import org.apache.guacamole.auth.jdbc.sharingprofile.SharingProfileParameterMapper;
|
||||||
@@ -109,10 +112,10 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS
|
|||||||
private ConnectionRecordMapper connectionRecordMapper;
|
private ConnectionRecordMapper connectionRecordMapper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provider for creating active connection records.
|
* Map of all currently-shared connections.
|
||||||
*/
|
*/
|
||||||
@Inject
|
@Inject
|
||||||
private Provider<ActiveConnectionRecord> activeConnectionRecordProvider;
|
private SharedConnectionMap connectionMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* All active connections through the tunnel having a given UUID.
|
* All active connections through the tunnel having a given UUID.
|
||||||
@@ -252,33 +255,6 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Saves the given ActiveConnectionRecord to the database. The end date of
|
|
||||||
* the saved record will be populated with the current time.
|
|
||||||
*
|
|
||||||
* @param record
|
|
||||||
* The record to save.
|
|
||||||
*/
|
|
||||||
private void saveConnectionRecord(ActiveConnectionRecord record) {
|
|
||||||
|
|
||||||
// Get associated models
|
|
||||||
ConnectionRecordModel recordModel = new ConnectionRecordModel();
|
|
||||||
|
|
||||||
// Copy user information and timestamps into new record
|
|
||||||
recordModel.setUsername(record.getUsername());
|
|
||||||
recordModel.setConnectionIdentifier(record.getConnectionIdentifier());
|
|
||||||
recordModel.setConnectionName(record.getConnectionName());
|
|
||||||
recordModel.setRemoteHost(record.getRemoteHost());
|
|
||||||
recordModel.setSharingProfileIdentifier(record.getSharingProfileIdentifier());
|
|
||||||
recordModel.setSharingProfileName(record.getSharingProfileName());
|
|
||||||
recordModel.setStartDate(record.getStartDate());
|
|
||||||
recordModel.setEndDate(new Date());
|
|
||||||
|
|
||||||
// Insert connection record
|
|
||||||
connectionRecordMapper.insert(recordModel);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an unconfigured GuacamoleSocket that is already connected to
|
* Returns an unconfigured GuacamoleSocket that is already connected to
|
||||||
* guacd as specified in guacamole.properties, using SSL if necessary.
|
* guacd as specified in guacamole.properties, using SSL if necessary.
|
||||||
@@ -369,7 +345,9 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS
|
|||||||
activeConnection.invalidate();
|
activeConnection.invalidate();
|
||||||
|
|
||||||
// Remove underlying tunnel from list of active tunnels
|
// Remove underlying tunnel from list of active tunnels
|
||||||
activeTunnels.remove(activeConnection.getUUID().toString());
|
UUID uuid = activeConnection.getUUID(); // May be null if record not successfully inserted
|
||||||
|
if (uuid != null)
|
||||||
|
activeTunnels.remove(uuid.toString());
|
||||||
|
|
||||||
// Get original user
|
// Get original user
|
||||||
RemoteAuthenticatedUser user = activeConnection.getUser();
|
RemoteAuthenticatedUser user = activeConnection.getUser();
|
||||||
@@ -393,8 +371,10 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS
|
|||||||
if (activeConnection.hasBalancingGroup())
|
if (activeConnection.hasBalancingGroup())
|
||||||
release(user, activeConnection.getBalancingGroup());
|
release(user, activeConnection.getBalancingGroup());
|
||||||
|
|
||||||
// Save history record to database
|
// Update history record with end date
|
||||||
saveConnectionRecord(activeConnection);
|
ConnectionRecordModel recordModel = activeConnection.getModel();
|
||||||
|
recordModel.setEndDate(new Date());
|
||||||
|
connectionRecordMapper.updateEndDate(recordModel);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -438,7 +418,16 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS
|
|||||||
|
|
||||||
// Record new active connection
|
// Record new active connection
|
||||||
Runnable cleanupTask = new ConnectionCleanupTask(activeConnection);
|
Runnable cleanupTask = new ConnectionCleanupTask(activeConnection);
|
||||||
activeTunnels.put(activeConnection.getUUID().toString(), activeConnection);
|
try {
|
||||||
|
connectionRecordMapper.insert(activeConnection.getModel()); // This MUST happen before getUUID() is invoked, to ensure the ID driving the UUID exists
|
||||||
|
activeTunnels.put(activeConnection.getUUID().toString(), activeConnection);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute cleanup if connection history could not be updated
|
||||||
|
catch (RuntimeException | Error e) {
|
||||||
|
cleanupTask.run();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
@@ -471,6 +460,10 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Include history record UUID as token
|
||||||
|
tokens = new HashMap<>(tokens);
|
||||||
|
tokens.put("HISTORY_UUID", activeConnection.getUUID().toString());
|
||||||
|
|
||||||
// Build token filter containing credential tokens
|
// Build token filter containing credential tokens
|
||||||
TokenFilter tokenFilter = new TokenFilter();
|
TokenFilter tokenFilter = new TokenFilter();
|
||||||
tokenFilter.setTokens(tokens);
|
tokenFilter.setTokens(tokens);
|
||||||
@@ -638,8 +631,7 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS
|
|||||||
acquire(user, Collections.singletonList(connection), true);
|
acquire(user, Collections.singletonList(connection), true);
|
||||||
|
|
||||||
// Connect only if the connection was successfully acquired
|
// Connect only if the connection was successfully acquired
|
||||||
ActiveConnectionRecord connectionRecord = activeConnectionRecordProvider.get();
|
ActiveConnectionRecord connectionRecord = new ActiveConnectionRecord(connectionMap, user, connection);
|
||||||
connectionRecord.init(user, connection);
|
|
||||||
return assignGuacamoleTunnel(connectionRecord, info, tokens, false);
|
return assignGuacamoleTunnel(connectionRecord, info, tokens, false);
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -685,8 +677,7 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS
|
|||||||
try {
|
try {
|
||||||
|
|
||||||
// Connect to acquired child
|
// Connect to acquired child
|
||||||
ActiveConnectionRecord connectionRecord = activeConnectionRecordProvider.get();
|
ActiveConnectionRecord connectionRecord = new ActiveConnectionRecord(connectionMap, user, connectionGroup, connection);
|
||||||
connectionRecord.init(user, connectionGroup, connection);
|
|
||||||
GuacamoleTunnel tunnel = assignGuacamoleTunnel(connectionRecord,
|
GuacamoleTunnel tunnel = assignGuacamoleTunnel(connectionRecord,
|
||||||
info, tokens, connections.size() > 1);
|
info, tokens, connections.size() > 1);
|
||||||
|
|
||||||
@@ -741,9 +732,8 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS
|
|||||||
throws GuacamoleException {
|
throws GuacamoleException {
|
||||||
|
|
||||||
// Create a connection record which describes the shared connection
|
// Create a connection record which describes the shared connection
|
||||||
ActiveConnectionRecord connectionRecord = activeConnectionRecordProvider.get();
|
ActiveConnectionRecord connectionRecord = new ActiveConnectionRecord(connectionMap,
|
||||||
connectionRecord.init(user, definition.getActiveConnection(),
|
user, definition.getActiveConnection(), definition.getSharingProfile());
|
||||||
definition.getSharingProfile());
|
|
||||||
|
|
||||||
// Connect to shared connection described by the created record
|
// Connect to shared connection described by the created record
|
||||||
GuacamoleTunnel tunnel = assignGuacamoleTunnel(connectionRecord, info, tokens, false);
|
GuacamoleTunnel tunnel = assignGuacamoleTunnel(connectionRecord, info, tokens, false);
|
||||||
|
@@ -19,10 +19,11 @@
|
|||||||
|
|
||||||
package org.apache.guacamole.auth.jdbc.tunnel;
|
package org.apache.guacamole.auth.jdbc.tunnel;
|
||||||
|
|
||||||
import com.google.inject.Inject;
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import org.apache.guacamole.auth.jdbc.connection.ConnectionRecordModel;
|
||||||
import org.apache.guacamole.auth.jdbc.connection.ModeledConnection;
|
import org.apache.guacamole.auth.jdbc.connection.ModeledConnection;
|
||||||
|
import org.apache.guacamole.auth.jdbc.connection.ModeledConnectionRecord;
|
||||||
import org.apache.guacamole.auth.jdbc.connectiongroup.ModeledConnectionGroup;
|
import org.apache.guacamole.auth.jdbc.connectiongroup.ModeledConnectionGroup;
|
||||||
import org.apache.guacamole.auth.jdbc.sharing.SharedConnectionMap;
|
import org.apache.guacamole.auth.jdbc.sharing.SharedConnectionMap;
|
||||||
import org.apache.guacamole.auth.jdbc.sharing.SharedObjectManager;
|
import org.apache.guacamole.auth.jdbc.sharing.SharedObjectManager;
|
||||||
@@ -31,7 +32,6 @@ import org.apache.guacamole.auth.jdbc.user.RemoteAuthenticatedUser;
|
|||||||
import org.apache.guacamole.net.AbstractGuacamoleTunnel;
|
import org.apache.guacamole.net.AbstractGuacamoleTunnel;
|
||||||
import org.apache.guacamole.net.GuacamoleSocket;
|
import org.apache.guacamole.net.GuacamoleSocket;
|
||||||
import org.apache.guacamole.net.GuacamoleTunnel;
|
import org.apache.guacamole.net.GuacamoleTunnel;
|
||||||
import org.apache.guacamole.net.auth.ConnectionRecord;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -39,41 +39,31 @@ import org.apache.guacamole.net.auth.ConnectionRecord;
|
|||||||
* the associated connection has not yet ended, getEndDate() will always return
|
* the associated connection has not yet ended, getEndDate() will always return
|
||||||
* null. The associated start date will be the time of this objects creation.
|
* null. The associated start date will be the time of this objects creation.
|
||||||
*/
|
*/
|
||||||
public class ActiveConnectionRecord implements ConnectionRecord {
|
public class ActiveConnectionRecord extends ModeledConnectionRecord {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The user that connected to the connection associated with this connection
|
* The user that connected to the connection associated with this connection
|
||||||
* record.
|
* record.
|
||||||
*/
|
*/
|
||||||
private RemoteAuthenticatedUser user;
|
private final RemoteAuthenticatedUser user;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The balancing group from which the associated connection was chosen, if
|
* The balancing group from which the associated connection was chosen, if
|
||||||
* any. If no balancing group was used, this will be null.
|
* any. If no balancing group was used, this will be null.
|
||||||
*/
|
*/
|
||||||
private ModeledConnectionGroup balancingGroup;
|
private final ModeledConnectionGroup balancingGroup;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The connection associated with this connection record.
|
* The connection associated with this connection record.
|
||||||
*/
|
*/
|
||||||
private ModeledConnection connection;
|
private final ModeledConnection connection;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The sharing profile that was used to access the connection associated
|
* The sharing profile that was used to access the connection associated
|
||||||
* with this connection record. If the connection was accessed directly
|
* with this connection record. If the connection was accessed directly
|
||||||
* (without involving a sharing profile), this will be null.
|
* (without involving a sharing profile), this will be null.
|
||||||
*/
|
*/
|
||||||
private ModeledSharingProfile sharingProfile;
|
private final ModeledSharingProfile sharingProfile;
|
||||||
|
|
||||||
/**
|
|
||||||
* The time this connection record was created.
|
|
||||||
*/
|
|
||||||
private final Date startDate = new Date();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The UUID that will be assigned to the underlying tunnel.
|
|
||||||
*/
|
|
||||||
private final UUID uuid = UUID.randomUUID();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The connection ID of the connection as determined by guacd, not to be
|
* The connection ID of the connection as determined by guacd, not to be
|
||||||
@@ -91,8 +81,7 @@ public class ActiveConnectionRecord implements ConnectionRecord {
|
|||||||
/**
|
/**
|
||||||
* Map of all currently-shared connections.
|
* Map of all currently-shared connections.
|
||||||
*/
|
*/
|
||||||
@Inject
|
private final SharedConnectionMap connectionMap;
|
||||||
private SharedConnectionMap connectionMap;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manager which tracks all share keys associated with this connection
|
* Manager which tracks all share keys associated with this connection
|
||||||
@@ -111,7 +100,57 @@ public class ActiveConnectionRecord implements ConnectionRecord {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes this connection record, associating it with the given user,
|
* Creates a new connection record model object, associating it with the
|
||||||
|
* given user, connection, and sharing profile. The given sharing profile
|
||||||
|
* MUST be the sharing profile that was used to share access to the given
|
||||||
|
* connection. The start date of this connection record will be the time of
|
||||||
|
* its creation. No end date will be assigned.
|
||||||
|
*
|
||||||
|
* @param user
|
||||||
|
* The user that connected to the connection associated with this
|
||||||
|
* connection record.
|
||||||
|
*
|
||||||
|
* @param connection
|
||||||
|
* The connection to associate with this connection record.
|
||||||
|
*
|
||||||
|
* @param sharingProfile
|
||||||
|
* The sharing profile that was used to share access to the given
|
||||||
|
* connection, or null if no sharing profile was used.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* A new connection record model object associated with the given user,
|
||||||
|
* connection, and sharing profile, and having the current date/time as
|
||||||
|
* its start date.
|
||||||
|
*/
|
||||||
|
private static ConnectionRecordModel createModel(RemoteAuthenticatedUser user,
|
||||||
|
ModeledConnection connection,
|
||||||
|
ModeledSharingProfile sharingProfile) {
|
||||||
|
|
||||||
|
// Create model object representing an active connection that started
|
||||||
|
// at the current time ...
|
||||||
|
ConnectionRecordModel recordModel = new ConnectionRecordModel();
|
||||||
|
recordModel.setStartDate(new Date());
|
||||||
|
|
||||||
|
// ... was established by the given user ...
|
||||||
|
recordModel.setUsername(user.getIdentifier());
|
||||||
|
recordModel.setRemoteHost(user.getRemoteHost());
|
||||||
|
|
||||||
|
// ... to the given connection ...
|
||||||
|
recordModel.setConnectionIdentifier(connection.getIdentifier());
|
||||||
|
recordModel.setConnectionName(connection.getName());
|
||||||
|
|
||||||
|
// ... using the given sharing profile (if any)
|
||||||
|
if (sharingProfile != null) {
|
||||||
|
recordModel.setSharingProfileIdentifier(sharingProfile.getIdentifier());
|
||||||
|
recordModel.setSharingProfileName(sharingProfile.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
return recordModel;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new ActiveConnectionRecord associated with the given user,
|
||||||
* connection, balancing connection group, and sharing profile. The given
|
* connection, balancing connection group, and sharing profile. The given
|
||||||
* balancing connection group MUST be the connection group from which the
|
* balancing connection group MUST be the connection group from which the
|
||||||
* given connection was chosen, and the given sharing profile MUST be the
|
* given connection was chosen, and the given sharing profile MUST be the
|
||||||
@@ -119,6 +158,10 @@ public class ActiveConnectionRecord implements ConnectionRecord {
|
|||||||
* The start date of this connection record will be the time of its
|
* The start date of this connection record will be the time of its
|
||||||
* creation.
|
* creation.
|
||||||
*
|
*
|
||||||
|
* @param connectionMap
|
||||||
|
* The SharedConnectionMap instance tracking all active shared
|
||||||
|
* connections.
|
||||||
|
*
|
||||||
* @param user
|
* @param user
|
||||||
* The user that connected to the connection associated with this
|
* The user that connected to the connection associated with this
|
||||||
* connection record.
|
* connection record.
|
||||||
@@ -134,10 +177,13 @@ public class ActiveConnectionRecord implements ConnectionRecord {
|
|||||||
* The sharing profile that was used to share access to the given
|
* The sharing profile that was used to share access to the given
|
||||||
* connection, or null if no sharing profile was used.
|
* connection, or null if no sharing profile was used.
|
||||||
*/
|
*/
|
||||||
private void init(RemoteAuthenticatedUser user,
|
public ActiveConnectionRecord(SharedConnectionMap connectionMap,
|
||||||
|
RemoteAuthenticatedUser user,
|
||||||
ModeledConnectionGroup balancingGroup,
|
ModeledConnectionGroup balancingGroup,
|
||||||
ModeledConnection connection,
|
ModeledConnection connection,
|
||||||
ModeledSharingProfile sharingProfile) {
|
ModeledSharingProfile sharingProfile) {
|
||||||
|
super(createModel(user, connection, sharingProfile));
|
||||||
|
this.connectionMap = connectionMap;
|
||||||
this.user = user;
|
this.user = user;
|
||||||
this.balancingGroup = balancingGroup;
|
this.balancingGroup = balancingGroup;
|
||||||
this.connection = connection;
|
this.connection = connection;
|
||||||
@@ -145,12 +191,16 @@ public class ActiveConnectionRecord implements ConnectionRecord {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes this connection record, associating it with the given user,
|
* Creates a new ActiveConnectionRecord associated with the given user,
|
||||||
* connection, and balancing connection group. The given balancing
|
* connection, and balancing connection group. The given balancing
|
||||||
* connection group MUST be the connection group from which the given
|
* connection group MUST be the connection group from which the given
|
||||||
* connection was chosen. The start date of this connection record will be
|
* connection was chosen. The start date of this connection record will be
|
||||||
* the time of its creation.
|
* the time of its creation.
|
||||||
*
|
*
|
||||||
|
* @param connectionMap
|
||||||
|
* The SharedConnectionMap instance tracking all active shared
|
||||||
|
* connections.
|
||||||
|
*
|
||||||
* @param user
|
* @param user
|
||||||
* The user that connected to the connection associated with this
|
* The user that connected to the connection associated with this
|
||||||
* connection record.
|
* connection record.
|
||||||
@@ -161,17 +211,22 @@ public class ActiveConnectionRecord implements ConnectionRecord {
|
|||||||
* @param connection
|
* @param connection
|
||||||
* The connection to associate with this connection record.
|
* The connection to associate with this connection record.
|
||||||
*/
|
*/
|
||||||
public void init(RemoteAuthenticatedUser user,
|
public ActiveConnectionRecord(SharedConnectionMap connectionMap,
|
||||||
|
RemoteAuthenticatedUser user,
|
||||||
ModeledConnectionGroup balancingGroup,
|
ModeledConnectionGroup balancingGroup,
|
||||||
ModeledConnection connection) {
|
ModeledConnection connection) {
|
||||||
init(user, balancingGroup, connection, null);
|
this(connectionMap, user, balancingGroup, connection, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes this connection record, associating it with the given user
|
* Creates a new ActiveConnectionRecord associated with the given user,
|
||||||
* and connection. The start date of this connection record will be the time
|
* and connection. The start date of this connection record will be the time
|
||||||
* of its creation.
|
* of its creation.
|
||||||
*
|
*
|
||||||
|
* @param connectionMap
|
||||||
|
* The SharedConnectionMap instance tracking all active shared
|
||||||
|
* connections.
|
||||||
|
*
|
||||||
* @param user
|
* @param user
|
||||||
* The user that connected to the connection associated with this
|
* The user that connected to the connection associated with this
|
||||||
* connection record.
|
* connection record.
|
||||||
@@ -179,18 +234,22 @@ public class ActiveConnectionRecord implements ConnectionRecord {
|
|||||||
* @param connection
|
* @param connection
|
||||||
* The connection to associate with this connection record.
|
* The connection to associate with this connection record.
|
||||||
*/
|
*/
|
||||||
public void init(RemoteAuthenticatedUser user,
|
public ActiveConnectionRecord(SharedConnectionMap connectionMap,
|
||||||
ModeledConnection connection) {
|
RemoteAuthenticatedUser user, ModeledConnection connection) {
|
||||||
init(user, null, connection);
|
this(connectionMap, user, null, connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes this connection record, associating it with the given user,
|
* Creates a new ActiveConnectionRecord associated with the given user,
|
||||||
* active connection, and sharing profile. The given sharing profile MUST be
|
* active connection, and sharing profile. The given sharing profile MUST be
|
||||||
* the sharing profile that was used to share access to the given
|
* the sharing profile that was used to share access to the given
|
||||||
* connection. The start date of this connection record will be the time of
|
* connection. The start date of this connection record will be the time of
|
||||||
* its creation.
|
* its creation.
|
||||||
*
|
*
|
||||||
|
* @param connectionMap
|
||||||
|
* The SharedConnectionMap instance tracking all active shared
|
||||||
|
* connections.
|
||||||
|
*
|
||||||
* @param user
|
* @param user
|
||||||
* The user that connected to the connection associated with this
|
* The user that connected to the connection associated with this
|
||||||
* connection record.
|
* connection record.
|
||||||
@@ -204,10 +263,11 @@ public class ActiveConnectionRecord implements ConnectionRecord {
|
|||||||
* connection, or null if no sharing profile should be used (access to
|
* connection, or null if no sharing profile should be used (access to
|
||||||
* the connection is unrestricted).
|
* the connection is unrestricted).
|
||||||
*/
|
*/
|
||||||
public void init(RemoteAuthenticatedUser user,
|
public ActiveConnectionRecord(SharedConnectionMap connectionMap,
|
||||||
|
RemoteAuthenticatedUser user,
|
||||||
ActiveConnectionRecord activeConnection,
|
ActiveConnectionRecord activeConnection,
|
||||||
ModeledSharingProfile sharingProfile) {
|
ModeledSharingProfile sharingProfile) {
|
||||||
init(user, null, activeConnection.getConnection(), sharingProfile);
|
this(connectionMap, user, null, activeConnection.getConnection(), sharingProfile);
|
||||||
this.connectionID = activeConnection.getConnectionID();
|
this.connectionID = activeConnection.getConnectionID();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -286,63 +346,6 @@ public class ActiveConnectionRecord implements ConnectionRecord {
|
|||||||
return sharingProfile == null;
|
return sharingProfile == null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getConnectionIdentifier() {
|
|
||||||
return connection.getIdentifier();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getConnectionName() {
|
|
||||||
return connection.getName();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getSharingProfileIdentifier() {
|
|
||||||
|
|
||||||
// Return sharing profile identifier if known
|
|
||||||
if (sharingProfile != null)
|
|
||||||
return sharingProfile.getIdentifier();
|
|
||||||
|
|
||||||
// No associated sharing profile
|
|
||||||
return null;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getSharingProfileName() {
|
|
||||||
|
|
||||||
// Return sharing profile name if known
|
|
||||||
if (sharingProfile != null)
|
|
||||||
return sharingProfile.getName();
|
|
||||||
|
|
||||||
// No associated sharing profile
|
|
||||||
return null;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Date getStartDate() {
|
|
||||||
return startDate;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Date getEndDate() {
|
|
||||||
|
|
||||||
// Active connections have not yet ended
|
|
||||||
return null;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getRemoteHost() {
|
|
||||||
return user.getRemoteHost();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getUsername() {
|
|
||||||
return user.getIdentifier();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isActive() {
|
public boolean isActive() {
|
||||||
return tunnel != null && tunnel.isOpen();
|
return tunnel != null && tunnel.isOpen();
|
||||||
@@ -387,7 +390,7 @@ public class ActiveConnectionRecord implements ConnectionRecord {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UUID getUUID() {
|
public UUID getUUID() {
|
||||||
return uuid;
|
return ActiveConnectionRecord.this.getUUID();
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
@@ -401,18 +404,6 @@ public class ActiveConnectionRecord implements ConnectionRecord {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the UUID of the underlying tunnel. If there is no underlying
|
|
||||||
* tunnel, this will be the UUID assigned to the underlying tunnel when the
|
|
||||||
* tunnel is set.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* The current or future UUID of the underlying tunnel.
|
|
||||||
*/
|
|
||||||
public UUID getUUID() {
|
|
||||||
return uuid;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the connection ID of the in-progress connection as determined by
|
* Returns the connection ID of the in-progress connection as determined by
|
||||||
* guacd, not to be confused with the connection identifier determined by
|
* guacd, not to be confused with the connection identifier determined by
|
||||||
|
@@ -291,7 +291,7 @@ public class ModeledUserContext extends RestrictedObject
|
|||||||
// Record logout time only if login time was recorded
|
// Record logout time only if login time was recorded
|
||||||
if (userRecord != null) {
|
if (userRecord != null) {
|
||||||
userRecord.setEndDate(new Date());
|
userRecord.setEndDate(new Date());
|
||||||
userRecordMapper.update(userRecord);
|
userRecordMapper.updateEndDate(userRecord);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -19,124 +19,10 @@
|
|||||||
|
|
||||||
package org.apache.guacamole.auth.jdbc.user;
|
package org.apache.guacamole.auth.jdbc.user;
|
||||||
|
|
||||||
import java.util.Collection;
|
import org.apache.guacamole.auth.jdbc.base.ActivityRecordMapper;
|
||||||
import java.util.List;
|
|
||||||
import org.apache.guacamole.auth.jdbc.base.ActivityRecordModel;
|
import org.apache.guacamole.auth.jdbc.base.ActivityRecordModel;
|
||||||
import org.apache.guacamole.auth.jdbc.base.ActivityRecordSearchTerm;
|
|
||||||
import org.apache.guacamole.auth.jdbc.base.ActivityRecordSortPredicate;
|
|
||||||
import org.apache.ibatis.annotations.Param;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mapper for user login activity records.
|
* Mapper for user login activity records.
|
||||||
*/
|
*/
|
||||||
public interface UserRecordMapper {
|
public interface UserRecordMapper extends ActivityRecordMapper<ActivityRecordModel> {}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a collection of all user login records associated with the user
|
|
||||||
* having the given username.
|
|
||||||
*
|
|
||||||
* @param username
|
|
||||||
* The username of the user whose login records are to be retrieved.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* A collection of all user login records associated with the user
|
|
||||||
* having the given username. This collection will be empty if no such
|
|
||||||
* user exists.
|
|
||||||
*/
|
|
||||||
List<ActivityRecordModel> select(@Param("username") String username);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inserts the given user login record.
|
|
||||||
*
|
|
||||||
* @param record
|
|
||||||
* The user login record to insert.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* The number of rows inserted.
|
|
||||||
*/
|
|
||||||
int insert(@Param("record") ActivityRecordModel record);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the given user login record.
|
|
||||||
*
|
|
||||||
* @param record
|
|
||||||
* The user login record to update.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* The number of rows updated.
|
|
||||||
*/
|
|
||||||
int update(@Param("record") ActivityRecordModel record);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Searches for up to <code>limit</code> user login records that contain
|
|
||||||
* the given terms, sorted by the given predicates, regardless of whether
|
|
||||||
* the data they are associated with is is readable by any particular user.
|
|
||||||
* This should only be called on behalf of a system administrator. If
|
|
||||||
* records are needed by a non-administrative user who must have explicit
|
|
||||||
* read rights, use {@link searchReadable()} instead.
|
|
||||||
*
|
|
||||||
* @param username
|
|
||||||
* The optional username to which records should be limited, or null
|
|
||||||
* if all records should be retrieved.
|
|
||||||
*
|
|
||||||
* @param terms
|
|
||||||
* The search terms that must match the returned records.
|
|
||||||
*
|
|
||||||
* @param sortPredicates
|
|
||||||
* A list of predicates to sort the returned records by, in order of
|
|
||||||
* priority.
|
|
||||||
*
|
|
||||||
* @param limit
|
|
||||||
* The maximum number of records that should be returned.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* The results of the search performed with the given parameters.
|
|
||||||
*/
|
|
||||||
List<ActivityRecordModel> search(@Param("username") String username,
|
|
||||||
@Param("terms") Collection<ActivityRecordSearchTerm> terms,
|
|
||||||
@Param("sortPredicates") List<ActivityRecordSortPredicate> sortPredicates,
|
|
||||||
@Param("limit") int limit);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Searches for up to <code>limit</code> user login records that contain
|
|
||||||
* the given terms, sorted by the given predicates. Only records that are
|
|
||||||
* associated with data explicitly readable by the given user will be
|
|
||||||
* returned. If records are needed by a system administrator (who, by
|
|
||||||
* definition, does not need explicit read rights), use {@link search()}
|
|
||||||
* instead.
|
|
||||||
*
|
|
||||||
* @param username
|
|
||||||
* The optional username to which records should be limited, or null
|
|
||||||
* if all readable records should be retrieved.
|
|
||||||
*
|
|
||||||
* @param user
|
|
||||||
* The user whose permissions should determine whether a record is
|
|
||||||
* returned.
|
|
||||||
*
|
|
||||||
* @param terms
|
|
||||||
* The search terms that must match the returned records.
|
|
||||||
*
|
|
||||||
* @param sortPredicates
|
|
||||||
* A list of predicates to sort the returned records by, in order of
|
|
||||||
* priority.
|
|
||||||
*
|
|
||||||
* @param limit
|
|
||||||
* The maximum number of records that should be returned.
|
|
||||||
*
|
|
||||||
* @param effectiveGroups
|
|
||||||
* The identifiers of all groups that should be taken into account
|
|
||||||
* when determining the permissions effectively granted to the user. If
|
|
||||||
* no groups are given, only permissions directly granted to the user
|
|
||||||
* will be used.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* The results of the search performed with the given parameters.
|
|
||||||
*/
|
|
||||||
List<ActivityRecordModel> searchReadable(@Param("username") String username,
|
|
||||||
@Param("user") UserModel user,
|
|
||||||
@Param("terms") Collection<ActivityRecordSearchTerm> terms,
|
|
||||||
@Param("sortPredicates") List<ActivityRecordSortPredicate> sortPredicates,
|
|
||||||
@Param("limit") int limit,
|
|
||||||
@Param("effectiveGroups") Collection<String> effectiveGroups);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
@@ -20,9 +20,9 @@
|
|||||||
package org.apache.guacamole.auth.jdbc.user;
|
package org.apache.guacamole.auth.jdbc.user;
|
||||||
|
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
import org.apache.guacamole.GuacamoleException;
|
import org.apache.guacamole.GuacamoleException;
|
||||||
import org.apache.guacamole.auth.jdbc.base.ActivityRecordSearchTerm;
|
import org.apache.guacamole.auth.jdbc.base.ActivityRecordSearchTerm;
|
||||||
import org.apache.guacamole.auth.jdbc.base.ActivityRecordSortPredicate;
|
import org.apache.guacamole.auth.jdbc.base.ActivityRecordSortPredicate;
|
||||||
@@ -38,6 +38,15 @@ import org.apache.guacamole.net.auth.AuthenticatedUser;
|
|||||||
*/
|
*/
|
||||||
public class UserRecordSet extends ModeledActivityRecordSet<ActivityRecord> {
|
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.
|
* Service for managing user objects.
|
||||||
*/
|
*/
|
||||||
@@ -70,14 +79,15 @@ public class UserRecordSet extends ModeledActivityRecordSet<ActivityRecord> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Collection<ActivityRecord> retrieveHistory(
|
protected List<ActivityRecord> retrieveHistory(
|
||||||
AuthenticatedUser user, Set<ActivityRecordSearchTerm> requiredContents,
|
AuthenticatedUser user, String recordIdentifier,
|
||||||
|
Set<ActivityRecordSearchTerm> requiredContents,
|
||||||
List<ActivityRecordSortPredicate> sortPredicates, int limit)
|
List<ActivityRecordSortPredicate> sortPredicates, int limit)
|
||||||
throws GuacamoleException {
|
throws GuacamoleException {
|
||||||
|
|
||||||
// Retrieve history from database
|
// Retrieve history from database
|
||||||
return userService.retrieveHistory(identifier, getCurrentUser(),
|
return userService.retrieveHistory(identifier, getCurrentUser(),
|
||||||
requiredContents, sortPredicates, limit);
|
recordIdentifier, requiredContents, sortPredicates, limit);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -546,7 +546,7 @@ public class UserService extends ModeledDirectoryObjectService<ModeledUser, User
|
|||||||
* A connection record object which is backed by the given model.
|
* A connection record object which is backed by the given model.
|
||||||
*/
|
*/
|
||||||
protected ActivityRecord getObjectInstance(ActivityRecordModel model) {
|
protected ActivityRecord getObjectInstance(ActivityRecordModel model) {
|
||||||
return new ModeledActivityRecord(model);
|
return new ModeledActivityRecord(UserRecordSet.UUID_NAMESPACE, model);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -572,32 +572,6 @@ public class UserService extends ModeledDirectoryObjectService<ModeledUser, User
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves the login history of the given user, including any active
|
|
||||||
* sessions.
|
|
||||||
*
|
|
||||||
* @param authenticatedUser
|
|
||||||
* The user retrieving the login history.
|
|
||||||
*
|
|
||||||
* @param user
|
|
||||||
* The user whose history is being retrieved.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* The login history of the given user, including any active sessions.
|
|
||||||
*
|
|
||||||
* @throws GuacamoleException
|
|
||||||
* If permission to read the login history is denied.
|
|
||||||
*/
|
|
||||||
public List<ActivityRecord> retrieveHistory(ModeledAuthenticatedUser authenticatedUser,
|
|
||||||
ModeledUser user) throws GuacamoleException {
|
|
||||||
|
|
||||||
String username = user.getIdentifier();
|
|
||||||
|
|
||||||
return retrieveHistory(username, authenticatedUser, Collections.emptyList(),
|
|
||||||
Collections.emptyList(), Integer.MAX_VALUE);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves user login history records matching the given criteria.
|
* Retrieves user login history records matching the given criteria.
|
||||||
* Retrieves up to <code>limit</code> user history records matching the
|
* Retrieves up to <code>limit</code> user history records matching the
|
||||||
@@ -611,6 +585,11 @@ public class UserService extends ModeledDirectoryObjectService<ModeledUser, User
|
|||||||
* @param user
|
* @param user
|
||||||
* The user retrieving the login history.
|
* The user retrieving the login history.
|
||||||
*
|
*
|
||||||
|
* @param recordIdentifier
|
||||||
|
* The identifier of the specific history record to retrieve, if not
|
||||||
|
* all matching records. Search terms, etc. will still be applied to
|
||||||
|
* the single record.
|
||||||
|
*
|
||||||
* @param requiredContents
|
* @param requiredContents
|
||||||
* The search terms that must be contained somewhere within each of the
|
* The search terms that must be contained somewhere within each of the
|
||||||
* returned records.
|
* returned records.
|
||||||
@@ -629,7 +608,7 @@ public class UserService extends ModeledDirectoryObjectService<ModeledUser, User
|
|||||||
* If permission to read the user login history is denied.
|
* If permission to read the user login history is denied.
|
||||||
*/
|
*/
|
||||||
public List<ActivityRecord> retrieveHistory(String username,
|
public List<ActivityRecord> retrieveHistory(String username,
|
||||||
ModeledAuthenticatedUser user,
|
ModeledAuthenticatedUser user, String recordIdentifier,
|
||||||
Collection<ActivityRecordSearchTerm> requiredContents,
|
Collection<ActivityRecordSearchTerm> requiredContents,
|
||||||
List<ActivityRecordSortPredicate> sortPredicates, int limit)
|
List<ActivityRecordSortPredicate> sortPredicates, int limit)
|
||||||
throws GuacamoleException {
|
throws GuacamoleException {
|
||||||
@@ -638,52 +617,18 @@ public class UserService extends ModeledDirectoryObjectService<ModeledUser, User
|
|||||||
|
|
||||||
// Bypass permission checks if the user is privileged
|
// Bypass permission checks if the user is privileged
|
||||||
if (user.isPrivileged())
|
if (user.isPrivileged())
|
||||||
searchResults = userRecordMapper.search(username, requiredContents,
|
searchResults = userRecordMapper.search(username, recordIdentifier,
|
||||||
sortPredicates, limit);
|
requiredContents, sortPredicates, limit);
|
||||||
|
|
||||||
// Otherwise only return explicitly readable history records
|
// Otherwise only return explicitly readable history records
|
||||||
else
|
else
|
||||||
searchResults = userRecordMapper.searchReadable(username,
|
searchResults = userRecordMapper.searchReadable(username,
|
||||||
user.getUser().getModel(),
|
user.getUser().getModel(), recordIdentifier,
|
||||||
requiredContents, sortPredicates, limit, user.getEffectiveUserGroups());
|
requiredContents, sortPredicates, limit,
|
||||||
|
user.getEffectiveUserGroups());
|
||||||
|
|
||||||
return getObjectInstances(searchResults);
|
return getObjectInstances(searchResults);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves user login history records matching the given criteria.
|
|
||||||
* Retrieves up to <code>limit</code> user history records matching the
|
|
||||||
* given terms and sorted by the given predicates. Only history records
|
|
||||||
* associated with data that the given user can read are returned.
|
|
||||||
*
|
|
||||||
* @param user
|
|
||||||
* The user retrieving the login history.
|
|
||||||
*
|
|
||||||
* @param requiredContents
|
|
||||||
* The search terms that must be contained somewhere within each of the
|
|
||||||
* returned records.
|
|
||||||
*
|
|
||||||
* @param sortPredicates
|
|
||||||
* A list of predicates to sort the returned records by, in order of
|
|
||||||
* priority.
|
|
||||||
*
|
|
||||||
* @param limit
|
|
||||||
* The maximum number of records that should be returned.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* The login history of the given user, including any active sessions.
|
|
||||||
*
|
|
||||||
* @throws GuacamoleException
|
|
||||||
* If permission to read the user login history is denied.
|
|
||||||
*/
|
|
||||||
public List<ActivityRecord> retrieveHistory(ModeledAuthenticatedUser user,
|
|
||||||
Collection<ActivityRecordSearchTerm> requiredContents,
|
|
||||||
List<ActivityRecordSortPredicate> sortPredicates, int limit)
|
|
||||||
throws GuacamoleException {
|
|
||||||
|
|
||||||
return retrieveHistory(null, user, requiredContents, sortPredicates, limit);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -25,6 +25,7 @@
|
|||||||
|
|
||||||
<!-- Result mapper for system permissions -->
|
<!-- Result mapper for system permissions -->
|
||||||
<resultMap id="ConnectionRecordResultMap" type="org.apache.guacamole.auth.jdbc.connection.ConnectionRecordModel">
|
<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_id" property="connectionIdentifier" jdbcType="INTEGER"/>
|
||||||
<result column="connection_name" property="connectionName" jdbcType="VARCHAR"/>
|
<result column="connection_name" property="connectionName" jdbcType="VARCHAR"/>
|
||||||
<result column="remote_host" property="remoteHost" jdbcType="VARCHAR"/>
|
<result column="remote_host" property="remoteHost" jdbcType="VARCHAR"/>
|
||||||
@@ -36,30 +37,9 @@
|
|||||||
<result column="end_date" property="endDate" jdbcType="TIMESTAMP"/>
|
<result column="end_date" property="endDate" jdbcType="TIMESTAMP"/>
|
||||||
</resultMap>
|
</resultMap>
|
||||||
|
|
||||||
<!-- Select all connection records from a given connection -->
|
|
||||||
<select id="select" resultMap="ConnectionRecordResultMap">
|
|
||||||
|
|
||||||
SELECT
|
|
||||||
guacamole_connection_history.connection_id,
|
|
||||||
guacamole_connection_history.connection_name,
|
|
||||||
guacamole_connection_history.remote_host,
|
|
||||||
guacamole_connection_history.sharing_profile_id,
|
|
||||||
guacamole_connection_history.sharing_profile_name,
|
|
||||||
guacamole_connection_history.user_id,
|
|
||||||
guacamole_connection_history.username,
|
|
||||||
guacamole_connection_history.start_date,
|
|
||||||
guacamole_connection_history.end_date
|
|
||||||
FROM guacamole_connection_history
|
|
||||||
WHERE
|
|
||||||
guacamole_connection_history.connection_id = #{identifier,jdbcType=VARCHAR}
|
|
||||||
ORDER BY
|
|
||||||
guacamole_connection_history.start_date DESC,
|
|
||||||
guacamole_connection_history.end_date DESC
|
|
||||||
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<!-- Insert the given connection record -->
|
<!-- Insert the given connection record -->
|
||||||
<insert id="insert" parameterType="org.apache.guacamole.auth.jdbc.connection.ConnectionRecordModel">
|
<insert id="insert" useGeneratedKeys="true" keyProperty="record.recordID"
|
||||||
|
parameterType="org.apache.guacamole.auth.jdbc.connection.ConnectionRecordModel">
|
||||||
|
|
||||||
INSERT INTO guacamole_connection_history (
|
INSERT INTO guacamole_connection_history (
|
||||||
connection_id,
|
connection_id,
|
||||||
@@ -90,10 +70,18 @@
|
|||||||
|
|
||||||
</insert>
|
</insert>
|
||||||
|
|
||||||
|
<!-- Update the given connection record, assigning an end date -->
|
||||||
|
<update id="updateEndDate" parameterType="org.apache.guacamole.auth.jdbc.connection.ConnectionRecordModel">
|
||||||
|
UPDATE guacamole_connection_history
|
||||||
|
SET end_date = #{record.endDate,jdbcType=TIMESTAMP}
|
||||||
|
WHERE history_id = #{record.recordID,jdbcType=INTEGER}
|
||||||
|
</update>
|
||||||
|
|
||||||
<!-- Search for specific connection records -->
|
<!-- Search for specific connection records -->
|
||||||
<select id="search" resultMap="ConnectionRecordResultMap">
|
<select id="search" resultMap="ConnectionRecordResultMap">
|
||||||
|
|
||||||
SELECT
|
SELECT
|
||||||
|
guacamole_connection_history.history_id,
|
||||||
guacamole_connection_history.connection_id,
|
guacamole_connection_history.connection_id,
|
||||||
guacamole_connection_history.connection_name,
|
guacamole_connection_history.connection_name,
|
||||||
guacamole_connection_history.remote_host,
|
guacamole_connection_history.remote_host,
|
||||||
@@ -110,8 +98,12 @@
|
|||||||
<!-- Search terms -->
|
<!-- Search terms -->
|
||||||
<where>
|
<where>
|
||||||
|
|
||||||
|
<if test="recordIdentifier != null">
|
||||||
|
guacamole_connection_history.history_id = #{recordIdentifier,jdbcType=VARCHAR}
|
||||||
|
</if>
|
||||||
|
|
||||||
<if test="identifier != null">
|
<if test="identifier != null">
|
||||||
guacamole_connection_history.connection_id = #{identifier,jdbcType=VARCHAR}
|
AND guacamole_connection_history.connection_id = #{identifier,jdbcType=VARCHAR}
|
||||||
</if>
|
</if>
|
||||||
|
|
||||||
<foreach collection="terms" item="term" open=" AND " separator=" AND ">
|
<foreach collection="terms" item="term" open=" AND " separator=" AND ">
|
||||||
@@ -159,6 +151,7 @@
|
|||||||
<select id="searchReadable" resultMap="ConnectionRecordResultMap">
|
<select id="searchReadable" resultMap="ConnectionRecordResultMap">
|
||||||
|
|
||||||
SELECT
|
SELECT
|
||||||
|
guacamole_connection_history.history_id,
|
||||||
guacamole_connection_history.connection_id,
|
guacamole_connection_history.connection_id,
|
||||||
guacamole_connection_history.connection_name,
|
guacamole_connection_history.connection_name,
|
||||||
guacamole_connection_history.remote_host,
|
guacamole_connection_history.remote_host,
|
||||||
@@ -175,8 +168,12 @@
|
|||||||
<!-- Search terms -->
|
<!-- Search terms -->
|
||||||
<where>
|
<where>
|
||||||
|
|
||||||
|
<if test="recordIdentifier != null">
|
||||||
|
guacamole_connection_history.history_id = #{recordIdentifier,jdbcType=VARCHAR}
|
||||||
|
</if>
|
||||||
|
|
||||||
<!-- Restrict to readable connections -->
|
<!-- Restrict to readable connections -->
|
||||||
guacamole_connection_history.connection_id IN (
|
AND guacamole_connection_history.connection_id IN (
|
||||||
<include refid="org.apache.guacamole.auth.jdbc.connection.ConnectionMapper.getReadableIDs">
|
<include refid="org.apache.guacamole.auth.jdbc.connection.ConnectionMapper.getReadableIDs">
|
||||||
<property name="entityID" value="#{user.entityID,jdbcType=INTEGER}"/>
|
<property name="entityID" value="#{user.entityID,jdbcType=INTEGER}"/>
|
||||||
<property name="groups" value="effectiveGroups"/>
|
<property name="groups" value="effectiveGroups"/>
|
||||||
|
@@ -33,26 +33,6 @@
|
|||||||
<result column="end_date" property="endDate" jdbcType="TIMESTAMP"/>
|
<result column="end_date" property="endDate" jdbcType="TIMESTAMP"/>
|
||||||
</resultMap>
|
</resultMap>
|
||||||
|
|
||||||
<!-- Select all user records from a given user -->
|
|
||||||
<select id="select" resultMap="UserRecordResultMap">
|
|
||||||
|
|
||||||
SELECT
|
|
||||||
guacamole_user_history.remote_host,
|
|
||||||
guacamole_user_history.user_id,
|
|
||||||
guacamole_user_history.username,
|
|
||||||
guacamole_user_history.start_date,
|
|
||||||
guacamole_user_history.end_date
|
|
||||||
FROM guacamole_user_history
|
|
||||||
JOIN guacamole_user ON guacamole_user_history.user_id = guacamole_user.user_id
|
|
||||||
JOIN guacamole_entity ON guacamole_user.entity_id = guacamole_entity.entity_id
|
|
||||||
WHERE
|
|
||||||
guacamole_entity.name = #{username,jdbcType=VARCHAR}
|
|
||||||
ORDER BY
|
|
||||||
guacamole_user_history.start_date DESC,
|
|
||||||
guacamole_user_history.end_date DESC
|
|
||||||
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<!-- Insert the given user record -->
|
<!-- Insert the given user record -->
|
||||||
<insert id="insert" useGeneratedKeys="true" keyProperty="record.recordID"
|
<insert id="insert" useGeneratedKeys="true" keyProperty="record.recordID"
|
||||||
parameterType="org.apache.guacamole.auth.jdbc.base.ActivityRecordModel">
|
parameterType="org.apache.guacamole.auth.jdbc.base.ActivityRecordModel">
|
||||||
@@ -78,18 +58,10 @@
|
|||||||
|
|
||||||
</insert>
|
</insert>
|
||||||
|
|
||||||
<!-- Update the given user record -->
|
<!-- Update the given user record, assigning an end date -->
|
||||||
<update id="update" parameterType="org.apache.guacamole.auth.jdbc.base.ActivityRecordModel">
|
<update id="updateEndDate" parameterType="org.apache.guacamole.auth.jdbc.base.ActivityRecordModel">
|
||||||
UPDATE guacamole_user_history
|
UPDATE guacamole_user_history
|
||||||
SET remote_host = #{record.remoteHost,jdbcType=VARCHAR},
|
SET end_date = #{record.endDate,jdbcType=TIMESTAMP}
|
||||||
user_id = (SELECT user_id FROM guacamole_user
|
|
||||||
JOIN guacamole_entity ON guacamole_user.entity_id = guacamole_entity.entity_id
|
|
||||||
WHERE
|
|
||||||
guacamole_entity.name = #{record.username,jdbcType=VARCHAR}
|
|
||||||
AND guacamole_entity.type = 'USER'),
|
|
||||||
username = #{record.username,jdbcType=VARCHAR},
|
|
||||||
start_date = #{record.startDate,jdbcType=TIMESTAMP},
|
|
||||||
end_date = #{record.endDate,jdbcType=TIMESTAMP}
|
|
||||||
WHERE history_id = #{record.recordID,jdbcType=INTEGER}
|
WHERE history_id = #{record.recordID,jdbcType=INTEGER}
|
||||||
</update>
|
</update>
|
||||||
|
|
||||||
@@ -97,6 +69,7 @@
|
|||||||
<select id="search" resultMap="UserRecordResultMap">
|
<select id="search" resultMap="UserRecordResultMap">
|
||||||
|
|
||||||
SELECT
|
SELECT
|
||||||
|
guacamole_user_history.history_id,
|
||||||
guacamole_user_history.remote_host,
|
guacamole_user_history.remote_host,
|
||||||
guacamole_user_history.user_id,
|
guacamole_user_history.user_id,
|
||||||
guacamole_user_history.username,
|
guacamole_user_history.username,
|
||||||
@@ -107,8 +80,8 @@
|
|||||||
<!-- Search terms -->
|
<!-- Search terms -->
|
||||||
<where>
|
<where>
|
||||||
|
|
||||||
<if test="username != null">
|
<if test="identifier != null">
|
||||||
guacamole_user_history.username = #{username,jdbcType=VARCHAR}
|
guacamole_user_history.username = #{identifier,jdbcType=VARCHAR}
|
||||||
</if>
|
</if>
|
||||||
|
|
||||||
<foreach collection="terms" item="term" open=" AND " separator=" AND ">
|
<foreach collection="terms" item="term" open=" AND " separator=" AND ">
|
||||||
@@ -153,6 +126,7 @@
|
|||||||
<select id="searchReadable" resultMap="UserRecordResultMap">
|
<select id="searchReadable" resultMap="UserRecordResultMap">
|
||||||
|
|
||||||
SELECT
|
SELECT
|
||||||
|
guacamole_user_history.history_id,
|
||||||
guacamole_user_history.remote_host,
|
guacamole_user_history.remote_host,
|
||||||
guacamole_user_history.user_id,
|
guacamole_user_history.user_id,
|
||||||
guacamole_user_history.username,
|
guacamole_user_history.username,
|
||||||
@@ -171,8 +145,8 @@
|
|||||||
</include>
|
</include>
|
||||||
)
|
)
|
||||||
|
|
||||||
<if test="username != null">
|
<if test="identifier != null">
|
||||||
AND guacamole_entity.name = #{username,jdbcType=VARCHAR}
|
AND guacamole_entity.name = #{identifier,jdbcType=VARCHAR}
|
||||||
</if>
|
</if>
|
||||||
|
|
||||||
<foreach collection="terms" item="term" open=" AND " separator=" AND ">
|
<foreach collection="terms" item="term" open=" AND " separator=" AND ">
|
||||||
|
@@ -25,6 +25,7 @@
|
|||||||
|
|
||||||
<!-- Result mapper for system permissions -->
|
<!-- Result mapper for system permissions -->
|
||||||
<resultMap id="ConnectionRecordResultMap" type="org.apache.guacamole.auth.jdbc.connection.ConnectionRecordModel">
|
<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_id" property="connectionIdentifier" jdbcType="INTEGER"/>
|
||||||
<result column="connection_name" property="connectionName" jdbcType="VARCHAR"/>
|
<result column="connection_name" property="connectionName" jdbcType="VARCHAR"/>
|
||||||
<result column="remote_host" property="remoteHost" jdbcType="VARCHAR"/>
|
<result column="remote_host" property="remoteHost" jdbcType="VARCHAR"/>
|
||||||
@@ -36,30 +37,9 @@
|
|||||||
<result column="end_date" property="endDate" jdbcType="TIMESTAMP"/>
|
<result column="end_date" property="endDate" jdbcType="TIMESTAMP"/>
|
||||||
</resultMap>
|
</resultMap>
|
||||||
|
|
||||||
<!-- Select all connection records from a given connection -->
|
|
||||||
<select id="select" resultMap="ConnectionRecordResultMap">
|
|
||||||
|
|
||||||
SELECT
|
|
||||||
guacamole_connection_history.connection_id,
|
|
||||||
guacamole_connection_history.connection_name,
|
|
||||||
guacamole_connection_history.remote_host,
|
|
||||||
guacamole_connection_history.sharing_profile_id,
|
|
||||||
guacamole_connection_history.sharing_profile_name,
|
|
||||||
guacamole_connection_history.user_id,
|
|
||||||
guacamole_connection_history.username,
|
|
||||||
guacamole_connection_history.start_date,
|
|
||||||
guacamole_connection_history.end_date
|
|
||||||
FROM guacamole_connection_history
|
|
||||||
WHERE
|
|
||||||
guacamole_connection_history.connection_id = #{identifier,jdbcType=INTEGER}::integer
|
|
||||||
ORDER BY
|
|
||||||
guacamole_connection_history.start_date DESC,
|
|
||||||
guacamole_connection_history.end_date DESC
|
|
||||||
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<!-- Insert the given connection record -->
|
<!-- Insert the given connection record -->
|
||||||
<insert id="insert" parameterType="org.apache.guacamole.auth.jdbc.connection.ConnectionRecordModel">
|
<insert id="insert" useGeneratedKeys="true" keyProperty="record.recordID"
|
||||||
|
parameterType="org.apache.guacamole.auth.jdbc.connection.ConnectionRecordModel">
|
||||||
|
|
||||||
INSERT INTO guacamole_connection_history (
|
INSERT INTO guacamole_connection_history (
|
||||||
connection_id,
|
connection_id,
|
||||||
@@ -90,10 +70,18 @@
|
|||||||
|
|
||||||
</insert>
|
</insert>
|
||||||
|
|
||||||
|
<!-- Update the given connection record, assigning an end date -->
|
||||||
|
<update id="updateEndDate" parameterType="org.apache.guacamole.auth.jdbc.connection.ConnectionRecordModel">
|
||||||
|
UPDATE guacamole_connection_history
|
||||||
|
SET end_date = #{record.endDate,jdbcType=TIMESTAMP}
|
||||||
|
WHERE history_id = #{record.recordID,jdbcType=INTEGER}::integer
|
||||||
|
</update>
|
||||||
|
|
||||||
<!-- Search for specific connection records -->
|
<!-- Search for specific connection records -->
|
||||||
<select id="search" resultMap="ConnectionRecordResultMap">
|
<select id="search" resultMap="ConnectionRecordResultMap">
|
||||||
|
|
||||||
SELECT
|
SELECT
|
||||||
|
guacamole_connection_history.history_id,
|
||||||
guacamole_connection_history.connection_id,
|
guacamole_connection_history.connection_id,
|
||||||
guacamole_connection_history.connection_name,
|
guacamole_connection_history.connection_name,
|
||||||
guacamole_connection_history.remote_host,
|
guacamole_connection_history.remote_host,
|
||||||
@@ -108,8 +96,12 @@
|
|||||||
<!-- Search terms -->
|
<!-- Search terms -->
|
||||||
<where>
|
<where>
|
||||||
|
|
||||||
|
<if test="recordIdentifier != null">
|
||||||
|
guacamole_connection_history.history_id = #{recordIdentifier,jdbcType=INTEGER}::integer
|
||||||
|
</if>
|
||||||
|
|
||||||
<if test="identifier != null">
|
<if test="identifier != null">
|
||||||
guacamole_connection_history.connection_id = #{identifier,jdbcType=INTEGER}::integer
|
AND guacamole_connection_history.connection_id = #{identifier,jdbcType=INTEGER}::integer
|
||||||
</if>
|
</if>
|
||||||
|
|
||||||
<foreach collection="terms" item="term" open=" AND " separator=" AND ">
|
<foreach collection="terms" item="term" open=" AND " separator=" AND ">
|
||||||
@@ -157,6 +149,7 @@
|
|||||||
<select id="searchReadable" resultMap="ConnectionRecordResultMap">
|
<select id="searchReadable" resultMap="ConnectionRecordResultMap">
|
||||||
|
|
||||||
SELECT
|
SELECT
|
||||||
|
guacamole_connection_history.history_id,
|
||||||
guacamole_connection_history.connection_id,
|
guacamole_connection_history.connection_id,
|
||||||
guacamole_connection_history.connection_name,
|
guacamole_connection_history.connection_name,
|
||||||
guacamole_connection_history.remote_host,
|
guacamole_connection_history.remote_host,
|
||||||
@@ -173,8 +166,12 @@
|
|||||||
<!-- Search terms -->
|
<!-- Search terms -->
|
||||||
<where>
|
<where>
|
||||||
|
|
||||||
|
<if test="recordIdentifier != null">
|
||||||
|
guacamole_connection_history.history_id = #{recordIdentifier,jdbcType=INTEGER}::integer
|
||||||
|
</if>
|
||||||
|
|
||||||
<!-- Restrict to readable connections -->
|
<!-- Restrict to readable connections -->
|
||||||
guacamole_connection_history.connection_id IN (
|
AND guacamole_connection_history.connection_id IN (
|
||||||
<include refid="org.apache.guacamole.auth.jdbc.connection.ConnectionMapper.getReadableIDs">
|
<include refid="org.apache.guacamole.auth.jdbc.connection.ConnectionMapper.getReadableIDs">
|
||||||
<property name="entityID" value="#{user.entityID,jdbcType=INTEGER}"/>
|
<property name="entityID" value="#{user.entityID,jdbcType=INTEGER}"/>
|
||||||
<property name="groups" value="effectiveGroups"/>
|
<property name="groups" value="effectiveGroups"/>
|
||||||
|
@@ -33,26 +33,6 @@
|
|||||||
<result column="end_date" property="endDate" jdbcType="TIMESTAMP"/>
|
<result column="end_date" property="endDate" jdbcType="TIMESTAMP"/>
|
||||||
</resultMap>
|
</resultMap>
|
||||||
|
|
||||||
<!-- Select all user records from a given user -->
|
|
||||||
<select id="select" resultMap="UserRecordResultMap">
|
|
||||||
|
|
||||||
SELECT
|
|
||||||
guacamole_user_history.remote_host,
|
|
||||||
guacamole_user_history.user_id,
|
|
||||||
guacamole_user_history.username,
|
|
||||||
guacamole_user_history.start_date,
|
|
||||||
guacamole_user_history.end_date
|
|
||||||
FROM guacamole_user_history
|
|
||||||
JOIN guacamole_user ON guacamole_user_history.user_id = guacamole_user.user_id
|
|
||||||
JOIN guacamole_entity ON guacamole_user.entity_id = guacamole_entity.entity_id
|
|
||||||
WHERE
|
|
||||||
guacamole_entity.name = #{username,jdbcType=VARCHAR}
|
|
||||||
ORDER BY
|
|
||||||
guacamole_user_history.start_date DESC,
|
|
||||||
guacamole_user_history.end_date DESC
|
|
||||||
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<!-- Insert the given user record -->
|
<!-- Insert the given user record -->
|
||||||
<insert id="insert" useGeneratedKeys="true" keyProperty="record.recordID"
|
<insert id="insert" useGeneratedKeys="true" keyProperty="record.recordID"
|
||||||
parameterType="org.apache.guacamole.auth.jdbc.base.ActivityRecordModel">
|
parameterType="org.apache.guacamole.auth.jdbc.base.ActivityRecordModel">
|
||||||
@@ -78,18 +58,10 @@
|
|||||||
|
|
||||||
</insert>
|
</insert>
|
||||||
|
|
||||||
<!-- Update the given user record -->
|
<!-- Update the given user record, assigning an end date -->
|
||||||
<update id="update" parameterType="org.apache.guacamole.auth.jdbc.base.ActivityRecordModel">
|
<update id="updateEndDate" parameterType="org.apache.guacamole.auth.jdbc.base.ActivityRecordModel">
|
||||||
UPDATE guacamole_user_history
|
UPDATE guacamole_user_history
|
||||||
SET remote_host = #{record.remoteHost,jdbcType=VARCHAR},
|
SET end_date = #{record.endDate,jdbcType=TIMESTAMP}
|
||||||
user_id = (SELECT user_id FROM guacamole_user
|
|
||||||
JOIN guacamole_entity ON guacamole_user.entity_id = guacamole_entity.entity_id
|
|
||||||
WHERE
|
|
||||||
guacamole_entity.name = #{record.username,jdbcType=VARCHAR}
|
|
||||||
AND guacamole_entity.type = 'USER'::guacamole_entity_type),
|
|
||||||
username = #{record.username,jdbcType=VARCHAR},
|
|
||||||
start_date = #{record.startDate,jdbcType=TIMESTAMP},
|
|
||||||
end_date = #{record.endDate,jdbcType=TIMESTAMP}
|
|
||||||
WHERE history_id = #{record.recordID,jdbcType=INTEGER}::integer
|
WHERE history_id = #{record.recordID,jdbcType=INTEGER}::integer
|
||||||
</update>
|
</update>
|
||||||
|
|
||||||
@@ -97,6 +69,7 @@
|
|||||||
<select id="search" resultMap="UserRecordResultMap">
|
<select id="search" resultMap="UserRecordResultMap">
|
||||||
|
|
||||||
SELECT
|
SELECT
|
||||||
|
guacamole_user_history.history_id,
|
||||||
guacamole_user_history.remote_host,
|
guacamole_user_history.remote_host,
|
||||||
guacamole_user_history.user_id,
|
guacamole_user_history.user_id,
|
||||||
guacamole_user_history.username,
|
guacamole_user_history.username,
|
||||||
@@ -107,8 +80,8 @@
|
|||||||
<!-- Search terms -->
|
<!-- Search terms -->
|
||||||
<where>
|
<where>
|
||||||
|
|
||||||
<if test="username != null">
|
<if test="identifier != null">
|
||||||
guacamole_user_history.username = #{username,jdbcType=VARCHAR}
|
guacamole_user_history.username = #{identifier,jdbcType=VARCHAR}
|
||||||
</if>
|
</if>
|
||||||
|
|
||||||
<foreach collection="terms" item="term" open=" AND " separator=" AND ">
|
<foreach collection="terms" item="term" open=" AND " separator=" AND ">
|
||||||
@@ -153,6 +126,7 @@
|
|||||||
<select id="searchReadable" resultMap="UserRecordResultMap">
|
<select id="searchReadable" resultMap="UserRecordResultMap">
|
||||||
|
|
||||||
SELECT
|
SELECT
|
||||||
|
guacamole_user_history.history_id,
|
||||||
guacamole_user_history.remote_host,
|
guacamole_user_history.remote_host,
|
||||||
guacamole_user_history.user_id,
|
guacamole_user_history.user_id,
|
||||||
guacamole_user_history.username,
|
guacamole_user_history.username,
|
||||||
@@ -171,8 +145,8 @@
|
|||||||
</include>
|
</include>
|
||||||
)
|
)
|
||||||
|
|
||||||
<if test="username != null">
|
<if test="identifier != null">
|
||||||
AND guacamole_entity.name = #{username,jdbcType=VARCHAR}
|
AND guacamole_entity.name = #{identifier,jdbcType=VARCHAR}
|
||||||
</if>
|
</if>
|
||||||
|
|
||||||
<foreach collection="terms" item="term" open=" AND " separator=" AND ">
|
<foreach collection="terms" item="term" open=" AND " separator=" AND ">
|
||||||
|
@@ -25,6 +25,7 @@
|
|||||||
|
|
||||||
<!-- Result mapper for system permissions -->
|
<!-- Result mapper for system permissions -->
|
||||||
<resultMap id="ConnectionRecordResultMap" type="org.apache.guacamole.auth.jdbc.connection.ConnectionRecordModel">
|
<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_id" property="connectionIdentifier" jdbcType="INTEGER"/>
|
||||||
<result column="connection_name" property="connectionName" jdbcType="VARCHAR"/>
|
<result column="connection_name" property="connectionName" jdbcType="VARCHAR"/>
|
||||||
<result column="remote_host" property="remoteHost" jdbcType="VARCHAR"/>
|
<result column="remote_host" property="remoteHost" jdbcType="VARCHAR"/>
|
||||||
@@ -36,30 +37,16 @@
|
|||||||
<result column="end_date" property="endDate" jdbcType="TIMESTAMP"/>
|
<result column="end_date" property="endDate" jdbcType="TIMESTAMP"/>
|
||||||
</resultMap>
|
</resultMap>
|
||||||
|
|
||||||
<!-- Select all connection records from a given connection -->
|
<!-- Update the given connection record, assigning an end date -->
|
||||||
<select id="select" resultMap="ConnectionRecordResultMap">
|
<update id="updateEndDate" parameterType="org.apache.guacamole.auth.jdbc.connection.ConnectionRecordModel">
|
||||||
|
UPDATE [guacamole_connection_history]
|
||||||
SELECT
|
SET end_date = #{record.endDate,jdbcType=TIMESTAMP}
|
||||||
[guacamole_connection_history].connection_id,
|
WHERE history_id = #{record.recordID,jdbcType=INTEGER}
|
||||||
[guacamole_connection_history].connection_name,
|
</update>
|
||||||
[guacamole_connection_history].remote_host,
|
|
||||||
[guacamole_connection_history].sharing_profile_id,
|
|
||||||
[guacamole_connection_history].sharing_profile_name,
|
|
||||||
[guacamole_connection_history].user_id,
|
|
||||||
[guacamole_connection_history].username,
|
|
||||||
[guacamole_connection_history].start_date,
|
|
||||||
[guacamole_connection_history].end_date
|
|
||||||
FROM [guacamole_connection_history]
|
|
||||||
WHERE
|
|
||||||
[guacamole_connection_history].connection_id = #{identifier,jdbcType=INTEGER}
|
|
||||||
ORDER BY
|
|
||||||
[guacamole_connection_history].start_date DESC,
|
|
||||||
[guacamole_connection_history].end_date DESC
|
|
||||||
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<!-- Insert the given connection record -->
|
<!-- Insert the given connection record -->
|
||||||
<insert id="insert" parameterType="org.apache.guacamole.auth.jdbc.connection.ConnectionRecordModel">
|
<insert id="insert" useGeneratedKeys="true" keyProperty="record.recordID"
|
||||||
|
parameterType="org.apache.guacamole.auth.jdbc.connection.ConnectionRecordModel">
|
||||||
|
|
||||||
INSERT INTO [guacamole_connection_history] (
|
INSERT INTO [guacamole_connection_history] (
|
||||||
connection_id,
|
connection_id,
|
||||||
@@ -94,6 +81,7 @@
|
|||||||
<select id="search" resultMap="ConnectionRecordResultMap">
|
<select id="search" resultMap="ConnectionRecordResultMap">
|
||||||
|
|
||||||
SELECT TOP (#{limit,jdbcType=INTEGER})
|
SELECT TOP (#{limit,jdbcType=INTEGER})
|
||||||
|
[guacamole_connection_history].history_id,
|
||||||
[guacamole_connection_history].connection_id,
|
[guacamole_connection_history].connection_id,
|
||||||
[guacamole_connection_history].connection_name,
|
[guacamole_connection_history].connection_name,
|
||||||
[guacamole_connection_history].remote_host,
|
[guacamole_connection_history].remote_host,
|
||||||
@@ -108,8 +96,12 @@
|
|||||||
<!-- Search terms -->
|
<!-- Search terms -->
|
||||||
<where>
|
<where>
|
||||||
|
|
||||||
|
<if test="recordIdentifier != null">
|
||||||
|
[guacamole_connection_history].history_id = #{recordIdentifier,jdbcType=INTEGER}
|
||||||
|
</if>
|
||||||
|
|
||||||
<if test="identifier != null">
|
<if test="identifier != null">
|
||||||
[guacamole_connection_history].connection_id = #{identifier,jdbcType=INTEGER}
|
AND [guacamole_connection_history].connection_id = #{identifier,jdbcType=INTEGER}
|
||||||
</if>
|
</if>
|
||||||
|
|
||||||
<foreach collection="terms" item="term" open=" AND " separator=" AND ">
|
<foreach collection="terms" item="term" open=" AND " separator=" AND ">
|
||||||
@@ -155,6 +147,7 @@
|
|||||||
<select id="searchReadable" resultMap="ConnectionRecordResultMap">
|
<select id="searchReadable" resultMap="ConnectionRecordResultMap">
|
||||||
|
|
||||||
SELECT TOP (#{limit,jdbcType=INTEGER})
|
SELECT TOP (#{limit,jdbcType=INTEGER})
|
||||||
|
[guacamole_connection_history].history_id,
|
||||||
[guacamole_connection_history].connection_id,
|
[guacamole_connection_history].connection_id,
|
||||||
[guacamole_connection_history].connection_name,
|
[guacamole_connection_history].connection_name,
|
||||||
[guacamole_connection_history].remote_host,
|
[guacamole_connection_history].remote_host,
|
||||||
@@ -171,8 +164,12 @@
|
|||||||
<!-- Search terms -->
|
<!-- Search terms -->
|
||||||
<where>
|
<where>
|
||||||
|
|
||||||
|
<if test="recordIdentifier != null">
|
||||||
|
[guacamole_connection_history].history_id = #{recordIdentifier,jdbcType=INTEGER}
|
||||||
|
</if>
|
||||||
|
|
||||||
<!-- Restrict to readable connections -->
|
<!-- Restrict to readable connections -->
|
||||||
[guacamole_connection_history].connection_id IN (
|
AND [guacamole_connection_history].connection_id IN (
|
||||||
<include refid="org.apache.guacamole.auth.jdbc.connection.ConnectionMapper.getReadableIDs">
|
<include refid="org.apache.guacamole.auth.jdbc.connection.ConnectionMapper.getReadableIDs">
|
||||||
<property name="entityID" value="#{user.entityID,jdbcType=INTEGER}"/>
|
<property name="entityID" value="#{user.entityID,jdbcType=INTEGER}"/>
|
||||||
<property name="groups" value="effectiveGroups"/>
|
<property name="groups" value="effectiveGroups"/>
|
||||||
|
@@ -33,26 +33,6 @@
|
|||||||
<result column="end_date" property="endDate" jdbcType="TIMESTAMP"/>
|
<result column="end_date" property="endDate" jdbcType="TIMESTAMP"/>
|
||||||
</resultMap>
|
</resultMap>
|
||||||
|
|
||||||
<!-- Select all user records from a given user -->
|
|
||||||
<select id="select" resultMap="UserRecordResultMap">
|
|
||||||
|
|
||||||
SELECT
|
|
||||||
[guacamole_user_history].remote_host,
|
|
||||||
[guacamole_user_history].user_id,
|
|
||||||
[guacamole_user_history].username,
|
|
||||||
[guacamole_user_history].start_date,
|
|
||||||
[guacamole_user_history].end_date
|
|
||||||
FROM [guacamole_user_history]
|
|
||||||
JOIN [guacamole_user] ON [guacamole_user_history].user_id = [guacamole_user].user_id
|
|
||||||
JOIN [guacamole_entity] ON [guacamole_user].entity_id = [guacamole_entity].entity_id
|
|
||||||
WHERE
|
|
||||||
[guacamole_entity].name = #{username,jdbcType=VARCHAR}
|
|
||||||
ORDER BY
|
|
||||||
[guacamole_user_history].start_date DESC,
|
|
||||||
[guacamole_user_history].end_date DESC
|
|
||||||
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<!-- Insert the given user record -->
|
<!-- Insert the given user record -->
|
||||||
<insert id="insert" useGeneratedKeys="true" keyProperty="record.recordID"
|
<insert id="insert" useGeneratedKeys="true" keyProperty="record.recordID"
|
||||||
parameterType="org.apache.guacamole.auth.jdbc.base.ActivityRecordModel">
|
parameterType="org.apache.guacamole.auth.jdbc.base.ActivityRecordModel">
|
||||||
@@ -78,18 +58,10 @@
|
|||||||
|
|
||||||
</insert>
|
</insert>
|
||||||
|
|
||||||
<!-- Update the given user record -->
|
<!-- Update the given user record, assigning an end date -->
|
||||||
<update id="update" parameterType="org.apache.guacamole.auth.jdbc.base.ActivityRecordModel">
|
<update id="updateEndDate" parameterType="org.apache.guacamole.auth.jdbc.base.ActivityRecordModel">
|
||||||
UPDATE [guacamole_user_history]
|
UPDATE [guacamole_user_history]
|
||||||
SET remote_host = #{record.remoteHost,jdbcType=VARCHAR},
|
SET end_date = #{record.endDate,jdbcType=TIMESTAMP}
|
||||||
user_id = (SELECT user_id FROM [guacamole_user]
|
|
||||||
JOIN [guacamole_entity] ON [guacamole_user].entity_id = [guacamole_entity].entity_id
|
|
||||||
WHERE
|
|
||||||
[guacamole_entity].name = #{record.username,jdbcType=VARCHAR}
|
|
||||||
AND [guacamole_entity].type = 'USER'),
|
|
||||||
username = #{record.username,jdbcType=VARCHAR},
|
|
||||||
start_date = #{record.startDate,jdbcType=TIMESTAMP},
|
|
||||||
end_date = #{record.endDate,jdbcType=TIMESTAMP}
|
|
||||||
WHERE history_id = #{record.recordID,jdbcType=INTEGER}
|
WHERE history_id = #{record.recordID,jdbcType=INTEGER}
|
||||||
</update>
|
</update>
|
||||||
|
|
||||||
@@ -97,6 +69,7 @@
|
|||||||
<select id="search" resultMap="UserRecordResultMap">
|
<select id="search" resultMap="UserRecordResultMap">
|
||||||
|
|
||||||
SELECT TOP (#{limit,jdbcType=INTEGER})
|
SELECT TOP (#{limit,jdbcType=INTEGER})
|
||||||
|
[guacamole_user_history].history_id,
|
||||||
[guacamole_user_history].remote_host,
|
[guacamole_user_history].remote_host,
|
||||||
[guacamole_user_history].user_id,
|
[guacamole_user_history].user_id,
|
||||||
[guacamole_user_history].username,
|
[guacamole_user_history].username,
|
||||||
@@ -107,8 +80,8 @@
|
|||||||
<!-- Search terms -->
|
<!-- Search terms -->
|
||||||
<where>
|
<where>
|
||||||
|
|
||||||
<if test="username != null">
|
<if test="identifier != null">
|
||||||
[guacamole_user_history].username = #{username,jdbcType=VARCHAR}
|
[guacamole_user_history].username = #{identifier,jdbcType=VARCHAR}
|
||||||
</if>
|
</if>
|
||||||
|
|
||||||
<foreach collection="terms" item="term" open=" AND " separator=" AND ">
|
<foreach collection="terms" item="term" open=" AND " separator=" AND ">
|
||||||
@@ -151,6 +124,7 @@
|
|||||||
<select id="searchReadable" resultMap="UserRecordResultMap">
|
<select id="searchReadable" resultMap="UserRecordResultMap">
|
||||||
|
|
||||||
SELECT TOP (#{limit,jdbcType=INTEGER})
|
SELECT TOP (#{limit,jdbcType=INTEGER})
|
||||||
|
[guacamole_user_history].history_id,
|
||||||
[guacamole_user_history].remote_host,
|
[guacamole_user_history].remote_host,
|
||||||
[guacamole_user_history].user_id,
|
[guacamole_user_history].user_id,
|
||||||
[guacamole_user_history].username,
|
[guacamole_user_history].username,
|
||||||
@@ -169,8 +143,8 @@
|
|||||||
</include>
|
</include>
|
||||||
)
|
)
|
||||||
|
|
||||||
<if test="username != null">
|
<if test="identifier != null">
|
||||||
AND [guacamole_entity].name = #{username,jdbcType=VARCHAR}
|
AND [guacamole_entity].name = #{identifier,jdbcType=VARCHAR}
|
||||||
</if>
|
</if>
|
||||||
|
|
||||||
<foreach collection="terms" item="term" open=" AND " separator=" AND ">
|
<foreach collection="terms" item="term" open=" AND " separator=" AND ">
|
||||||
|
3
extensions/guacamole-history-recording-storage/.gitignore
vendored
Normal file
3
extensions/guacamole-history-recording-storage/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
src/main/resources/generated/
|
||||||
|
target/
|
||||||
|
*~
|
52
extensions/guacamole-history-recording-storage/pom.xml
Normal file
52
extensions/guacamole-history-recording-storage/pom.xml
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
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.
|
||||||
|
-->
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
|
||||||
|
http://maven.apache.org/maven-v4_0_0.xsd">
|
||||||
|
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<groupId>org.apache.guacamole</groupId>
|
||||||
|
<artifactId>guacamole-history-recording-storage</artifactId>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
<version>1.4.0</version>
|
||||||
|
<name>guacamole-history-recording-storage</name>
|
||||||
|
<url>http://guacamole.apache.org/</url>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>org.apache.guacamole</groupId>
|
||||||
|
<artifactId>extensions</artifactId>
|
||||||
|
<version>1.4.0</version>
|
||||||
|
<relativePath>../</relativePath>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
|
||||||
|
<!-- Guacamole Extension API -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.guacamole</groupId>
|
||||||
|
<artifactId>guacamole-ext</artifactId>
|
||||||
|
<version>1.4.0</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
@@ -0,0 +1,53 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
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.
|
||||||
|
-->
|
||||||
|
<assembly
|
||||||
|
xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
|
||||||
|
|
||||||
|
<id>dist</id>
|
||||||
|
<baseDirectory>${project.artifactId}-${project.version}</baseDirectory>
|
||||||
|
|
||||||
|
<!-- Output tar.gz -->
|
||||||
|
<formats>
|
||||||
|
<format>tar.gz</format>
|
||||||
|
</formats>
|
||||||
|
|
||||||
|
<!-- Include licenses and extension .jar -->
|
||||||
|
<fileSets>
|
||||||
|
|
||||||
|
<!-- Include licenses -->
|
||||||
|
<fileSet>
|
||||||
|
<outputDirectory></outputDirectory>
|
||||||
|
<directory>target/licenses</directory>
|
||||||
|
</fileSet>
|
||||||
|
|
||||||
|
<!-- Include extension .jar -->
|
||||||
|
<fileSet>
|
||||||
|
<directory>target</directory>
|
||||||
|
<outputDirectory></outputDirectory>
|
||||||
|
<includes>
|
||||||
|
<include>*.jar</include>
|
||||||
|
</includes>
|
||||||
|
</fileSet>
|
||||||
|
|
||||||
|
</fileSets>
|
||||||
|
|
||||||
|
</assembly>
|
@@ -0,0 +1,89 @@
|
|||||||
|
/*
|
||||||
|
* 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.history;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import org.apache.guacamole.history.user.HistoryUserContext;
|
||||||
|
import org.apache.guacamole.GuacamoleException;
|
||||||
|
import org.apache.guacamole.environment.Environment;
|
||||||
|
import org.apache.guacamole.environment.LocalEnvironment;
|
||||||
|
import org.apache.guacamole.net.auth.AbstractAuthenticationProvider;
|
||||||
|
import org.apache.guacamole.net.auth.AuthenticatedUser;
|
||||||
|
import org.apache.guacamole.net.auth.Credentials;
|
||||||
|
import org.apache.guacamole.net.auth.UserContext;
|
||||||
|
import org.apache.guacamole.properties.FileGuacamoleProperty;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AuthenticationProvider implementation which automatically associates history
|
||||||
|
* entries with session recordings, typescripts, etc. History association is
|
||||||
|
* determined by matching the history entry UUID with the filenames of files
|
||||||
|
* located within a standardized/configurable directory.
|
||||||
|
*/
|
||||||
|
public class HistoryAuthenticationProvider extends AbstractAuthenticationProvider {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default directory to search for associated session recordings, if
|
||||||
|
* not overridden with the "recording-search-path" property.
|
||||||
|
*/
|
||||||
|
private static final File DEFAULT_RECORDING_SEARCH_PATH = new File("/var/lib/guacamole/recordings");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The directory to search for associated session recordings. By default,
|
||||||
|
* "/var/lib/guacamole/recordings" will be used.
|
||||||
|
*/
|
||||||
|
private static final FileGuacamoleProperty RECORDING_SEARCH_PATH = new FileGuacamoleProperty() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "recording-search-path";
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the directory that should be searched for session recordings
|
||||||
|
* associated with history entries.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* The directory that should be searched for session recordings
|
||||||
|
* associated with history entries.
|
||||||
|
*
|
||||||
|
* @throws GuacamoleException
|
||||||
|
* If the "recording-search-path" property cannot be parsed.
|
||||||
|
*/
|
||||||
|
public static File getRecordingSearchPath() throws GuacamoleException {
|
||||||
|
Environment environment = LocalEnvironment.getInstance();
|
||||||
|
return environment.getProperty(RECORDING_SEARCH_PATH,
|
||||||
|
DEFAULT_RECORDING_SEARCH_PATH);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getIdentifier() {
|
||||||
|
return "recording-storage";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UserContext decorate(UserContext context,
|
||||||
|
AuthenticatedUser authenticatedUser, Credentials credentials)
|
||||||
|
throws GuacamoleException {
|
||||||
|
return new HistoryUserContext(context.self(), context);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* 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.history.connection;
|
||||||
|
|
||||||
|
import org.apache.guacamole.GuacamoleException;
|
||||||
|
import org.apache.guacamole.net.auth.ActivityRecordSet;
|
||||||
|
import org.apache.guacamole.net.auth.Connection;
|
||||||
|
import org.apache.guacamole.net.auth.ConnectionRecord;
|
||||||
|
import org.apache.guacamole.net.auth.DelegatingConnection;
|
||||||
|
import org.apache.guacamole.net.auth.User;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connection implementation that automatically defines ActivityLogs for
|
||||||
|
* files that relate to history entries associated with the wrapped connection.
|
||||||
|
*/
|
||||||
|
public class HistoryConnection extends DelegatingConnection {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current Guacamole user.
|
||||||
|
*/
|
||||||
|
private final User currentUser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new HistoryConnection that wraps the given connection,
|
||||||
|
* automatically associating history entries with ActivityLogs based on
|
||||||
|
* related files (session recordings, typescripts, etc.).
|
||||||
|
*
|
||||||
|
* @param currentUser
|
||||||
|
* The current Guacamole user.
|
||||||
|
*
|
||||||
|
* @param connection
|
||||||
|
* The connection to wrap.
|
||||||
|
*/
|
||||||
|
public HistoryConnection(User currentUser, Connection connection) {
|
||||||
|
super(connection);
|
||||||
|
this.currentUser = currentUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the connection wrapped by this HistoryConnection.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* The connection wrapped by this HistoryConnection.
|
||||||
|
*/
|
||||||
|
public Connection getWrappedConnection() {
|
||||||
|
return getDelegateConnection();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ActivityRecordSet<ConnectionRecord> getConnectionHistory() throws GuacamoleException {
|
||||||
|
return new RecordedConnectionActivityRecordSet(currentUser, super.getConnectionHistory());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,284 @@
|
|||||||
|
/*
|
||||||
|
* 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.history.connection;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.Reader;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
import org.apache.guacamole.GuacamoleException;
|
||||||
|
import org.apache.guacamole.history.HistoryAuthenticationProvider;
|
||||||
|
import org.apache.guacamole.io.GuacamoleReader;
|
||||||
|
import org.apache.guacamole.io.ReaderGuacamoleReader;
|
||||||
|
import org.apache.guacamole.language.TranslatableMessage;
|
||||||
|
import org.apache.guacamole.net.auth.ActivityLog;
|
||||||
|
import org.apache.guacamole.net.auth.ConnectionRecord;
|
||||||
|
import org.apache.guacamole.net.auth.DelegatingConnectionRecord;
|
||||||
|
import org.apache.guacamole.net.auth.FileActivityLog;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ConnectionRecord implementation that automatically defines ActivityLogs for
|
||||||
|
* files that relate to the wrapped record.
|
||||||
|
*/
|
||||||
|
public class HistoryConnectionRecord extends DelegatingConnectionRecord {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logger for this class.
|
||||||
|
*/
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(HistoryConnectionRecord.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The namespace for URL UUIDs as defined by RFC 4122.
|
||||||
|
*/
|
||||||
|
private static final UUID UUID_NAMESPACE_URL = UUID.fromString("6ba7b811-9dad-11d1-80b4-00c04fd430c8");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The filename suffix of typescript timing files.
|
||||||
|
*/
|
||||||
|
private static final String TIMING_FILE_SUFFIX = ".timing";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The recording file associated with the wrapped connection record. This
|
||||||
|
* may be a single file or a directory that may contain any number of
|
||||||
|
* relevant recordings.
|
||||||
|
*/
|
||||||
|
private final File recording;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new HistoryConnectionRecord that wraps the given
|
||||||
|
* ConnectionRecord, automatically associating ActivityLogs based on
|
||||||
|
* related files (session recordings, typescripts, etc.).
|
||||||
|
*
|
||||||
|
* @param record
|
||||||
|
* The ConnectionRecord to wrap.
|
||||||
|
*
|
||||||
|
* @throws GuacamoleException
|
||||||
|
* If the configured path for stored recordings cannot be read.
|
||||||
|
*/
|
||||||
|
public HistoryConnectionRecord(ConnectionRecord record) throws GuacamoleException {
|
||||||
|
super(record);
|
||||||
|
|
||||||
|
String uuid = record.getUUID().toString();
|
||||||
|
File recordingFile = new File(HistoryAuthenticationProvider.getRecordingSearchPath(), uuid);
|
||||||
|
this.recording = recordingFile.canRead() ? recordingFile : null;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the given file appears to be a Guacamole session
|
||||||
|
* recording. As there is no standard extension for session recordings,
|
||||||
|
* this is determined by attempting to read a single Guacamole instruction
|
||||||
|
* from the file.
|
||||||
|
*
|
||||||
|
* @param file
|
||||||
|
* The file to test.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* true if the file appears to be a Guacamole session recording, false
|
||||||
|
* otherwise.
|
||||||
|
*/
|
||||||
|
private boolean isSessionRecording(File file) {
|
||||||
|
|
||||||
|
try (Reader reader = new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8)) {
|
||||||
|
|
||||||
|
GuacamoleReader guacReader = new ReaderGuacamoleReader(reader);
|
||||||
|
if (guacReader.readInstruction() != null)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (GuacamoleException e) {
|
||||||
|
logger.debug("File \"{}\" does not appear to be a session "
|
||||||
|
+ "recording, as it could not be parsed as Guacamole "
|
||||||
|
+ "protocol data.", file, e);
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
logger.warn("Possible session recording \"{}\" could not be "
|
||||||
|
+ "identified as it cannot be read: {}", file, e.getMessage());
|
||||||
|
logger.debug("Possible session recording \"{}\" could not be read.", file, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the given file appears to be a typescript (text
|
||||||
|
* recording of a terminal session). As there is no standard extension for
|
||||||
|
* session recordings, this is determined by testing whether there is an
|
||||||
|
* associated timing file. Guacamole will always include a timing file for
|
||||||
|
* its typescripts.
|
||||||
|
*
|
||||||
|
* @param file
|
||||||
|
* The file to test.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* true if the file appears to be a typescript, false otherwise.
|
||||||
|
*/
|
||||||
|
private boolean isTypescript(File file) {
|
||||||
|
return new File(file.getAbsolutePath() + TIMING_FILE_SUFFIX).exists();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the given file appears to be a typescript timing file.
|
||||||
|
* Typescript timing files have the standard extension ".timing".
|
||||||
|
*
|
||||||
|
* @param file
|
||||||
|
* The file to test.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* true if the file appears to be a typescript timing file, false
|
||||||
|
* otherwise.
|
||||||
|
*/
|
||||||
|
private boolean isTypescriptTiming(File file) {
|
||||||
|
return file.getName().endsWith(TIMING_FILE_SUFFIX);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the type of session recording or log contained within the given
|
||||||
|
* file by inspecting its name and contents.
|
||||||
|
*
|
||||||
|
* @param file
|
||||||
|
* The file to test.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* The type of session recording or log contained within the given
|
||||||
|
* file, or null if this cannot be determined.
|
||||||
|
*/
|
||||||
|
private ActivityLog.Type getType(File file) {
|
||||||
|
|
||||||
|
if (isSessionRecording(file))
|
||||||
|
return ActivityLog.Type.GUACAMOLE_SESSION_RECORDING;
|
||||||
|
|
||||||
|
if (isTypescript(file))
|
||||||
|
return ActivityLog.Type.TYPESCRIPT;
|
||||||
|
|
||||||
|
if (isTypescriptTiming(file))
|
||||||
|
return ActivityLog.Type.TYPESCRIPT_TIMING;
|
||||||
|
|
||||||
|
return ActivityLog.Type.SERVER_LOG;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new ActivityLog instance representing the session recording or
|
||||||
|
* log contained within the given file. If the type of recording/log cannot
|
||||||
|
* be determined, or if the file is unreadable, null is returned.
|
||||||
|
*
|
||||||
|
* @param file
|
||||||
|
* The file to produce an ActivityLog instance for.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* A new ActivityLog instance representing the recording/log contained
|
||||||
|
* within the given file, or null if the file is unreadable or cannot
|
||||||
|
* be identified.
|
||||||
|
*/
|
||||||
|
private ActivityLog getActivityLog(File file) {
|
||||||
|
|
||||||
|
// Verify file can actually be read
|
||||||
|
if (!file.canRead()) {
|
||||||
|
logger.warn("Ignoring file \"{}\" relevant to connection history "
|
||||||
|
+ "record as it cannot be read.", file);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine type of recording/log by inspecting file
|
||||||
|
ActivityLog.Type logType = getType(file);
|
||||||
|
if (logType == null) {
|
||||||
|
logger.warn("Recording/log type of \"{}\" cannot be determined.", file);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new FileActivityLog(
|
||||||
|
logType,
|
||||||
|
new TranslatableMessage("RECORDING_STORAGE.INFO_" + logType.name()),
|
||||||
|
file
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an ActivityLog instance representing the session recording or log
|
||||||
|
* contained within the given file to the given map of logs. If no
|
||||||
|
* ActivityLog can be produced for the given file (it is unreadable or
|
||||||
|
* cannot be identified), this function has no effect.
|
||||||
|
*
|
||||||
|
* @param logs
|
||||||
|
* The map of logs to add the ActivityLog to.
|
||||||
|
*
|
||||||
|
* @param file
|
||||||
|
* The file to produce an ActivityLog instance for.
|
||||||
|
*/
|
||||||
|
private void addActivityLog(Map<String, ActivityLog> logs, File file) {
|
||||||
|
|
||||||
|
ActivityLog log = getActivityLog(file);
|
||||||
|
if (log == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Convert file into deterministic name UUID within URL namespace
|
||||||
|
UUID fileUUID;
|
||||||
|
try {
|
||||||
|
byte[] urlBytes = file.toURI().toURL().toString().getBytes(StandardCharsets.UTF_8);
|
||||||
|
fileUUID = UUID.nameUUIDFromBytes(ByteBuffer.allocate(16 + urlBytes.length)
|
||||||
|
.putLong(UUID_NAMESPACE_URL.getMostSignificantBits())
|
||||||
|
.putLong(UUID_NAMESPACE_URL.getLeastSignificantBits())
|
||||||
|
.put(urlBytes)
|
||||||
|
.array());
|
||||||
|
}
|
||||||
|
catch (MalformedURLException e) {
|
||||||
|
logger.warn("Ignoring file \"{}\" as a unique URL and UUID for that file could not be generated: {}", e.getMessage());
|
||||||
|
logger.debug("URL for file \"{}\" could not be determined.", file, e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
logs.put(fileUUID.toString(), log);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, ActivityLog> getLogs() {
|
||||||
|
|
||||||
|
// Do nothing if there are no associated logs
|
||||||
|
if (recording == null)
|
||||||
|
return super.getLogs();
|
||||||
|
|
||||||
|
// Add associated log (or logs, if this is a directory)
|
||||||
|
Map<String, ActivityLog> logs = new HashMap<>(super.getLogs());
|
||||||
|
if (recording.isDirectory()) {
|
||||||
|
Arrays.asList(recording.listFiles()).stream()
|
||||||
|
.forEach((file) -> addActivityLog(logs, file));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
addActivityLog(logs, recording);
|
||||||
|
|
||||||
|
return logs;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,125 @@
|
|||||||
|
/*
|
||||||
|
* 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.history.connection;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Set;
|
||||||
|
import org.apache.guacamole.GuacamoleException;
|
||||||
|
import org.apache.guacamole.net.auth.ActivityRecordSet;
|
||||||
|
import org.apache.guacamole.net.auth.ConnectionRecord;
|
||||||
|
import org.apache.guacamole.net.auth.DecoratingActivityRecordSet;
|
||||||
|
import org.apache.guacamole.net.auth.Permissions;
|
||||||
|
import org.apache.guacamole.net.auth.User;
|
||||||
|
import org.apache.guacamole.net.auth.permission.ObjectPermission;
|
||||||
|
import org.apache.guacamole.net.auth.permission.SystemPermission;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ActivityRecordSet implementation that automatically defines ActivityLogs for
|
||||||
|
* files that relate to history entries within the wrapped set.
|
||||||
|
*/
|
||||||
|
public class RecordedConnectionActivityRecordSet extends DecoratingActivityRecordSet<ConnectionRecord> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the current user is an administrator.
|
||||||
|
*/
|
||||||
|
private final boolean isAdmin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The overall set of connection permissions defined for the current user.
|
||||||
|
*/
|
||||||
|
private final Set<ObjectPermission> connectionPermissions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new RecordedConnectionActivityRecordSet that wraps the given
|
||||||
|
* ActivityRecordSet, automatically associating history entries with
|
||||||
|
* ActivityLogs based on related files (session recordings, typescripts,
|
||||||
|
* etc.).
|
||||||
|
*
|
||||||
|
* @param currentUser
|
||||||
|
* The current Guacamole user.
|
||||||
|
*
|
||||||
|
* @param activityRecordSet
|
||||||
|
* The ActivityRecordSet to wrap.
|
||||||
|
*
|
||||||
|
* @throws GuacamoleException
|
||||||
|
* If the permissions for the current user cannot be retrieved.
|
||||||
|
*/
|
||||||
|
public RecordedConnectionActivityRecordSet(User currentUser,
|
||||||
|
ActivityRecordSet<ConnectionRecord> activityRecordSet)
|
||||||
|
throws GuacamoleException {
|
||||||
|
super(activityRecordSet);
|
||||||
|
|
||||||
|
// Determine whether current user is an administrator
|
||||||
|
Permissions perms = currentUser.getEffectivePermissions();
|
||||||
|
isAdmin = perms.getSystemPermissions().hasPermission(SystemPermission.Type.ADMINISTER);
|
||||||
|
|
||||||
|
// If not an admin, additionally pull specific connection permissions
|
||||||
|
if (isAdmin)
|
||||||
|
connectionPermissions = Collections.emptySet();
|
||||||
|
else
|
||||||
|
connectionPermissions = perms.getConnectionPermissions().getPermissions();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the current user has permission to view the logs
|
||||||
|
* associated with the given history record. It is already given that the
|
||||||
|
* user has permission to view the history record itself. This extension
|
||||||
|
* considers a user to have permission to view history logs if they are
|
||||||
|
* an administrator or if they have permission to edit the associated
|
||||||
|
* connection.
|
||||||
|
*
|
||||||
|
* @param record
|
||||||
|
* The record to check.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* true if the current user has permission to view the logs associated
|
||||||
|
* with the given record, false otherwise.
|
||||||
|
*/
|
||||||
|
private boolean canViewLogs(ConnectionRecord record) {
|
||||||
|
|
||||||
|
// Administrator can always view
|
||||||
|
if (isAdmin)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Non-administrator CANNOT view if permissions cannot be verified
|
||||||
|
String identifier = record.getConnectionIdentifier();
|
||||||
|
if (identifier == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Non-administer can only view if they implicitly have permission to
|
||||||
|
// configure recordings (they have permission to edit)
|
||||||
|
ObjectPermission canUpdate = new ObjectPermission(ObjectPermission.Type.UPDATE, identifier);
|
||||||
|
return connectionPermissions.contains(canUpdate);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ConnectionRecord decorate(ConnectionRecord record) throws GuacamoleException {
|
||||||
|
|
||||||
|
// Provide access to logs only if permission is granted
|
||||||
|
if (canViewLogs(record))
|
||||||
|
return new HistoryConnectionRecord(record);
|
||||||
|
|
||||||
|
return record;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,122 @@
|
|||||||
|
/*
|
||||||
|
* 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.history.user;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.apache.guacamole.GuacamoleException;
|
||||||
|
import org.apache.guacamole.history.HistoryAuthenticationProvider;
|
||||||
|
import org.apache.guacamole.history.connection.HistoryConnection;
|
||||||
|
import org.apache.guacamole.history.connection.RecordedConnectionActivityRecordSet;
|
||||||
|
import org.apache.guacamole.net.auth.ActivityRecordSet;
|
||||||
|
import org.apache.guacamole.net.auth.Connection;
|
||||||
|
import org.apache.guacamole.net.auth.ConnectionGroup;
|
||||||
|
import org.apache.guacamole.net.auth.ConnectionRecord;
|
||||||
|
import org.apache.guacamole.net.auth.DecoratingDirectory;
|
||||||
|
import org.apache.guacamole.net.auth.Directory;
|
||||||
|
import org.apache.guacamole.net.auth.TokenInjectingUserContext;
|
||||||
|
import org.apache.guacamole.net.auth.User;
|
||||||
|
import org.apache.guacamole.net.auth.UserContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UserContext implementation that automatically defines ActivityLogs for
|
||||||
|
* files that relate to history entries.
|
||||||
|
*/
|
||||||
|
public class HistoryUserContext extends TokenInjectingUserContext {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the parameter token that contains the automatically-searched
|
||||||
|
* history recording/log path.
|
||||||
|
*/
|
||||||
|
private static final String HISTORY_PATH_TOKEN_NAME = "HISTORY_PATH";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current Guacamole user.
|
||||||
|
*/
|
||||||
|
private final User currentUser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new HistoryUserContext that wraps the given UserContext,
|
||||||
|
* automatically associating history entries with ActivityLogs based on
|
||||||
|
* related files (session recordings, typescripts, etc.).
|
||||||
|
*
|
||||||
|
* @param currentUser
|
||||||
|
* The current Guacamole user.
|
||||||
|
*
|
||||||
|
* @param context
|
||||||
|
* The UserContext to wrap.
|
||||||
|
*/
|
||||||
|
public HistoryUserContext(User currentUser, UserContext context) {
|
||||||
|
super(context);
|
||||||
|
this.currentUser = currentUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the tokens which should be added to an in-progress call to
|
||||||
|
* connect() for any Connectable object.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* The tokens which should be added to the in-progress call to
|
||||||
|
* connect().
|
||||||
|
*
|
||||||
|
* @throws GuacamoleException
|
||||||
|
* If the relevant tokens cannot be generated.
|
||||||
|
*/
|
||||||
|
private Map<String, String> getTokens() throws GuacamoleException {
|
||||||
|
return Collections.singletonMap(HISTORY_PATH_TOKEN_NAME,
|
||||||
|
HistoryAuthenticationProvider.getRecordingSearchPath().getAbsolutePath());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Map<String, String> getTokens(ConnectionGroup connectionGroup)
|
||||||
|
throws GuacamoleException {
|
||||||
|
return getTokens();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Map<String, String> getTokens(Connection connection)
|
||||||
|
throws GuacamoleException {
|
||||||
|
return getTokens();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Directory<Connection> getConnectionDirectory() throws GuacamoleException {
|
||||||
|
return new DecoratingDirectory<Connection>(super.getConnectionDirectory()) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Connection decorate(Connection object) {
|
||||||
|
return new HistoryConnection(currentUser, object);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Connection undecorate(Connection object) throws GuacamoleException {
|
||||||
|
return ((HistoryConnection) object).getWrappedConnection();
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ActivityRecordSet<ConnectionRecord> getConnectionHistory()
|
||||||
|
throws GuacamoleException {
|
||||||
|
return new RecordedConnectionActivityRecordSet(currentUser, super.getConnectionHistory());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
|
||||||
|
"guacamoleVersion" : "1.4.0",
|
||||||
|
|
||||||
|
"name" : "Session Recording Storage",
|
||||||
|
"namespace" : "recording-storage",
|
||||||
|
|
||||||
|
"authProviders" : [
|
||||||
|
"org.apache.guacamole.history.HistoryAuthenticationProvider"
|
||||||
|
],
|
||||||
|
|
||||||
|
"translations" : [
|
||||||
|
"translations/en.json"
|
||||||
|
]
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
|
||||||
|
"DATA_SOURCE_RECORDING_STORAGE" : {
|
||||||
|
"NAME" : "Session Recording Storage"
|
||||||
|
},
|
||||||
|
|
||||||
|
"RECORDING_STORAGE" : {
|
||||||
|
"INFO_GUACAMOLE_SESSION_RECORDING" : "Graphical recording of remote desktop session",
|
||||||
|
"INFO_SERVER_LOG" : "Server/system log",
|
||||||
|
"INFO_TYPESCRIPT" : "Text recording of terminal session",
|
||||||
|
"INFO_TYPESCRIPT_TIMING" : "Timing information for text recording of terminal session"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -50,6 +50,7 @@
|
|||||||
<module>guacamole-auth-totp</module>
|
<module>guacamole-auth-totp</module>
|
||||||
|
|
||||||
<!-- Additional features -->
|
<!-- Additional features -->
|
||||||
|
<module>guacamole-history-recording-storage</module>
|
||||||
<module>guacamole-vault</module>
|
<module>guacamole-vault</module>
|
||||||
|
|
||||||
</modules>
|
</modules>
|
||||||
|
@@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* 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 org.apache.guacamole.language.TranslatableMessage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base implementation of an ActivityLog, providing storage and simple
|
||||||
|
* getters/setters for its main properties.
|
||||||
|
*/
|
||||||
|
public abstract class AbstractActivityLog implements ActivityLog {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of this ActivityLog.
|
||||||
|
*/
|
||||||
|
private final Type type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A human-readable description of this log.
|
||||||
|
*/
|
||||||
|
private final TranslatableMessage description;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new AbstractActivityLog having the given type and
|
||||||
|
* human-readable description.
|
||||||
|
*
|
||||||
|
* @param type
|
||||||
|
* The type of this ActivityLog.
|
||||||
|
*
|
||||||
|
* @param description
|
||||||
|
* A human-readable message that describes this log.
|
||||||
|
*/
|
||||||
|
public AbstractActivityLog(Type type, TranslatableMessage description) {
|
||||||
|
this.type = type;
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TranslatableMessage getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,142 @@
|
|||||||
|
/*
|
||||||
|
* 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.io.InputStream;
|
||||||
|
import org.apache.guacamole.GuacamoleException;
|
||||||
|
import org.apache.guacamole.language.TranslatableMessage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An arbitrary log of an activity whose content may be exposed to a user with
|
||||||
|
* sufficient privileges. Types of content that might be exposed in this way
|
||||||
|
* include textual server logs, Guacamole session recordings, and typescripts.
|
||||||
|
*/
|
||||||
|
public interface ActivityLog {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The value returned by {@link #getSize()} if the number of available
|
||||||
|
* bytes within {@link #getContent()} is unknown.
|
||||||
|
*/
|
||||||
|
public static final long UNKNOWN_SIZE = -1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All possible types of {@link ActivityLog}.
|
||||||
|
*/
|
||||||
|
enum Type {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Guacamole session recording in the form of a Guacamole protocol
|
||||||
|
* dump.
|
||||||
|
*/
|
||||||
|
GUACAMOLE_SESSION_RECORDING("application/octet-stream"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A text log from a server-side process, such as the Guacamole web
|
||||||
|
* application or guacd.
|
||||||
|
*/
|
||||||
|
SERVER_LOG("text/plain"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A text session recording in the form of a standard typescript.
|
||||||
|
*/
|
||||||
|
TYPESCRIPT("application/octet-stream"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The timing file related to a typescript.
|
||||||
|
*/
|
||||||
|
TYPESCRIPT_TIMING("text/plain");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The MIME type of the content of an activity log of this type.
|
||||||
|
*/
|
||||||
|
private final String contentType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new Type that may be associated with content having the
|
||||||
|
* given MIME type.
|
||||||
|
*
|
||||||
|
* @param contentType
|
||||||
|
* The MIME type of the content of an activity log of this type.
|
||||||
|
*/
|
||||||
|
Type(String contentType) {
|
||||||
|
this.contentType = contentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the MIME type of the content of an activity log of this
|
||||||
|
* type, as might be sent via the HTTP "Content-Type" header.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* The MIME type of the content of an activity log of this type.
|
||||||
|
*/
|
||||||
|
public String getContentType() {
|
||||||
|
return contentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the type of this activity log. The type of an activity log
|
||||||
|
* dictates how its content should be interpreted or exposed.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* The type of this activity log.
|
||||||
|
*/
|
||||||
|
Type getType();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a human-readable message that describes this log. This message
|
||||||
|
* should provide sufficient information for a user with access to this
|
||||||
|
* log to understand its context and/or purpose.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* A human-readable message that describes this log.
|
||||||
|
*/
|
||||||
|
TranslatableMessage getDescription();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of bytes available for reading within the content of
|
||||||
|
* this log. If this value is unknown, -1 ({@link #UNKNOWN_SIZE}) should be
|
||||||
|
* returned.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* The number of bytes available for reading within the content of
|
||||||
|
* this log, or -1 ({@link #UNKNOWN_SIZE}) if this value is unknown.
|
||||||
|
*
|
||||||
|
* @throws GuacamoleException
|
||||||
|
* If the size of the content of this log cannot be determined due to
|
||||||
|
* an error.
|
||||||
|
*/
|
||||||
|
long getSize() throws GuacamoleException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an InputStream that allows the content of this log to be read.
|
||||||
|
* Multiple instances of this InputStream may be open at any given time. It
|
||||||
|
* is the responsibility of the caller to close the returned InputStream.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* An InputStream that allows the content of this log to be read.
|
||||||
|
*
|
||||||
|
* @throws GuacamoleException
|
||||||
|
* If the content of this log cannot be read due to an error.
|
||||||
|
*/
|
||||||
|
InputStream getContent() throws GuacamoleException;
|
||||||
|
|
||||||
|
}
|
@@ -19,13 +19,16 @@
|
|||||||
|
|
||||||
package org.apache.guacamole.net.auth;
|
package org.apache.guacamole.net.auth;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A logging record describing when a user started and ended a particular
|
* A logging record describing when a user started and ended a particular
|
||||||
* activity.
|
* activity.
|
||||||
*/
|
*/
|
||||||
public interface ActivityRecord {
|
public interface ActivityRecord extends ReadableAttributes {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the date and time the activity began.
|
* Returns the date and time the activity began.
|
||||||
@@ -75,4 +78,52 @@ public interface ActivityRecord {
|
|||||||
*/
|
*/
|
||||||
public boolean isActive();
|
public boolean isActive();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the unique identifier assigned to this record, if any. If this
|
||||||
|
* record is not uniquely identifiable, this may be null. If provided, this
|
||||||
|
* unique identifier MUST be unique across all {@link ActivityRecord}
|
||||||
|
* objects within the same {@link ActivityRecordSet}.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* The unique identifier assigned to this record, or null if this
|
||||||
|
* record has no such identifier.
|
||||||
|
*/
|
||||||
|
public default String getIdentifier() {
|
||||||
|
UUID uuid = getUUID();
|
||||||
|
return uuid != null ? uuid.toString() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a UUID that uniquely identifies this record. If provided, this
|
||||||
|
* UUID MUST be deterministic and unique across all {@link ActivityRecord}
|
||||||
|
* objects within the same {@link ActivityRecordSet}, and SHOULD be unique
|
||||||
|
* across all {@link ActivityRecord} objects.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* A UUID that uniquely identifies this record, or null if no such
|
||||||
|
* unique identifier exists.
|
||||||
|
*/
|
||||||
|
public default UUID getUUID() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a Map of logs related to this record and accessible by the
|
||||||
|
* current user, such as Guacamole session recordings. Each log is
|
||||||
|
* associated with a corresponding, arbitrary, unique name. If the user
|
||||||
|
* does not have access to any logs, or if no logs are available, this may
|
||||||
|
* be an empty map.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* A Map of logs related to this record.
|
||||||
|
*/
|
||||||
|
public default Map<String, ActivityLog> getLogs() {
|
||||||
|
return Collections.emptyMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public default Map<String, String> getAttributes() {
|
||||||
|
return Collections.emptyMap();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -57,6 +57,30 @@ public interface ActivityRecordSet<RecordType extends ActivityRecord> {
|
|||||||
*/
|
*/
|
||||||
Collection<RecordType> asCollection() throws GuacamoleException;
|
Collection<RecordType> asCollection() throws GuacamoleException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the record having the given unique identifier, if records within
|
||||||
|
* this set have unique identifiers. If records within this set do not have
|
||||||
|
* defined unique identifiers, this function has no effect.
|
||||||
|
*
|
||||||
|
* @param identifier
|
||||||
|
* The unique identifier of the record to retrieve.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* The record having the given unique identifier, or null if there is
|
||||||
|
* no such record.
|
||||||
|
*
|
||||||
|
* @throws GuacamoleException
|
||||||
|
* If an error occurs while retrieving the record.
|
||||||
|
*/
|
||||||
|
default RecordType get(String identifier) throws GuacamoleException {
|
||||||
|
return asCollection().stream()
|
||||||
|
.filter((record) -> {
|
||||||
|
String recordIdentifier = record.getIdentifier();
|
||||||
|
return recordIdentifier != null && recordIdentifier.equals(identifier);
|
||||||
|
})
|
||||||
|
.findFirst().orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the subset of records which contain the given value. The
|
* Returns the subset of records which contain the given value. The
|
||||||
* properties and semantics involved with determining whether a particular
|
* properties and semantics involved with determining whether a particular
|
||||||
|
@@ -22,21 +22,10 @@ package org.apache.guacamole.net.auth;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An object which is associated with a set of arbitrary attributes, defined
|
* An object which is associated with a set of arbitrary attributes that may
|
||||||
* as name/value pairs.
|
* be modifiable, defined as name/value pairs.
|
||||||
*/
|
*/
|
||||||
public interface Attributes {
|
public interface Attributes extends ReadableAttributes {
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns all attributes associated with this object. The returned map
|
|
||||||
* may not be modifiable.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* A map of all attribute identifiers to their corresponding values,
|
|
||||||
* for all attributes associated with this object, which may not be
|
|
||||||
* modifiable.
|
|
||||||
*/
|
|
||||||
Map<String, String> getAttributes();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the given attributes. If an attribute within the map is not
|
* Sets the given attributes. If an attribute within the map is not
|
||||||
|
@@ -19,6 +19,9 @@
|
|||||||
|
|
||||||
package org.apache.guacamole.net.auth;
|
package org.apache.guacamole.net.auth;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
import org.apache.guacamole.net.GuacamoleTunnel;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A logging record describing when a user started and ended usage of a
|
* A logging record describing when a user started and ended usage of a
|
||||||
* particular connection.
|
* particular connection.
|
||||||
@@ -70,4 +73,15 @@ public interface ConnectionRecord extends ActivityRecord {
|
|||||||
*/
|
*/
|
||||||
public String getSharingProfileName();
|
public String getSharingProfileName();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
* <p>If implemented, this UUID SHOULD be identical to the UUID of the
|
||||||
|
* {@link GuacamoleTunnel} originally returned when the connection was
|
||||||
|
* established to allow extensions and/or the web application to
|
||||||
|
* automatically associate connection information with corresponding
|
||||||
|
* history records, such as log messages and session recordings.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public UUID getUUID();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,165 @@
|
|||||||
|
/*
|
||||||
|
* 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.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import org.apache.guacamole.GuacamoleException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ActivityRecordSet implementation which simplifies decorating the records
|
||||||
|
* within an underlying ActivityRecordSet. The decorate() function must be
|
||||||
|
* implemented to define how each record is decorated. As ActivityRecordSets
|
||||||
|
* are read-only, there is no need to define an undecorate() function as
|
||||||
|
* required by {@link DecoratingDirectory}.
|
||||||
|
*
|
||||||
|
* @param <RecordType>
|
||||||
|
* The type of records stored within this ActivityRecordSet.
|
||||||
|
*/
|
||||||
|
public abstract class DecoratingActivityRecordSet<RecordType extends ActivityRecord>
|
||||||
|
extends DelegatingActivityRecordSet<RecordType> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new DecoratingActivityRecordSet which decorates the records
|
||||||
|
* within the given set.
|
||||||
|
*
|
||||||
|
* @param recordSet
|
||||||
|
* The ActivityRecordSet whose records are being decorated.
|
||||||
|
*/
|
||||||
|
public DecoratingActivityRecordSet(ActivityRecordSet<RecordType> recordSet) {
|
||||||
|
super(recordSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a record retrieved from a ActivityRecordSet which originates from
|
||||||
|
* a different AuthenticationProvider, returns an identical type of record
|
||||||
|
* optionally wrapped with additional information, functionality, etc. If
|
||||||
|
* this record set chooses to decorate the record provided, it is up to the
|
||||||
|
* implementation of that decorated record to properly pass through
|
||||||
|
* operations as appropriate. All records retrieved from this
|
||||||
|
* DecoratingActivityRecordSet will first be passed through this function.
|
||||||
|
*
|
||||||
|
* @param record
|
||||||
|
* A record from a ActivityRecordSet which originates from a different
|
||||||
|
* AuthenticationProvider.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* A record which may have been decorated by this
|
||||||
|
* DecoratingActivityRecordSet. If the record was not decorated, the
|
||||||
|
* original, unmodified record may be returned instead.
|
||||||
|
*
|
||||||
|
* @throws GuacamoleException
|
||||||
|
* If the provided record cannot be decorated due to an error.
|
||||||
|
*/
|
||||||
|
protected abstract RecordType decorate(RecordType record)
|
||||||
|
throws GuacamoleException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given an ActivityRecordSet which originates from a different
|
||||||
|
* AuthenticationProvider, returns an identical type of record set
|
||||||
|
* optionally wrapped with additional information, functionality, etc. If
|
||||||
|
* this record set chooses to decorate the record set provided, it is up to
|
||||||
|
* the implementation of that decorated record set to properly pass through
|
||||||
|
* operations as appropriate. All record sets retrieved from this
|
||||||
|
* DecoratingActivityRecordSet will first be passed through this function,
|
||||||
|
* such as those returned by {@link #limit(int)} and similar functions.
|
||||||
|
* <p>
|
||||||
|
* By default, this function will wrap any provided ActivityRecordSet in a
|
||||||
|
* simple, anonymous instance of DecoratingActivityRecordSet that delegates
|
||||||
|
* to the decorate() implementations of this DecoratingActivityRecordSet.
|
||||||
|
* <strong>This default behavior may need to be overridden if the
|
||||||
|
* DecoratingActivityRecordSet implementation maintains any internal
|
||||||
|
* state.</strong>
|
||||||
|
*
|
||||||
|
* @param recordSet
|
||||||
|
* An ActivityRecordSet which originates from a different
|
||||||
|
* AuthenticationProvider.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* A record set which may have been decorated by this
|
||||||
|
* DecoratingActivityRecordSet. If the record set was not decorated, the
|
||||||
|
* original, unmodified record set may be returned instead, however
|
||||||
|
* beware that this may result in records within the set no longer
|
||||||
|
* being decorated.
|
||||||
|
*
|
||||||
|
* @throws GuacamoleException
|
||||||
|
* If the provided record set cannot be decorated due to an error.
|
||||||
|
*/
|
||||||
|
protected ActivityRecordSet<RecordType> decorate(ActivityRecordSet<RecordType> recordSet)
|
||||||
|
throws GuacamoleException {
|
||||||
|
final DecoratingActivityRecordSet<RecordType> decorator = this;
|
||||||
|
return new DecoratingActivityRecordSet<RecordType>(recordSet) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected RecordType decorate(RecordType record) throws GuacamoleException {
|
||||||
|
return decorator.decorate(record);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ActivityRecordSet<RecordType> decorate(ActivityRecordSet<RecordType> recordSet)
|
||||||
|
throws GuacamoleException {
|
||||||
|
return decorator.decorate(recordSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RecordType get(String string) throws GuacamoleException {
|
||||||
|
|
||||||
|
RecordType record = super.get(string);
|
||||||
|
if (record != null)
|
||||||
|
return decorate(record);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ActivityRecordSet<RecordType> sort(SortableProperty property,
|
||||||
|
boolean desc) throws GuacamoleException {
|
||||||
|
return decorate(super.sort(property, desc));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ActivityRecordSet<RecordType> limit(int limit) throws GuacamoleException {
|
||||||
|
return decorate(super.limit(limit));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ActivityRecordSet<RecordType> contains(String value) throws GuacamoleException {
|
||||||
|
return decorate(super.contains(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<RecordType> asCollection() throws GuacamoleException {
|
||||||
|
|
||||||
|
Collection<RecordType> records = super.asCollection();
|
||||||
|
|
||||||
|
List<RecordType> decoratedRecords = new ArrayList<>(records.size());
|
||||||
|
for (RecordType record : records)
|
||||||
|
decoratedRecords.add(decorate(record));
|
||||||
|
|
||||||
|
return decoratedRecords;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
* 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.Date;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ActivityRecord implementation which simply delegates all function calls to an
|
||||||
|
* underlying ActivityRecord.
|
||||||
|
*/
|
||||||
|
public class DelegatingActivityRecord implements ActivityRecord {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The wrapped ActivityRecord.
|
||||||
|
*/
|
||||||
|
private final ActivityRecord record;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wraps the given ActivityRecord such that all function calls against this
|
||||||
|
* DelegatingActivityRecord will be delegated to it.
|
||||||
|
*
|
||||||
|
* @param record
|
||||||
|
* The record to wrap.
|
||||||
|
*/
|
||||||
|
public DelegatingActivityRecord(ActivityRecord record) {
|
||||||
|
this.record = record;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the underlying ActivityRecord wrapped by this
|
||||||
|
* DelegatingActivityRecord.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* The ActivityRecord wrapped by this DelegatingActivityRecord.
|
||||||
|
*/
|
||||||
|
protected ActivityRecord getDelegateActivityRecord() {
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Date getStartDate() {
|
||||||
|
return record.getStartDate();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Date getEndDate() {
|
||||||
|
return record.getEndDate();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getRemoteHost() {
|
||||||
|
return record.getRemoteHost();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUsername() {
|
||||||
|
return record.getUsername();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isActive() {
|
||||||
|
return record.isActive();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getIdentifier() {
|
||||||
|
return record.getIdentifier();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UUID getUUID() {
|
||||||
|
return record.getUUID();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, ActivityLog> getLogs() {
|
||||||
|
return record.getLogs();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, String> getAttributes() {
|
||||||
|
return record.getAttributes();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,88 @@
|
|||||||
|
/*
|
||||||
|
* 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.Collection;
|
||||||
|
import org.apache.guacamole.GuacamoleException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ActivityRecordSet implementation which simply delegates all function calls
|
||||||
|
* to an underlying ActivityRecordSet.
|
||||||
|
*
|
||||||
|
* @param <RecordType>
|
||||||
|
* The type of ActivityRecord contained within this set.
|
||||||
|
*/
|
||||||
|
public class DelegatingActivityRecordSet<RecordType extends ActivityRecord>
|
||||||
|
implements ActivityRecordSet<RecordType> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The wrapped ActivityRecordSet.
|
||||||
|
*/
|
||||||
|
private final ActivityRecordSet<RecordType> recordSet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wraps the given ActivityRecordSet such that all function calls against this
|
||||||
|
* DelegatingActivityRecordSet will be delegated to it.
|
||||||
|
*
|
||||||
|
* @param recordSet
|
||||||
|
* The ActivityRecordSet to wrap.
|
||||||
|
*/
|
||||||
|
public DelegatingActivityRecordSet(ActivityRecordSet<RecordType> recordSet) {
|
||||||
|
this.recordSet = recordSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the underlying ActivityRecordSet wrapped by this
|
||||||
|
* DelegatingActivityRecordSet.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* The ActivityRecordSet wrapped by this DelegatingActivityRecordSet.
|
||||||
|
*/
|
||||||
|
protected ActivityRecordSet<RecordType> getDelegateActivityRecordSet() {
|
||||||
|
return recordSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RecordType get(String identifier) throws GuacamoleException {
|
||||||
|
return recordSet.get(identifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<RecordType> asCollection() throws GuacamoleException {
|
||||||
|
return recordSet.asCollection();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ActivityRecordSet<RecordType> contains(String value) throws GuacamoleException {
|
||||||
|
return recordSet.contains(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ActivityRecordSet<RecordType> limit(int limit) throws GuacamoleException {
|
||||||
|
return recordSet.limit(limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ActivityRecordSet<RecordType> sort(SortableProperty property,
|
||||||
|
boolean desc) throws GuacamoleException {
|
||||||
|
return recordSet.sort(property, desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
* 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.Date;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ConnectionRecord implementation which simply delegates all function calls to
|
||||||
|
* an underlying ConnectionRecord.
|
||||||
|
*/
|
||||||
|
public class DelegatingConnectionRecord extends DelegatingActivityRecord
|
||||||
|
implements ConnectionRecord {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The wrapped ConnectionRecord.
|
||||||
|
*/
|
||||||
|
private final ConnectionRecord record;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wraps the given ConnectionRecord such that all function calls against
|
||||||
|
* this DelegatingConnectionRecord will be delegated to it.
|
||||||
|
*
|
||||||
|
* @param record
|
||||||
|
* The record to wrap.
|
||||||
|
*/
|
||||||
|
public DelegatingConnectionRecord(ConnectionRecord record) {
|
||||||
|
super(record);
|
||||||
|
this.record = record;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the underlying ConnectionRecord wrapped by this
|
||||||
|
* DelegatingConnectionRecord.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* The ConnectionRecord wrapped by this DelegatingConnectionRecord.
|
||||||
|
*/
|
||||||
|
protected ConnectionRecord getDelegateConnectionRecord() {
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getConnectionIdentifier() {
|
||||||
|
return record.getConnectionIdentifier();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getConnectionName() {
|
||||||
|
return record.getConnectionName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getSharingProfileIdentifier() {
|
||||||
|
return record.getSharingProfileIdentifier();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getSharingProfileName() {
|
||||||
|
return record.getSharingProfileName();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* 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.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import org.apache.guacamole.GuacamoleException;
|
||||||
|
import org.apache.guacamole.GuacamoleResourceNotFoundException;
|
||||||
|
import org.apache.guacamole.language.TranslatableMessage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ActivityLog implementation that exposes the content of a local file.
|
||||||
|
*/
|
||||||
|
public class FileActivityLog extends AbstractActivityLog {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The File providing the content of this log.
|
||||||
|
*/
|
||||||
|
private final File content;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new FileActivityLog that exposes the content of the given
|
||||||
|
* local file as an {@link ActivityLog}.
|
||||||
|
*
|
||||||
|
* @param type
|
||||||
|
* The type of this ActivityLog.
|
||||||
|
*
|
||||||
|
* @param description
|
||||||
|
* A human-readable message that describes this log.
|
||||||
|
*
|
||||||
|
* @param content
|
||||||
|
* The File that should be used to provide the content of this log.
|
||||||
|
*/
|
||||||
|
public FileActivityLog(Type type, TranslatableMessage description, File content) {
|
||||||
|
super(type, description);
|
||||||
|
this.content = content;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getSize() throws GuacamoleException {
|
||||||
|
return content.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream getContent() throws GuacamoleException {
|
||||||
|
try {
|
||||||
|
return new FileInputStream(content);
|
||||||
|
}
|
||||||
|
catch (FileNotFoundException e) {
|
||||||
|
throw new GuacamoleResourceNotFoundException("Associated file "
|
||||||
|
+ "does not exist or cannot be read.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* 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.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An object which is associated with a set of arbitrary attributes, defined
|
||||||
|
* as name/value pairs.
|
||||||
|
*/
|
||||||
|
public interface ReadableAttributes {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all attributes associated with this object. The returned map
|
||||||
|
* may not be modifiable.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* A map of all attribute identifiers to their corresponding values,
|
||||||
|
* for all attributes associated with this object, which may not be
|
||||||
|
* modifiable.
|
||||||
|
*/
|
||||||
|
Map<String, String> getAttributes();
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,89 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service which defines the ActivityLog class.
|
||||||
|
*/
|
||||||
|
angular.module('rest').factory('ActivityLog', [function defineActivityLog() {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The object returned by REST API calls when representing a log or
|
||||||
|
* recording associated with a connection's usage history, such as a
|
||||||
|
* session recording or typescript.
|
||||||
|
*
|
||||||
|
* @constructor
|
||||||
|
* @param {ActivityLog|Object} [template={}]
|
||||||
|
* The object whose properties should be copied within the new
|
||||||
|
* ActivityLog.
|
||||||
|
*/
|
||||||
|
var ActivityLog = function ActivityLog(template) {
|
||||||
|
|
||||||
|
// Use empty object by default
|
||||||
|
template = template || {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of this ActivityLog.
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
this.type = template.type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A human-readable description of this log.
|
||||||
|
*
|
||||||
|
* @type {TranslatableMessage}
|
||||||
|
*/
|
||||||
|
this.description = template.description;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All possible types of ActivityLog.
|
||||||
|
*
|
||||||
|
* @type {!object.<string, string>}
|
||||||
|
*/
|
||||||
|
ActivityLog.Type = {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Guacamole session recording in the form of a Guacamole protocol
|
||||||
|
* dump.
|
||||||
|
*/
|
||||||
|
GUACAMOLE_SESSION_RECORDING : 'GUACAMOLE_SESSION_RECORDING',
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A text log from a server-side process, such as the Guacamole web
|
||||||
|
* application or guacd.
|
||||||
|
*/
|
||||||
|
SERVER_LOG : 'SERVER_LOG',
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A text session recording in the form of a standard typescript.
|
||||||
|
*/
|
||||||
|
TYPESCRIPT : 'TYPESCRIPT',
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The timing file related to a typescript.
|
||||||
|
*/
|
||||||
|
TYPESCRIPT_TIMING : 'TYPESCRIPT_TIMING'
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
return ActivityLog;
|
||||||
|
|
||||||
|
}]);
|
@@ -38,6 +38,23 @@ angular.module('rest').factory('ConnectionHistoryEntry', [function defineConnect
|
|||||||
// Use empty object by default
|
// Use empty object by default
|
||||||
template = template || {};
|
template = template || {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An arbitrary identifier that uniquely identifies this record
|
||||||
|
* relative to other records in the same set, or null if no such unique
|
||||||
|
* identifier exists.
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
this.identifier = template.identifier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A UUID that uniquely identifies this record, or null if no such
|
||||||
|
* unique identifier exists.
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
this.uuid = template.uuid;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The identifier of the connection associated with this history entry.
|
* The identifier of the connection associated with this history entry.
|
||||||
*
|
*
|
||||||
@@ -103,6 +120,23 @@ angular.module('rest').factory('ConnectionHistoryEntry', [function defineConnect
|
|||||||
*/
|
*/
|
||||||
this.active = template.active;
|
this.active = template.active;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Arbitrary name/value pairs which further describe this history
|
||||||
|
* entry. The semantics and validity of these attributes are dictated
|
||||||
|
* by the extension which defines them.
|
||||||
|
*
|
||||||
|
* @type {!object.<string, string>}
|
||||||
|
*/
|
||||||
|
this.attributes = template.attributes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All logs associated and accessible via this record, stored by their
|
||||||
|
* corresponding unique names.
|
||||||
|
*
|
||||||
|
* @type {!object.<string, ActivityLog>}
|
||||||
|
*/
|
||||||
|
this.logs = template.logs;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* 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.rest.history;
|
||||||
|
|
||||||
|
import org.apache.guacamole.language.TranslatableMessage;
|
||||||
|
import org.apache.guacamole.net.auth.ActivityLog;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A activity log which may be exposed through the REST endpoints.
|
||||||
|
*/
|
||||||
|
public class APIActivityLog {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of this ActivityLog.
|
||||||
|
*/
|
||||||
|
private final ActivityLog.Type type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A human-readable description of this log.
|
||||||
|
*/
|
||||||
|
private final TranslatableMessage description;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new APIActivityLog, copying the data from the given activity
|
||||||
|
* log.
|
||||||
|
*
|
||||||
|
* @param log
|
||||||
|
* The log to copy data from.
|
||||||
|
*/
|
||||||
|
public APIActivityLog(ActivityLog log) {
|
||||||
|
this.type = log.getType();
|
||||||
|
this.description = log.getDescription();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the type of this activity log. The type of an activity log
|
||||||
|
* dictates how its content should be interpreted or exposed, however the
|
||||||
|
* content of a log is not directly exposed by this class.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* The type of this activity log.
|
||||||
|
*/
|
||||||
|
public ActivityLog.Type getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a human-readable message that describes this log.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* A human-readable message that describes this log.
|
||||||
|
*/
|
||||||
|
public TranslatableMessage getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -20,6 +20,9 @@
|
|||||||
package org.apache.guacamole.rest.history;
|
package org.apache.guacamole.rest.history;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
import org.apache.guacamole.net.auth.ActivityRecord;
|
import org.apache.guacamole.net.auth.ActivityRecord;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -55,6 +58,30 @@ public class APIActivityRecord {
|
|||||||
*/
|
*/
|
||||||
private final boolean active;
|
private final boolean active;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The unique identifier assigned to this record, or null if this record
|
||||||
|
* has no such identifier.
|
||||||
|
*/
|
||||||
|
private final String identifier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A UUID that uniquely identifies this record, or null if no such unique
|
||||||
|
* identifier exists.
|
||||||
|
*/
|
||||||
|
private final UUID uuid;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A map of all attribute identifiers to their corresponding values, for
|
||||||
|
* all attributes associated with this record.
|
||||||
|
*/
|
||||||
|
private final Map<String, String> attributes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A map of all logs associated and accessible via this record, associated
|
||||||
|
* with their corresponding unique names.
|
||||||
|
*/
|
||||||
|
private final Map<String, APIActivityLog> logs;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new APIActivityRecord, copying the data from the given activity
|
* Creates a new APIActivityRecord, copying the data from the given activity
|
||||||
* record.
|
* record.
|
||||||
@@ -63,11 +90,21 @@ public class APIActivityRecord {
|
|||||||
* The record to copy data from.
|
* The record to copy data from.
|
||||||
*/
|
*/
|
||||||
public APIActivityRecord(ActivityRecord record) {
|
public APIActivityRecord(ActivityRecord record) {
|
||||||
|
|
||||||
this.startDate = record.getStartDate();
|
this.startDate = record.getStartDate();
|
||||||
this.endDate = record.getEndDate();
|
this.endDate = record.getEndDate();
|
||||||
this.remoteHost = record.getRemoteHost();
|
this.remoteHost = record.getRemoteHost();
|
||||||
this.username = record.getUsername();
|
this.username = record.getUsername();
|
||||||
this.active = record.isActive();
|
this.active = record.isActive();
|
||||||
|
this.identifier = record.getIdentifier();
|
||||||
|
this.uuid = record.getUUID();
|
||||||
|
this.attributes = record.getAttributes();
|
||||||
|
|
||||||
|
this.logs = record.getLogs().entrySet().stream().collect(Collectors.toMap(
|
||||||
|
Map.Entry::getKey,
|
||||||
|
(entry) -> new APIActivityLog(entry.getValue())
|
||||||
|
));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -128,4 +165,51 @@ public class APIActivityRecord {
|
|||||||
return active;
|
return active;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the unique identifier assigned to this record, if any. If this
|
||||||
|
* record is not uniquely identifiable, this may be null.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* The unique identifier assigned to this record, or null if this
|
||||||
|
* record has no such identifier.
|
||||||
|
*/
|
||||||
|
public String getIdentifier() {
|
||||||
|
return identifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a UUID that uniquely identifies this record. If not implemented
|
||||||
|
* by the extension exposing this history record, this may be null.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* A UUID that uniquely identifies this record, or null if no such
|
||||||
|
* unique identifier exists.
|
||||||
|
*/
|
||||||
|
public UUID getUUID() {
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all attributes associated with this record.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* A map of all attribute identifiers to their corresponding values,
|
||||||
|
* for all attributes associated with this record.
|
||||||
|
*/
|
||||||
|
public Map<String, String> getAttributes() {
|
||||||
|
return attributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a Map of logs related to this record and accessible by the
|
||||||
|
* current user, such as Guacamole session recordings. Each log is
|
||||||
|
* associated with a corresponding, arbitrary, unique name.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* A Map of logs related to this record.
|
||||||
|
*/
|
||||||
|
public Map<String, APIActivityLog> getLogs() {
|
||||||
|
return logs;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* 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.rest.history;
|
||||||
|
|
||||||
|
import javax.ws.rs.GET;
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
import javax.ws.rs.core.Response.ResponseBuilder;
|
||||||
|
import org.apache.guacamole.GuacamoleException;
|
||||||
|
import org.apache.guacamole.net.auth.ActivityLog;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A REST resource which exposes the contents of a given ActivityLog.
|
||||||
|
*/
|
||||||
|
public class ActivityLogResource {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The ActivityLog whose contents are being exposed.
|
||||||
|
*/
|
||||||
|
private final ActivityLog log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new ActivityLogResource which exposes the records within the
|
||||||
|
* given ActivityLog.
|
||||||
|
*
|
||||||
|
* @param log
|
||||||
|
* The ActivityLog whose contents should be exposed.
|
||||||
|
*/
|
||||||
|
public ActivityLogResource(ActivityLog log) {
|
||||||
|
this.log = log;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the raw contents of the underlying ActivityLog. If the size of
|
||||||
|
* the ActivityLog is known, this size is included as the "Content-Length"
|
||||||
|
* of the response.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* A Response containing the raw contents of the underlying
|
||||||
|
* ActivityLog.
|
||||||
|
*
|
||||||
|
* @throws GuacamoleException
|
||||||
|
* If an error prevents retrieving the content of the log or its size.
|
||||||
|
*/
|
||||||
|
@GET
|
||||||
|
public Response getContents() throws GuacamoleException {
|
||||||
|
|
||||||
|
// Build base response exposing the raw contents of the underlying log
|
||||||
|
ResponseBuilder response = Response.ok(log.getContent(),
|
||||||
|
log.getType().getContentType());
|
||||||
|
|
||||||
|
// Include size, if known
|
||||||
|
long size = log.getSize();
|
||||||
|
if (size >= 0)
|
||||||
|
response.header("Content-Length", size);
|
||||||
|
|
||||||
|
return response.build();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,107 @@
|
|||||||
|
/*
|
||||||
|
* 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.rest.history;
|
||||||
|
|
||||||
|
import java.util.function.Function;
|
||||||
|
import javax.ws.rs.Consumes;
|
||||||
|
import javax.ws.rs.GET;
|
||||||
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.PathParam;
|
||||||
|
import javax.ws.rs.Produces;
|
||||||
|
import javax.ws.rs.core.MediaType;
|
||||||
|
import org.apache.guacamole.GuacamoleException;
|
||||||
|
import org.apache.guacamole.GuacamoleResourceNotFoundException;
|
||||||
|
import org.apache.guacamole.net.auth.ActivityLog;
|
||||||
|
import org.apache.guacamole.net.auth.ActivityRecord;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A REST resource which exposes a single ActivityRecord, allowing any
|
||||||
|
* associated and accessible logs to be retrieved.
|
||||||
|
*/
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
|
public class ActivityRecordResource {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The ActivityRecord being exposed.
|
||||||
|
*/
|
||||||
|
private final ActivityRecord record;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The REST API object representing the ActivityRecord being exposed.
|
||||||
|
*/
|
||||||
|
private final APIActivityRecord externalRecord;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new ActivityRecordResource which exposes the given record.
|
||||||
|
*
|
||||||
|
* @param record
|
||||||
|
* The ActivityRecord that should be exposed.
|
||||||
|
*
|
||||||
|
* @param externalRecord
|
||||||
|
* The REST API object representing the ActivityRecord being exposed.
|
||||||
|
*/
|
||||||
|
public ActivityRecordResource(ActivityRecord record,
|
||||||
|
APIActivityRecord externalRecord) {
|
||||||
|
this.record = record;
|
||||||
|
this.externalRecord = externalRecord;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the record represented by this ActivityRecordResource, in a
|
||||||
|
* format intended for interchange.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* The record that this ActivityRecordResource represents, in a format
|
||||||
|
* intended for interchange.
|
||||||
|
*/
|
||||||
|
@GET
|
||||||
|
public APIActivityRecord getRecord() {
|
||||||
|
return externalRecord;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an ActivityLogResource representing the log associated with the
|
||||||
|
* underlying ActivityRecord and having the given name. If no such log
|
||||||
|
* can be retrieved, either because it does not exist or the current user
|
||||||
|
* does not have access, an exception is thrown.
|
||||||
|
*
|
||||||
|
* @param logName
|
||||||
|
* The unique name of the log to retrieve.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* An ActivityLogResource representing the log having the given name.
|
||||||
|
*
|
||||||
|
* @throws GuacamoleException
|
||||||
|
* If no such log can be retrieved.
|
||||||
|
*/
|
||||||
|
@Path("logs/{name}")
|
||||||
|
public ActivityLogResource getLog(@PathParam("name") String logName)
|
||||||
|
throws GuacamoleException {
|
||||||
|
|
||||||
|
ActivityLog log = record.getLogs().get(logName);
|
||||||
|
if (log != null)
|
||||||
|
return new ActivityLogResource(log);
|
||||||
|
|
||||||
|
throw new GuacamoleResourceNotFoundException("No such log.");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -23,10 +23,13 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import javax.ws.rs.Consumes;
|
import javax.ws.rs.Consumes;
|
||||||
import javax.ws.rs.GET;
|
import javax.ws.rs.GET;
|
||||||
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.PathParam;
|
||||||
import javax.ws.rs.Produces;
|
import javax.ws.rs.Produces;
|
||||||
import javax.ws.rs.QueryParam;
|
import javax.ws.rs.QueryParam;
|
||||||
import javax.ws.rs.core.MediaType;
|
import javax.ws.rs.core.MediaType;
|
||||||
import org.apache.guacamole.GuacamoleException;
|
import org.apache.guacamole.GuacamoleException;
|
||||||
|
import org.apache.guacamole.GuacamoleResourceNotFoundException;
|
||||||
import org.apache.guacamole.net.auth.ActivityRecord;
|
import org.apache.guacamole.net.auth.ActivityRecord;
|
||||||
import org.apache.guacamole.net.auth.ActivityRecordSet;
|
import org.apache.guacamole.net.auth.ActivityRecordSet;
|
||||||
|
|
||||||
@@ -86,6 +89,45 @@ public abstract class ActivityRecordSetResource<InternalRecordType extends Activ
|
|||||||
*/
|
*/
|
||||||
protected abstract ExternalRecordType toExternalRecord(InternalRecordType record);
|
protected abstract ExternalRecordType toExternalRecord(InternalRecordType record);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies the given search and sorting criteria to the ActivityRecordSet
|
||||||
|
* exposed by this ActivityRecordSetResource. The ActivityRecordSet stored
|
||||||
|
* as {@link #history} is modified as a result of this call.
|
||||||
|
*
|
||||||
|
* @param requiredContents
|
||||||
|
* The set of strings that each must occur somewhere within the
|
||||||
|
* returned records, whether within the associated username,
|
||||||
|
* the name of some associated object (such as a connection), or any
|
||||||
|
* associated date. If non-empty, any record not matching each of the
|
||||||
|
* strings within the collection will be excluded from the results.
|
||||||
|
*
|
||||||
|
* @param sortPredicates
|
||||||
|
* A list of predicates to apply while sorting the resulting records,
|
||||||
|
* describing the properties involved and the sort order for those
|
||||||
|
* properties.
|
||||||
|
*
|
||||||
|
* @throws GuacamoleException
|
||||||
|
* If an error occurs while applying the given filter criteria or
|
||||||
|
* sort predicates.
|
||||||
|
*/
|
||||||
|
private void applyCriteria(List<String> requiredContents,
|
||||||
|
List<APISortPredicate> sortPredicates) throws GuacamoleException {
|
||||||
|
|
||||||
|
// Restrict to records which contain the specified strings
|
||||||
|
for (String required : requiredContents) {
|
||||||
|
if (!required.isEmpty())
|
||||||
|
history = history.contains(required);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort according to specified ordering
|
||||||
|
for (APISortPredicate predicate : sortPredicates)
|
||||||
|
history = history.sort(predicate.getProperty(), predicate.isDescending());
|
||||||
|
|
||||||
|
// Limit to maximum result size
|
||||||
|
history = history.limit(MAXIMUM_HISTORY_SIZE);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the list of activity records stored within the underlying
|
* Retrieves the list of activity records stored within the underlying
|
||||||
* ActivityRecordSet which match the given, arbitrary criteria. If
|
* ActivityRecordSet which match the given, arbitrary criteria. If
|
||||||
@@ -118,18 +160,8 @@ public abstract class ActivityRecordSetResource<InternalRecordType extends Activ
|
|||||||
@QueryParam("order") List<APISortPredicate> sortPredicates)
|
@QueryParam("order") List<APISortPredicate> sortPredicates)
|
||||||
throws GuacamoleException {
|
throws GuacamoleException {
|
||||||
|
|
||||||
// Restrict to records which contain the specified strings
|
// Apply search/sort criteria
|
||||||
for (String required : requiredContents) {
|
applyCriteria(requiredContents, sortPredicates);
|
||||||
if (!required.isEmpty())
|
|
||||||
history = history.contains(required);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sort according to specified ordering
|
|
||||||
for (APISortPredicate predicate : sortPredicates)
|
|
||||||
history = history.sort(predicate.getProperty(), predicate.isDescending());
|
|
||||||
|
|
||||||
// Limit to maximum result size
|
|
||||||
history = history.limit(MAXIMUM_HISTORY_SIZE);
|
|
||||||
|
|
||||||
// Convert record set to collection of API records
|
// Convert record set to collection of API records
|
||||||
List<ExternalRecordType> apiRecords = new ArrayList<>();
|
List<ExternalRecordType> apiRecords = new ArrayList<>();
|
||||||
@@ -141,4 +173,30 @@ public abstract class ActivityRecordSetResource<InternalRecordType extends Activ
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves record having the given identifier from among the set of
|
||||||
|
* activity records stored within the underlying ActivityRecordSet.
|
||||||
|
*
|
||||||
|
* @param identifier
|
||||||
|
* The unique identifier of the record to retrieve.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* A resource representing the record having the given identifier.
|
||||||
|
*
|
||||||
|
* @throws GuacamoleException
|
||||||
|
* If an error occurs while locating the requested record, or if the
|
||||||
|
* requested record cannot be found.
|
||||||
|
*/
|
||||||
|
@Path("{identifier}")
|
||||||
|
public ActivityRecordResource getRecord(@PathParam("identifier") String identifier)
|
||||||
|
throws GuacamoleException {
|
||||||
|
|
||||||
|
InternalRecordType record = history.get(identifier);
|
||||||
|
if (record == null)
|
||||||
|
throw new GuacamoleResourceNotFoundException("Not found: \"" + identifier + "\"");
|
||||||
|
|
||||||
|
return new ActivityRecordResource(record, toExternalRecord(record));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user