GUACAMOLE-1020: Pull effective group membership from the AuthenticatedUser object.

This commit is contained in:
Virtually Nick
2024-09-09 11:15:59 -04:00
parent cc14281f01
commit 1e04d6d366
3 changed files with 62 additions and 40 deletions

View File

@@ -19,8 +19,6 @@
package org.apache.guacamole.auth.restrict; package org.apache.guacamole.auth.restrict;
import com.google.inject.Guice;
import com.google.inject.Injector;
import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.restrict.user.RestrictedUserContext; import org.apache.guacamole.auth.restrict.user.RestrictedUserContext;
import org.apache.guacamole.net.auth.AbstractAuthenticationProvider; import org.apache.guacamole.net.auth.AbstractAuthenticationProvider;
@@ -47,21 +45,15 @@ public class RestrictionAuthenticationProvider extends AbstractAuthenticationPro
String remoteAddress = credentials.getRemoteAddress(); String remoteAddress = credentials.getRemoteAddress();
// Verify identity of user // Verify identity of user
RestrictionVerificationService.verifyLoginRestrictions(context, remoteAddress); RestrictionVerificationService.verifyLoginRestrictions(context,
authenticatedUser.getEffectiveUserGroups(), remoteAddress);
// User has been verified, and authentication should be allowed to // User has been verified, and authentication should be allowed to
// continue // continue
return new RestrictedUserContext(context, remoteAddress); return new RestrictedUserContext(context, remoteAddress,
authenticatedUser.getEffectiveUserGroups());
} }
@Override
public UserContext redecorate(UserContext decorated, UserContext context,
AuthenticatedUser authenticatedUser, Credentials credentials)
throws GuacamoleException {
return new RestrictedUserContext(context, credentials.getRemoteAddress());
}
} }

View File

@@ -26,6 +26,7 @@ import java.net.UnknownHostException;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.restrict.connection.RestrictedConnection; import org.apache.guacamole.auth.restrict.connection.RestrictedConnection;
import org.apache.guacamole.auth.restrict.user.RestrictedUser; import org.apache.guacamole.auth.restrict.user.RestrictedUser;
@@ -214,6 +215,10 @@ public class RestrictionVerificationService {
* @param context * @param context
* The UserContext associated with the user who is being verified. * The UserContext associated with the user who is being verified.
* *
* @param effectiveUserGroups
* The set of identifiers of groups of which the user who is being
* verified is a member.
*
* @param remoteAddress * @param remoteAddress
* The remote address of the client from which the current user is * The remote address of the client from which the current user is
* logged in. * logged in.
@@ -223,7 +228,9 @@ public class RestrictionVerificationService {
* logging in from the current client, or if an error occurs attempting * logging in from the current client, or if an error occurs attempting
* to retrieve permissions. * to retrieve permissions.
*/ */
public static void verifyHostRestrictions(UserContext context, String remoteAddress) throws GuacamoleException { public static void verifyHostRestrictions(UserContext context,
Set<String> effectiveUserGroups, String remoteAddress)
throws GuacamoleException {
// Get the current user // Get the current user
User currentUser = context.self(); User currentUser = context.self();
@@ -250,7 +257,8 @@ public class RestrictionVerificationService {
+ currentUser.getIdentifier() + currentUser.getIdentifier()
+"\" is not allowed to log in from \"" +"\" is not allowed to log in from \""
+ remoteAddress + "\"", + remoteAddress + "\"",
"RESTRICT.ERROR_USER_LOGIN_NOT_ALLOWED_FROM_HOST"); "RESTRICT.ERROR_USER_LOGIN_NOT_ALLOWED_FROM_HOST"
);
// User-level explicit allow means the user is allowed. // User-level explicit allow means the user is allowed.
case EXPLICIT_ALLOW: case EXPLICIT_ALLOW:
@@ -262,7 +270,7 @@ public class RestrictionVerificationService {
Collection<UserGroup> userGroups = context Collection<UserGroup> userGroups = context
.getPrivileged() .getPrivileged()
.getUserGroupDirectory() .getUserGroupDirectory()
.getAll(currentUser.getUserGroups().getObjects()); .getAll(effectiveUserGroups);
// Loop user's effective groups and verify restrictions // Loop user's effective groups and verify restrictions
for (UserGroup userGroup : userGroups) { for (UserGroup userGroup : userGroups) {
@@ -283,7 +291,8 @@ public class RestrictionVerificationService {
+ remoteAddress + remoteAddress
+ "\" due to restrictions on group \"" + "\" due to restrictions on group \""
+ userGroup.getIdentifier() + "\".", + userGroup.getIdentifier() + "\".",
"RESTRICT.ERROR_USER_LOGIN_NOT_ALLOWED_FROM_HOST"); "RESTRICT.ERROR_USER_LOGIN_NOT_ALLOWED_FROM_HOST"
);
// Compare the two, returning the highest-priority restriction so far. // Compare the two, returning the highest-priority restriction so far.
hostRestrictionResult = RestrictionType.getHigherPriority(hostRestrictionResult, grpRestrictionResult); hostRestrictionResult = RestrictionType.getHigherPriority(hostRestrictionResult, grpRestrictionResult);
@@ -294,14 +303,10 @@ public class RestrictionVerificationService {
switch (hostRestrictionResult) { switch (hostRestrictionResult) {
// Explicit allow was the highest result, so we log it and return, allowing the user to be logged in. // Explicit allow was the highest result, so we log it and return, allowing the user to be logged in.
case EXPLICIT_ALLOW: case EXPLICIT_ALLOW:
LOGGER.debug("User \"{}\" is explicitly allowed from host \"{}\".",
currentUser.getIdentifier(), remoteAddress);
return; return;
// Implicit allow was the highest result, so we log it and return, allowing the user to be logged in. // Implicit allow was the highest result, so we log it and return, allowing the user to be logged in.
case IMPLICIT_ALLOW: case IMPLICIT_ALLOW:
LOGGER.debug("User \"{}\" is implicitly allowed from host \"{}\".",
currentUser.getIdentifier(), remoteAddress);
return; return;
} }
@@ -309,7 +314,8 @@ public class RestrictionVerificationService {
throw new TranslatableInvalidHostLoginException("User \"" throw new TranslatableInvalidHostLoginException("User \""
+ currentUser.getIdentifier() + currentUser.getIdentifier()
+ "\" is implicitly denied at this time.", + "\" is implicitly denied at this time.",
"RESTRICT.ERROR_USER_LOGIN_NOT_ALLOWED_FROM_HOST"); "RESTRICT.ERROR_USER_LOGIN_NOT_ALLOWED_FROM_HOST"
);
} }
@@ -329,7 +335,8 @@ public class RestrictionVerificationService {
* If the connection should not be allowed from the remote host from * If the connection should not be allowed from the remote host from
* which the user is logged in. * which the user is logged in.
*/ */
public void verifyHostRestrictions(Restrictable restrictable, String remoteAddress) throws GuacamoleException { public static void verifyHostRestrictions(Restrictable restrictable,
String remoteAddress) throws GuacamoleException {
// Verify time-based restrictions specific to this connection. // Verify time-based restrictions specific to this connection.
String allowedHostsString = restrictable.getAttributes().get(RestrictedConnection.RESTRICT_HOSTS_ALLOWED_ATTRIBUTE_NAME); String allowedHostsString = restrictable.getAttributes().get(RestrictedConnection.RESTRICT_HOSTS_ALLOWED_ATTRIBUTE_NAME);
@@ -355,12 +362,17 @@ public class RestrictionVerificationService {
* The UserContext of the user whose access to Guacamole is being * The UserContext of the user whose access to Guacamole is being
* checked. * checked.
* *
* @param effectiveUserGroups
* The set of identifiers of groups of which the user who is being
* verified is a member.
*
* @throws GuacamoleException * @throws GuacamoleException
* If any of the time constraints configured for the user result in the * If any of the time constraints configured for the user result in the
* user not being allowed to be logged in to Guacamole, or if errors * user not being allowed to be logged in to Guacamole, or if errors
* occur trying to retrieve permissions or attributes. * occur trying to retrieve permissions or attributes.
*/ */
public static void verifyTimeRestrictions(UserContext context) throws GuacamoleException { public static void verifyTimeRestrictions(UserContext context,
Set<String> effectiveUserGroups) throws GuacamoleException {
// Retrieve the current User object associated with the UserContext // Retrieve the current User object associated with the UserContext
User currentUser = context.self(); User currentUser = context.self();
@@ -387,7 +399,8 @@ public class RestrictionVerificationService {
throw new TranslatableInvalidTimeLoginException("User \"" throw new TranslatableInvalidTimeLoginException("User \""
+ currentUser.getIdentifier() + currentUser.getIdentifier()
+ "\" is not allowed to log in at this time.", + "\" is not allowed to log in at this time.",
"RESTRICT.ERROR_USER_LOGIN_NOT_ALLOWED_NOW"); "RESTRICT.ERROR_USER_LOGIN_NOT_ALLOWED_NOW"
);
// User-level explicit allow means the user is allowed. // User-level explicit allow means the user is allowed.
case EXPLICIT_ALLOW: case EXPLICIT_ALLOW:
@@ -399,7 +412,7 @@ public class RestrictionVerificationService {
Collection<UserGroup> userGroups = context Collection<UserGroup> userGroups = context
.getPrivileged() .getPrivileged()
.getUserGroupDirectory() .getUserGroupDirectory()
.getAll(currentUser.getUserGroups().getObjects()); .getAll(effectiveUserGroups);
// Loop user's effective groups and verify restrictions // Loop user's effective groups and verify restrictions
for (UserGroup userGroup : userGroups) { for (UserGroup userGroup : userGroups) {
@@ -418,7 +431,8 @@ public class RestrictionVerificationService {
+ currentUser.getIdentifier() + currentUser.getIdentifier()
+"\" is not allowed to log in at this time due to restrictions on group \"" +"\" is not allowed to log in at this time due to restrictions on group \""
+ userGroup + "\".", + userGroup + "\".",
"RESTRICT.ERROR_USER_LOGIN_NOT_ALLOWED_NOW"); "RESTRICT.ERROR_USER_LOGIN_NOT_ALLOWED_NOW"
);
// Compare the two, returning the highest-priority restriction so far. // Compare the two, returning the highest-priority restriction so far.
timeRestrictionResult = RestrictionType.getHigherPriority(timeRestrictionResult, grpRestrictionResult); timeRestrictionResult = RestrictionType.getHigherPriority(timeRestrictionResult, grpRestrictionResult);
@@ -428,17 +442,19 @@ public class RestrictionVerificationService {
switch (timeRestrictionResult) { switch (timeRestrictionResult) {
// Explicit allow was the highest result, so we log it and return, allowing the user to be logged in. // Explicit allow was the highest result, so we log it and return, allowing the user to be logged in.
case EXPLICIT_ALLOW: case EXPLICIT_ALLOW:
LOGGER.debug("User \"{}\" is explicitly allowed at this time.", currentUser.getIdentifier());
return; return;
// Implicit allow was the highest result, so we log it and return, allowing the user to be logged in. // Implicit allow was the highest result, so we log it and return, allowing the user to be logged in.
case IMPLICIT_ALLOW: case IMPLICIT_ALLOW:
LOGGER.debug("User \"{}\" is implicitly allowed at this time.", currentUser.getIdentifier());
return; return;
} }
// If we reach, here, we've reached an implict deny, so we throw an exception. // If we reach, here, we've reached an implict deny, so we throw an exception.
throw new TranslatableInvalidTimeLoginException("User \"{}\" is implicitly denied at this time.", currentUser.getIdentifier()); throw new TranslatableInvalidTimeLoginException("User \""
+ currentUser.getIdentifier()
+ "\" is implicitly denied at this time.",
"RESTRICT.ERROR_USER_LOGIN_NOT_ALLOWED_NOW"
);
} }
@@ -476,6 +492,10 @@ public class RestrictionVerificationService {
* @param context * @param context
* The context of the user who is attempting to log in. * The context of the user who is attempting to log in.
* *
* @param effectiveUserGroups
* The identifiers of the UserGroups of which the user who is logging
* in is a member.
*
* @param remoteAddress * @param remoteAddress
* The remote address of the client from which the current user is * The remote address of the client from which the current user is
* logged in. * logged in.
@@ -483,10 +503,12 @@ public class RestrictionVerificationService {
* @throws GuacamoleException * @throws GuacamoleException
* If any of the restrictions should prevent the user from logging in. * If any of the restrictions should prevent the user from logging in.
*/ */
public static void verifyLoginRestrictions(UserContext context, String remoteAddress) throws GuacamoleException { public static void verifyLoginRestrictions(UserContext context,
Set<String> effectiveUserGroups, String remoteAddress)
throws GuacamoleException {
verifyTimeRestrictions(context); verifyTimeRestrictions(context, effectiveUserGroups);
verifyHostRestrictions(context, remoteAddress); verifyHostRestrictions(context, effectiveUserGroups, remoteAddress);
} }
@@ -507,13 +529,10 @@ public class RestrictionVerificationService {
* If any of the restrictions should prevent the connection from being * If any of the restrictions should prevent the connection from being
* used by the user at the current time. * used by the user at the current time.
*/ */
public void verifyConnectionRestrictions(Restrictable restrictable, String remoteAddress) public static void verifyConnectionRestrictions(Restrictable restrictable,
throws GuacamoleException { String remoteAddress) throws GuacamoleException {
verifyTimeRestrictions(restrictable); verifyTimeRestrictions(restrictable);
verifyHostRestrictions(restrictable, remoteAddress); verifyHostRestrictions(restrictable, remoteAddress);
} }
} }

View File

@@ -22,6 +22,7 @@ package org.apache.guacamole.auth.restrict.user;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set;
import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.restrict.RestrictionVerificationService; import org.apache.guacamole.auth.restrict.RestrictionVerificationService;
import org.apache.guacamole.auth.restrict.connection.RestrictedConnection; import org.apache.guacamole.auth.restrict.connection.RestrictedConnection;
@@ -55,6 +56,11 @@ public class RestrictedUserContext extends DelegatingUserContext {
*/ */
private final String remoteAddress; private final String remoteAddress;
/**
* The identifiers effective groups of the user associated with this context.
*/
private final Set<String> effectiveUserGroups;
/** /**
* Creates a new RestrictedUserContext which wraps the given UserContext, * Creates a new RestrictedUserContext which wraps the given UserContext,
* providing additional control for user logins and connections. * providing additional control for user logins and connections.
@@ -64,10 +70,15 @@ public class RestrictedUserContext extends DelegatingUserContext {
* *
* @param remoteAddress * @param remoteAddress
* The address the user is logging in from, if known. * The address the user is logging in from, if known.
*
* @param effectiveUserGroups
* The identifiers of the groups this user is associated with.
*/ */
public RestrictedUserContext(UserContext userContext, String remoteAddress) { public RestrictedUserContext(UserContext userContext, String remoteAddress,
Set<String> effectiveUserGroups) {
super(userContext); super(userContext);
this.remoteAddress = remoteAddress; this.remoteAddress = remoteAddress;
this.effectiveUserGroups = effectiveUserGroups;
} }
@Override @Override
@@ -174,7 +185,7 @@ public class RestrictedUserContext extends DelegatingUserContext {
public boolean isValid() { public boolean isValid() {
try { try {
// Verify whether or not time restrictions still apply. // Verify whether or not time restrictions still apply.
RestrictionVerificationService.verifyTimeRestrictions(this); RestrictionVerificationService.verifyTimeRestrictions(this, effectiveUserGroups);
return true; return true;
} }
catch (GuacamoleException e) { catch (GuacamoleException e) {