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 646bf20d3..1bb2c68e7 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 @@ public class JDBCAuthenticationProviderService implements AuthenticationProvider // 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 1e525710d..2a4cd9ee5 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 @@ public class ActiveConnectionPermissionService // 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 records = tunnelService.getActiveConnections(user); // We have READ, and possibly DELETE, on all active connections - Set permissions = new HashSet(); + Set 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 c21e9c36c..046cee1e2 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 @@ public class ActiveConnectionService Collection identifiers) throws GuacamoleException { String username = user.getIdentifier(); - boolean isAdmin = user.getUser().isAdministrator(); + boolean isPrivileged = user.isPrivileged(); Set identifierSet = new HashSet(identifiers); // Retrieve all visible connections (permissions enforced by tunnel service) @@ -95,7 +95,7 @@ public class ActiveConnectionService // 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 @@ public class ActiveConnectionService 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 f517e2788..76903134c 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 @@ public abstract class ModeledChildDirectoryObjectService 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 abstract class ModeledDirectoryObjectService 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 965062cc0..be5ac1503 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 @@ public abstract class ModeledPermissions } /** - * 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 f7b75ef12..810e9a5f1 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 @@ public abstract class RelatedObjectSet 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 @@ public abstract class RelatedObjectSet 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 e2f3c1523..926df32fa 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 @@ public class ConnectionService extends ModeledChildDirectoryObjectService 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 3e9ec72a8..dbf7793ec 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 @@ public class ConnectionGroupService extends ModeledChildDirectoryObjectService 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 @@ public abstract class ModeledObjectPermissionService 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 5e5a43bc4..4b8269f6b 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 @@ public class SystemPermissionService ModeledPermissions targetEntity, Collection 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 models = getModelInstances(targetEntity, permissions); systemPermissionMapper.insert(models); return; @@ -111,8 +112,9 @@ public class SystemPermissionService ModeledPermissions targetEntity, Collection 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 abecf32f8..383ef3a9b 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 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS if (records.isEmpty()) return Collections.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 5778ad01a..7ede92c6c 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 com.google.common.collect.Sets; 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 @@ public class ModeledAuthenticatedUser extends RemoteAuthenticatedUser { 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 e98a25a04..fe60a5faf 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.ConnectionGroup; 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; /** @@ -117,6 +118,12 @@ public class ModeledUserContext extends RestrictedObject @Inject private Provider userRecordSetProvider; + /** + * Provider for retrieving UserContext instances. + */ + @Inject + private Provider userContextProvider; + /** * Mapper for user login records. */ @@ -124,7 +131,10 @@ public class ModeledUserContext extends RestrictedObject 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 @@ public class ModeledUserContext extends RestrictedObject 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 @@ public class ModeledUserContext extends RestrictedObject @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 000000000..82fcf0852 --- /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 a68f08237..0aecd10fa 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 @@ public class UserService extends ModeledDirectoryObjectService 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 a73d08f96..e0cd84b3d 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 @@ public class UserVerificationService { // 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 eb31f7edb..c4dbf10b0 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 abstract class AbstractUserContext implements UserContext { public void invalidate() { } + /** + * {@inheritDoc} + * + *

This implementation simply returns this. 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 9db6adb79..85e025909 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 @@ public class DelegatingUserContext implements UserContext { 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 ea7c8c40e..ccdcaae09 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 @@ public interface UserContext { */ 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. + * + *

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. + * + *

A default implementation which simply returns this 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; + } + }