GUACAMOLE-820: Merge match IP address filters anywhere in object properties.
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/JDBCAuthenticationProviderService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/JDBCAuthenticationProviderService.java
index 646bf20..1bb2c68 100644
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/JDBCAuthenticationProviderService.java
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/JDBCAuthenticationProviderService.java
@@ -139,6 +139,7 @@
// Initialize the UserContext with the user account and return it.
context.init(user.getCurrentUser());
+ context.recordUserLogin();
return context;
}
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/activeconnection/ActiveConnectionPermissionService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/activeconnection/ActiveConnectionPermissionService.java
index 1e52571..2a4cd9e 100644
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/activeconnection/ActiveConnectionPermissionService.java
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/activeconnection/ActiveConnectionPermissionService.java
@@ -82,22 +82,24 @@
// Retrieve permissions only if allowed
if (canReadPermissions(user, targetEntity)) {
- // Only administrators may access active connections
- boolean isAdmin = targetEntity.isAdministrator();
+ // Privileged accounts (such as administrators or UserContexts
+ // returned by getPrivileged()) may always access active connections
+ boolean isPrivileged = targetEntity.isPrivileged();
// Get all active connections
Collection<ActiveConnectionRecord> records = tunnelService.getActiveConnections(user);
// We have READ, and possibly DELETE, on all active connections
- Set<ObjectPermission> permissions = new HashSet<ObjectPermission>();
+ Set<ObjectPermission> permissions = new HashSet<>();
for (ActiveConnectionRecord record : records) {
// Add implicit READ
String identifier = record.getUUID().toString();
permissions.add(new ObjectPermission(ObjectPermission.Type.READ, identifier));
- // If we're an admin, or the connection is ours, then we can DELETE
- if (isAdmin || targetEntity.isUser(record.getUsername()))
+ // If the target user is privileged, or the connection belongs
+ // to the target user, then they can DELETE
+ if (isPrivileged || targetEntity.isUser(record.getUsername()))
permissions.add(new ObjectPermission(ObjectPermission.Type.DELETE, identifier));
}
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/activeconnection/ActiveConnectionService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/activeconnection/ActiveConnectionService.java
index c21e9c3..046cee1 100644
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/activeconnection/ActiveConnectionService.java
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/activeconnection/ActiveConnectionService.java
@@ -81,7 +81,7 @@
Collection<String> identifiers) throws GuacamoleException {
String username = user.getIdentifier();
- boolean isAdmin = user.getUser().isAdministrator();
+ boolean isPrivileged = user.isPrivileged();
Set<String> identifierSet = new HashSet<String>(identifiers);
// Retrieve all visible connections (permissions enforced by tunnel service)
@@ -95,7 +95,7 @@
// be able to connect to (join) the active connection if they are
// the user that started the connection OR the user is an admin
boolean hasPrivilegedAccess =
- isAdmin || username.equals(record.getUsername());
+ isPrivileged || username.equals(record.getUsername());
// Add connection if within requested identifiers
if (identifierSet.contains(record.getUUID().toString())) {
@@ -211,7 +211,7 @@
ObjectPermissionSet permissionSet = getPermissionSet(user);
- return user.getUser().isAdministrator()
+ return user.isPrivileged()
|| permissionSet.hasPermission(type, identifier);
}
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/ModeledChildDirectoryObjectService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/ModeledChildDirectoryObjectService.java
index f517e27..7690313 100644
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/ModeledChildDirectoryObjectService.java
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/ModeledChildDirectoryObjectService.java
@@ -148,8 +148,8 @@
protected boolean canUpdateModifiedParents(ModeledAuthenticatedUser user,
String identifier, ModelType model) throws GuacamoleException {
- // If user is an administrator, no need to check
- if (user.getUser().isAdministrator())
+ // If user is privileged, no need to check
+ if (user.isPrivileged())
return true;
// Verify that we have permission to modify any modified parents
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/ModeledDirectoryObjectService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/ModeledDirectoryObjectService.java
index edbb67e..f8d0e8a 100644
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/ModeledDirectoryObjectService.java
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/ModeledDirectoryObjectService.java
@@ -171,7 +171,7 @@
ObjectPermissionSet permissionSet = getEffectivePermissionSet(user);
// Return whether permission is granted
- return user.getUser().isAdministrator()
+ return user.isPrivileged()
|| permissionSet.hasPermission(type, identifier);
}
@@ -248,7 +248,7 @@
ExternalType object, ModelType model) throws GuacamoleException {
// Verify permission to create objects
- if (!user.getUser().isAdministrator() && !hasCreatePermission(user))
+ if (!user.isPrivileged() && !hasCreatePermission(user))
throw new GuacamoleSecurityException("Permission denied.");
}
@@ -395,8 +395,8 @@
Collection<ModelType> objects;
- // Bypass permission checks if the user is a system admin
- if (user.getUser().isAdministrator())
+ // Bypass permission checks if the user is privileged
+ if (user.isPrivileged())
objects = getObjectMapper().select(identifiers);
// Otherwise only return explicitly readable identifiers
@@ -507,8 +507,8 @@
public Set<String> getIdentifiers(ModeledAuthenticatedUser user)
throws GuacamoleException {
- // Bypass permission checks if the user is a system admin
- if (user.getUser().isAdministrator())
+ // Bypass permission checks if the user is privileged
+ if (user.isPrivileged())
return getObjectMapper().selectIdentifiers();
// Otherwise only return explicitly readable identifiers
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/ModeledPermissions.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/ModeledPermissions.java
index 965062c..be5ac15 100644
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/ModeledPermissions.java
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/ModeledPermissions.java
@@ -132,18 +132,20 @@
}
/**
- * Returns whether this entity is a system administrator, and thus is not
- * restricted by permissions, taking into account permission inheritance
- * via user groups.
+ * Returns whether this entity is effectively unrestricted by permissions,
+ * such as a system administrator or an internal user operating via a
+ * privileged UserContext. Permission inheritance via user groups is taken
+ * into account.
*
* @return
- * true if this entity is a system administrator, false otherwise.
+ * true if this entity should be unrestricted by permissions, false
+ * otherwise.
*
* @throws GuacamoleException
- * If an error occurs while determining the entity's system administrator
- * status.
+ * If an error occurs while determining whether permission restrictions
+ * apply to the entity.
*/
- public boolean isAdministrator() throws GuacamoleException {
+ public boolean isPrivileged() throws GuacamoleException {
SystemPermissionSet systemPermissionSet = getEffective().getSystemPermissions();
return systemPermissionSet.hasPermission(SystemPermission.Type.ADMINISTER);
}
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/RelatedObjectSet.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/RelatedObjectSet.java
index f7b75ef..810e9a5 100644
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/RelatedObjectSet.java
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/RelatedObjectSet.java
@@ -140,8 +140,9 @@
private boolean canAlterRelation(Collection<String> identifiers)
throws GuacamoleException {
- // System administrators may alter any relations
- if (getCurrentUser().getUser().isAdministrator())
+ // Privileged users (such as system administrators) may alter any
+ // relations
+ if (getCurrentUser().isPrivileged())
return true;
// Non-admin users require UPDATE permission on the parent object ...
@@ -162,9 +163,9 @@
@Override
public Set<String> getObjects() throws GuacamoleException {
- // Bypass permission checks if the user is a system admin
+ // Bypass permission checks if the user is a privileged
ModeledAuthenticatedUser user = getCurrentUser();
- if (user.getUser().isAdministrator())
+ if (user.isPrivileged())
return getObjectRelationMapper().selectChildIdentifiers(parent.getModel());
// Otherwise only return explicitly readable identifiers
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connection/ConnectionService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connection/ConnectionService.java
index e2f3c15..926df32 100644
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connection/ConnectionService.java
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connection/ConnectionService.java
@@ -297,8 +297,8 @@
String identifier)
throws GuacamoleException {
- // Bypass permission checks if the user is a system admin
- if (user.getUser().isAdministrator())
+ // Bypass permission checks if the user is privileged
+ if (user.isPrivileged())
return connectionMapper.selectIdentifiersWithin(identifier);
// Otherwise only return explicitly readable identifiers
@@ -470,8 +470,8 @@
List<ConnectionRecordModel> searchResults;
- // Bypass permission checks if the user is a system admin
- if (user.getUser().isAdministrator())
+ // Bypass permission checks if the user is privileged
+ if (user.isPrivileged())
searchResults = connectionRecordMapper.search(requiredContents,
sortPredicates, limit);
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connectiongroup/ConnectionGroupService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connectiongroup/ConnectionGroupService.java
index 3e9ec72..dbf7793 100644
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connectiongroup/ConnectionGroupService.java
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connectiongroup/ConnectionGroupService.java
@@ -218,8 +218,8 @@
String identifier)
throws GuacamoleException {
- // Bypass permission checks if the user is a system admin
- if (user.getUser().isAdministrator())
+ // Bypass permission checks if the user is privileged
+ if (user.isPrivileged())
return connectionGroupMapper.selectIdentifiersWithin(identifier);
// Otherwise only return explicitly readable identifiers
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/permission/AbstractPermissionService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/permission/AbstractPermissionService.java
index eea570f..eb320bd 100644
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/permission/AbstractPermissionService.java
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/permission/AbstractPermissionService.java
@@ -104,8 +104,8 @@
if (targetEntity.isUser(user.getUser().getIdentifier()))
return true;
- // A system adminstrator can do anything
- if (user.getUser().isAdministrator())
+ // Privileged users (such as system administrators) may do anything
+ if (user.isPrivileged())
return true;
// Can read permissions on target entity if explicit READ is granted
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/permission/ModeledObjectPermissionService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/permission/ModeledObjectPermissionService.java
index 8c4be58..820873a 100644
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/permission/ModeledObjectPermissionService.java
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/permission/ModeledObjectPermissionService.java
@@ -95,8 +95,8 @@
Collection<ObjectPermission> permissions)
throws GuacamoleException {
- // A system adminstrator can do anything
- if (user.getUser().isAdministrator())
+ // Privileged users (such as system administrators) may do anything
+ if (user.isPrivileged())
return true;
// Verify user has update permission on the target entity
@@ -187,8 +187,8 @@
if (identifiers.isEmpty())
return identifiers;
- // If user is an admin, everything is accessible
- if (user.getUser().isAdministrator())
+ // Privileged users (such as system administrators) may access everything
+ if (user.isPrivileged())
return identifiers;
// Otherwise, return explicitly-retrievable identifiers only if allowed
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/permission/SystemPermissionService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/permission/SystemPermissionService.java
index 5e5a43b..4b8269f 100644
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/permission/SystemPermissionService.java
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/permission/SystemPermissionService.java
@@ -94,8 +94,9 @@
ModeledPermissions<? extends EntityModel> targetEntity,
Collection<SystemPermission> permissions) throws GuacamoleException {
- // Only an admin can create system permissions
- if (user.getUser().isAdministrator()) {
+ // Only privileged users (such as system administrators) can create
+ // system permissions
+ if (user.isPrivileged()) {
Collection<SystemPermissionModel> models = getModelInstances(targetEntity, permissions);
systemPermissionMapper.insert(models);
return;
@@ -111,8 +112,9 @@
ModeledPermissions<? extends EntityModel> targetEntity,
Collection<SystemPermission> permissions) throws GuacamoleException {
- // Only an admin can delete system permissions
- if (user.getUser().isAdministrator()) {
+ // Only privileged users (such as system administrators) can delete
+ // system permissions
+ if (user.isPrivileged()) {
// Do not allow users to remove their own admin powers
if (user.getUser().getIdentifier().equals(targetEntity.getIdentifier()))
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/tunnel/AbstractGuacamoleTunnelService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/tunnel/AbstractGuacamoleTunnelService.java
index abecf32..383ef3a 100644
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/tunnel/AbstractGuacamoleTunnelService.java
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/tunnel/AbstractGuacamoleTunnelService.java
@@ -628,8 +628,9 @@
if (records.isEmpty())
return Collections.<ActiveConnectionRecord>emptyList();
- // A system administrator can view all connections; no need to filter
- if (user.getUser().isAdministrator())
+ // Privileged users (such as system administrators) can view all
+ // connections; no need to filter
+ if (user.isPrivileged())
return records;
// Build set of all connection identifiers associated with active tunnels
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledAuthenticatedUser.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledAuthenticatedUser.java
index 5778ad0..7ede92c 100644
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledAuthenticatedUser.java
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledAuthenticatedUser.java
@@ -23,6 +23,7 @@
import java.util.Collections;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.net.auth.AuthenticatedUser;
import org.apache.guacamole.net.auth.AuthenticationProvider;
import org.apache.guacamole.net.auth.Credentials;
@@ -176,4 +177,22 @@
super.getEffectiveUserGroups());
}
+ /**
+ * Returns whether this user is effectively unrestricted by permissions,
+ * such as a system administrator or an internal user operating via a
+ * privileged UserContext. Permission inheritance via user groups is taken
+ * into account.
+ *
+ * @return
+ * true if this user should be unrestricted by permissions, false
+ * otherwise.
+ *
+ * @throws GuacamoleException
+ * If an error occurs while determining whether permission restrictions
+ * apply to the user.
+ */
+ public boolean isPrivileged() throws GuacamoleException {
+ return getUser().isPrivileged();
+ }
+
}
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUserContext.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUserContext.java
index e98a25a..fe60a5f 100644
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUserContext.java
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUserContext.java
@@ -48,6 +48,7 @@
import org.apache.guacamole.net.auth.Directory;
import org.apache.guacamole.net.auth.SharingProfile;
import org.apache.guacamole.net.auth.User;
+import org.apache.guacamole.net.auth.UserContext;
import org.apache.guacamole.net.auth.UserGroup;
/**
@@ -118,13 +119,22 @@
private Provider<UserRecordSet> userRecordSetProvider;
/**
+ * Provider for retrieving UserContext instances.
+ */
+ @Inject
+ private Provider<ModeledUserContext> userContextProvider;
+
+ /**
* Mapper for user login records.
*/
@Inject
private UserRecordMapper userRecordMapper;
/**
- * The activity record associated with this user's Guacamole session.
+ * The activity record associated with this user's Guacamole session. If
+ * this user's session will not have an associated activity record, such as
+ * a temporary privileged session created via getPrivileged(), this will be
+ * null.
*/
private ActivityRecordModel userRecord;
@@ -141,15 +151,40 @@
sharingProfileDirectory.init(currentUser);
activeConnectionDirectory.init(currentUser);
+ }
+
+ /**
+ * Records that the user associated with this UserContext has logged in,
+ * creating a partial activity record. The resulting activity record will
+ * contain a start date only, with the end date being automatically
+ * populated when this UserContext is invalidated. If this function is
+ * invoked more than once for the same UserContext, only the first
+ * invocation has any effect. If this function is never invoked, no
+ * activity record will be recorded, including when this UserContext is
+ * invalidated.
+ */
+ public void recordUserLogin() {
+
+ // Do nothing if invoked multiple times
+ if (userRecord != null)
+ return;
+
// Create login record for user
userRecord = new ActivityRecordModel();
- userRecord.setUsername(currentUser.getIdentifier());
+ userRecord.setUsername(getCurrentUser().getIdentifier());
userRecord.setStartDate(new Date());
- userRecord.setRemoteHost(currentUser.getCredentials().getRemoteAddress());
+ userRecord.setRemoteHost(getCurrentUser().getCredentials().getRemoteAddress());
// Insert record representing login
userRecordMapper.insert(userRecord);
+
+ }
+ @Override
+ public UserContext getPrivileged() {
+ ModeledUserContext context = userContextProvider.get();
+ context.init(new PrivilegedModeledAuthenticatedUser(getCurrentUser()));
+ return context;
}
@Override
@@ -253,9 +288,11 @@
@Override
public void invalidate() {
- // Record logout time
- userRecord.setEndDate(new Date());
- userRecordMapper.update(userRecord);
+ // Record logout time only if login time was recorded
+ if (userRecord != null) {
+ userRecord.setEndDate(new Date());
+ userRecordMapper.update(userRecord);
+ }
}
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/PrivilegedModeledAuthenticatedUser.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/PrivilegedModeledAuthenticatedUser.java
new file mode 100644
index 0000000..82fcf08
--- /dev/null
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/PrivilegedModeledAuthenticatedUser.java
@@ -0,0 +1,50 @@
+/*
+ * 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.user;
+
+import org.apache.guacamole.GuacamoleException;
+
+/**
+ * A ModeledAuthenticatedUser which is always privileged, returning true for
+ * every call to isPrivileged().
+ */
+public class PrivilegedModeledAuthenticatedUser extends ModeledAuthenticatedUser {
+
+ /**
+ * Creates a new PrivilegedModeledAuthenticatedUser which shares the same
+ * user identity as the given ModeledAuthenticatedUser. Regardless of the
+ * privileges explicitly granted to the given user, the resulting
+ * PrivilegedModeledAuthenticatedUser will always assert that it is
+ * privileged.
+ *
+ * @param authenticatedUser
+ * The ModeledAuthenticatedUser that declares the identity of the user
+ * in question.
+ */
+ public PrivilegedModeledAuthenticatedUser(ModeledAuthenticatedUser authenticatedUser){
+ super(authenticatedUser, authenticatedUser.getModelAuthenticationProvider(), authenticatedUser.getUser());
+ }
+
+ @Override
+ public boolean isPrivileged() throws GuacamoleException {
+ return true;
+ }
+
+}
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserService.java
index a68f082..0aecd10 100644
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserService.java
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserService.java
@@ -278,8 +278,8 @@
// Verify new password does not violate defined policies (if specified)
if (object.getPassword() != null) {
- // Enforce password age only for non-adminstrators
- if (!user.getUser().isAdministrator())
+ // Enforce password age only for non-privileged users
+ if (!user.isPrivileged())
passwordPolicyService.verifyPasswordAge(object);
// Always verify password complexity
@@ -626,8 +626,8 @@
List<ActivityRecordModel> searchResults;
- // Bypass permission checks if the user is a system admin
- if (user.getUser().isAdministrator())
+ // Bypass permission checks if the user is privileged
+ if (user.isPrivileged())
searchResults = userRecordMapper.search(requiredContents,
sortPredicates, limit);
diff --git a/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/user/UserVerificationService.java b/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/user/UserVerificationService.java
index a73d08f..e0cd84b 100644
--- a/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/user/UserVerificationService.java
+++ b/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/user/UserVerificationService.java
@@ -181,12 +181,13 @@
// Update user object
try {
- context.getUserDirectory().update(self);
+ context.getPrivileged().getUserDirectory().update(self);
}
catch (GuacamoleSecurityException e) {
logger.info("User \"{}\" cannot store their TOTP key as they "
- + "lack permission to update their own account. TOTP "
- + "will be disabled for this user.",
+ + "lack permission to update their own account and the "
+ + "TOTP extension was unable to obtain privileged access. "
+ + "TOTP will be disabled for this user.",
self.getIdentifier());
logger.debug("Permission denied to set TOTP key of user "
+ "account.", e);
diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AbstractUserContext.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AbstractUserContext.java
index eb31f7e..c4dbf10 100644
--- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AbstractUserContext.java
+++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AbstractUserContext.java
@@ -254,4 +254,16 @@
public void invalidate() {
}
+ /**
+ * {@inheritDoc}
+ *
+ * <p>This implementation simply returns <code>this</code>. Implementations
+ * that wish to provide additional privileges to extensions requesting
+ * privileged access should override this function.
+ */
+ @Override
+ public UserContext getPrivileged() {
+ return this;
+ }
+
}
diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DelegatingUserContext.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DelegatingUserContext.java
index 9db6adb..85e0259 100644
--- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DelegatingUserContext.java
+++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DelegatingUserContext.java
@@ -152,4 +152,9 @@
userContext.invalidate();
}
+ @Override
+ public UserContext getPrivileged() {
+ return userContext.getPrivileged();
+ }
+
}
diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/UserContext.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/UserContext.java
index ea7c8c4..ccdcaae 100644
--- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/UserContext.java
+++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/UserContext.java
@@ -262,4 +262,29 @@
*/
void invalidate();
+ /**
+ * Returns a user context which provides privileged access. Unlike the
+ * original user context, which is required to enforce its own permissions
+ * and act only within the rights of the associated user, the user context
+ * returned by this function MAY ignore the restrictions that otherwise
+ * limit the current user's access.
+ *
+ * <p>This function is intended to allow extensions which decorate other
+ * extensions to act independently of the restrictions that affect the
+ * current user. This function will only be invoked by extensions and
+ * WILL NOT be invoked directly by the web application. Implementations of
+ * this function MAY still enforce access restrictions, particularly if
+ * they do not want to grant full, unrestricted access to other extensions.
+ *
+ * <p>A default implementation which simply returns <code>this</code> is
+ * provided for compatibility with Apache Guacamole 1.1.0 and older.
+ *
+ * @return
+ * A user context instance which MAY ignore some or all restrictions
+ * which otherwise limit the current user's access.
+ */
+ default UserContext getPrivileged() {
+ return this;
+ }
+
}
diff --git a/guacamole/src/main/webapp/app/groupList/templates/guacGroupList.html b/guacamole/src/main/webapp/app/groupList/templates/guacGroupList.html
index 4c9bb42..40145c8 100644
--- a/guacamole/src/main/webapp/app/groupList/templates/guacGroupList.html
+++ b/guacamole/src/main/webapp/app/groupList/templates/guacGroupList.html
@@ -3,6 +3,7 @@
<script type="text/ng-template" id="nestedItem.html">
<div class="{{item.type}}" ng-if="isVisible(item.type)"
ng-class="{
+ balancer : item.balancing,
expanded : item.expanded,
expandable : item.expandable,
empty : !item.children.length
diff --git a/guacamole/src/main/webapp/app/groupList/types/GroupListItem.js b/guacamole/src/main/webapp/app/groupList/types/GroupListItem.js
index 29bf91b..2255cb4 100644
--- a/guacamole/src/main/webapp/app/groupList/types/GroupListItem.js
+++ b/guacamole/src/main/webapp/app/groupList/types/GroupListItem.js
@@ -145,7 +145,7 @@
});
// If the item is a connection group, generate a connection group identifier
- if (this.type === GroupListItem.Type.CONNECTION_GROUP)
+ if (this.type === GroupListItem.Type.CONNECTION_GROUP && this.balancing)
return ClientIdentifier.toString({
dataSource : this.dataSource,
type : ClientIdentifier.Types.CONNECTION_GROUP,
@@ -158,6 +158,27 @@
};
/**
+ * Returns the relative URL of the client page that connects to the
+ * connection or connection group represented by this GroupListItem.
+ *
+ * @returns {String}
+ * The relative URL of the client page that connects to the
+ * connection or connection group represented by this GroupListItem,
+ * or null if this GroupListItem cannot be connected to.
+ */
+ this.getClientURL = template.getClientURL || function getClientURL() {
+
+ // There is a client page for this item only if it has an
+ // associated client identifier
+ var identifier = this.getClientIdentifier();
+ if (identifier)
+ return '#/client/' + encodeURIComponent(identifier);
+
+ return null;
+
+ };
+
+ /**
* The connection, connection group, or sharing profile whose data is
* exposed within this GroupListItem. If the type of this GroupListItem
* is not one of the types defined by GroupListItem.Type, then this
diff --git a/guacamole/src/main/webapp/app/home/styles/home.css b/guacamole/src/main/webapp/app/home/styles/home.css
index fb01cac..33b867a 100644
--- a/guacamole/src/main/webapp/app/home/styles/home.css
+++ b/guacamole/src/main/webapp/app/home/styles/home.css
@@ -51,6 +51,27 @@
overflow: hidden;
}
-a.home-connection {
+a.home-connection, .empty.balancer a.home-connection-group {
display: block;
}
+
+/* Show only expand/collapse icon for connection groups on home screen ... */
+
+.all-connections .connection-group > .caption .icon {
+ display: none;
+}
+
+.all-connections .connection-group > .caption .icon.expand {
+ display: inline-block;
+}
+
+/* ... except for empty balancing groups, which should be rendered as if they
+ * are connections. */
+
+.all-connections .connection-group.empty.balancer > .caption .icon {
+ display: inline-block;
+}
+
+.all-connections .connection-group.empty.balancer > .caption .icon.expand {
+ display: none;
+}
diff --git a/guacamole/src/main/webapp/app/home/templates/connection.html b/guacamole/src/main/webapp/app/home/templates/connection.html
index b428412..85a7f57 100644
--- a/guacamole/src/main/webapp/app/home/templates/connection.html
+++ b/guacamole/src/main/webapp/app/home/templates/connection.html
@@ -1,5 +1,5 @@
<a class="home-connection"
- ng-href="#/client/{{ item.getClientIdentifier() }}"
+ ng-href="{{ item.getClientURL() }}"
ng-class="{active: item.getActiveConnections()}">
<!-- Connection icon -->
diff --git a/guacamole/src/main/webapp/app/home/templates/connectionGroup.html b/guacamole/src/main/webapp/app/home/templates/connectionGroup.html
index 909aacf..ba0a204 100644
--- a/guacamole/src/main/webapp/app/home/templates/connectionGroup.html
+++ b/guacamole/src/main/webapp/app/home/templates/connectionGroup.html
@@ -1,4 +1,10 @@
-<span class="home-connection-group name">
- <a ng-show="item.balancing" ng-href="#/client/{{ item.getClientIdentifier() }}">{{item.name}}</a>
- <span ng-show="!item.balancing">{{item.name}}</span>
-</span>
+<a class="home-connection-group"
+ ng-href="{{ item.getClientURL() }}">
+
+ <!-- Connection group icon -->
+ <div ng-show="item.balancing" class="icon type balancer"></div>
+
+ <!-- Connection group name -->
+ <span class="name">{{item.name}}</span>
+
+</a>