GUACAMOLE-1020: Implement a new Restrictable interface and make slight name changes to classes.

This commit is contained in:
Virtually Nick
2024-07-29 09:07:29 -04:00
parent 8ad254e89f
commit 042b99bede
9 changed files with 641 additions and 183 deletions

View File

@@ -0,0 +1,67 @@
/*
* 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.restrict;
import org.apache.guacamole.calendar.RestrictionType;
import org.apache.guacamole.net.auth.Attributes;
/**
* An interface which defines methods that apply to items that can have
* restrictions applied to them.
*/
public interface Restrictable extends Attributes {
/**
* Return the restriction state for this restrictable object at the
* current date and time. By default returns an implicit denial.
*
* @return
* The restriction status for the current date and time.
*/
default public RestrictionType getCurrentTimeRestriction() {
return RestrictionType.IMPLICIT_DENY;
}
/**
* Return the restriction state for this restrictable object for the host
* from which the current user is logged in. By default returns an implicit
* denial.
*
* @return
* The restriction status for the host from which the current user is
* logged in.
*/
default public RestrictionType getCurrentHostRestriction() {
return RestrictionType.IMPLICIT_DENY;
}
/**
* Returns true if the current item is available based on the restrictions
* for the given implementation of this interface, or false if the item is
* not currently available. The default implementation checks current time
* and host restrictions, allowing if both those restrictions allow access.
*
* @return
* true if the item is available, otherwise false.
*/
default public boolean isAvailable() {
return (getCurrentTimeRestriction().isAllowed() && getCurrentHostRestriction().isAllowed());
}
}

View File

@@ -19,8 +19,10 @@
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.RestrictUserContext; import org.apache.guacamole.auth.restrict.user.RestrictedUserContext;
import org.apache.guacamole.net.auth.AbstractAuthenticationProvider; import org.apache.guacamole.net.auth.AbstractAuthenticationProvider;
import org.apache.guacamole.net.auth.AuthenticatedUser; import org.apache.guacamole.net.auth.AuthenticatedUser;
import org.apache.guacamole.net.auth.Credentials; import org.apache.guacamole.net.auth.Credentials;
@@ -32,7 +34,7 @@ import org.apache.guacamole.net.auth.UserContext;
* administrators to further control access to Guacamole resources. * administrators to further control access to Guacamole resources.
*/ */
public class RestrictionAuthenticationProvider extends AbstractAuthenticationProvider { public class RestrictionAuthenticationProvider extends AbstractAuthenticationProvider {
@Override @Override
public String getIdentifier() { public String getIdentifier() {
return "restrict"; return "restrict";
@@ -43,12 +45,15 @@ public class RestrictionAuthenticationProvider extends AbstractAuthenticationPro
AuthenticatedUser authenticatedUser, Credentials credentials) AuthenticatedUser authenticatedUser, Credentials credentials)
throws GuacamoleException { throws GuacamoleException {
String remoteAddress = credentials.getRemoteAddress();
// Verify identity of user // Verify identity of user
RestrictionVerificationService.verifyLoginRestrictions(context, authenticatedUser); RestrictionVerificationService.verifyLoginRestrictions(context, 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 RestrictUserContext(context, credentials.getRemoteAddress()); return new RestrictedUserContext(context, remoteAddress);
} }
@@ -56,7 +61,7 @@ public class RestrictionAuthenticationProvider extends AbstractAuthenticationPro
public UserContext redecorate(UserContext decorated, UserContext context, public UserContext redecorate(UserContext decorated, UserContext context,
AuthenticatedUser authenticatedUser, Credentials credentials) AuthenticatedUser authenticatedUser, Credentials credentials)
throws GuacamoleException { throws GuacamoleException {
return new RestrictUserContext(context, credentials.getRemoteAddress()); return new RestrictedUserContext(context, credentials.getRemoteAddress());
} }
} }

View File

@@ -23,19 +23,19 @@ import inet.ipaddr.HostName;
import inet.ipaddr.HostNameException; import inet.ipaddr.HostNameException;
import inet.ipaddr.IPAddress; import inet.ipaddr.IPAddress;
import java.net.UnknownHostException; import java.net.UnknownHostException;
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.RestrictConnection; import org.apache.guacamole.auth.restrict.connection.RestrictedConnection;
import org.apache.guacamole.auth.restrict.user.RestrictUser; import org.apache.guacamole.auth.restrict.user.RestrictedUser;
import org.apache.guacamole.auth.restrict.usergroup.RestrictUserGroup; import org.apache.guacamole.auth.restrict.usergroup.RestrictedUserGroup;
import org.apache.guacamole.calendar.DailyRestriction; import org.apache.guacamole.calendar.DailyRestriction;
import org.apache.guacamole.calendar.RestrictionType;
import org.apache.guacamole.calendar.TimeRestrictionParser; import org.apache.guacamole.calendar.TimeRestrictionParser;
import org.apache.guacamole.host.HostRestrictionParser; import org.apache.guacamole.host.HostRestrictionParser;
import org.apache.guacamole.language.TranslatableGuacamoleSecurityException; import org.apache.guacamole.language.TranslatableGuacamoleSecurityException;
import org.apache.guacamole.net.auth.AuthenticatedUser; import org.apache.guacamole.net.auth.User;
import org.apache.guacamole.net.auth.Directory;
import org.apache.guacamole.net.auth.UserContext; import org.apache.guacamole.net.auth.UserContext;
import org.apache.guacamole.net.auth.UserGroup; import org.apache.guacamole.net.auth.UserGroup;
import org.apache.guacamole.net.auth.permission.SystemPermission; import org.apache.guacamole.net.auth.permission.SystemPermission;
@@ -56,8 +56,7 @@ public class RestrictionVerificationService {
/** /**
* Parse out the provided strings of allowed and denied times, verifying * Parse out the provided strings of allowed and denied times, verifying
* whether or not a login or connection should be allowed at the current * whether or not a login or connection should be allowed at the current
* day and time. A boolean true will be returned if the action should be * day and time, and returning the appropriate restriction type.
* allowed, otherwise false will be returned.
* *
* @param allowedTimeString * @param allowedTimeString
* The string containing the times that should be parsed to determine if * The string containing the times that should be parsed to determine if
@@ -71,47 +70,48 @@ public class RestrictionVerificationService {
* action should be denied. * action should be denied.
* *
* @return * @return
* True if the login or connection should be allowed, otherwise false. * A RestrictionType based on the provided allowed and denied strings.
*/ */
private static boolean allowedByTimeRestrictions(String allowedTimeString, public static RestrictionType allowedByTimeRestrictions(String allowedTimeString,
String deniedTimeString) { String deniedTimeString) {
// Check for denied entries, first, returning false if the login or // Check for denied entries, first, returning the explicit deny if the
// connection should not be allowed. // login or connection should not be allowed.
if (deniedTimeString != null && !deniedTimeString.isEmpty()) { if (deniedTimeString != null && !deniedTimeString.isEmpty()) {
List<DailyRestriction> deniedTimes = List<DailyRestriction> deniedTimes =
TimeRestrictionParser.parseString(deniedTimeString); TimeRestrictionParser.parseString(deniedTimeString);
for (DailyRestriction restriction : deniedTimes) { for (DailyRestriction restriction : deniedTimes) {
if (restriction.appliesNow()) if (restriction.appliesNow())
return false; return RestrictionType.EXPLICIT_DENY;
} }
} }
// If no allowed entries are present, return true, allowing the login // If no allowed entries are present, return the implicit allow, allowing
// or connection to continue. // the login or connection to continue.
if (allowedTimeString == null || allowedTimeString.isEmpty()) if (allowedTimeString == null || allowedTimeString.isEmpty())
return true; return RestrictionType.IMPLICIT_ALLOW;
// Pull the list of allowed times.
List<DailyRestriction> allowedTimes = List<DailyRestriction> allowedTimes =
TimeRestrictionParser.parseString(allowedTimeString); TimeRestrictionParser.parseString(allowedTimeString);
// Allowed entries are present, loop through them and check for a valid time. // Allowed entries are present, loop through them and check for a valid time.
for (DailyRestriction restriction : allowedTimes) { for (DailyRestriction restriction : allowedTimes) {
// If this time allows the login or connection return true. // If this time allows the login or connection return the explicit allow.
if (restriction.appliesNow()) if (restriction.appliesNow())
return true; return RestrictionType.EXPLICIT_ALLOW;
} }
// We have allowed entries, but login hasn't matched, so deny it. // We have allowed entries, but login hasn't matched, so implicitly deny it.
return false; return RestrictionType.IMPLICIT_DENY;
} }
/** /**
* Given the strings of allowed and denied hosts, verify that the login or * Given the strings of allowed and denied hosts, verify that the login or
* connection should be allowed from the given remote address. If the action * connection should be allowed from the given remote address, returning
* should not be allowed, return false - otherwise, return true. * the RestrictionType that matches the provided allowed and denied strings.
* *
* @param allowedHostsString * @param allowedHostsString
* The string containing a semicolon-separated list of hosts from * The string containing a semicolon-separated list of hosts from
@@ -129,18 +129,18 @@ public class RestrictionVerificationService {
* and restrictions are defined, the login or connection will be denied. * and restrictions are defined, the login or connection will be denied.
* *
* @return * @return
* True if the login or connection should be allowed by the host-based * A RestrictionType that matches the provided allow and deny strings.
* restrictions, otherwise false.
*/ */
private static boolean allowedByHostRestrictions(String allowedHostsString, public static RestrictionType allowedByHostRestrictions(String allowedHostsString,
String deniedHostsString, String remoteAddress) { String deniedHostsString, String remoteAddress) {
// Convert the string to a HostName
HostName remoteHostName = new HostName(remoteAddress); HostName remoteHostName = new HostName(remoteAddress);
// If attributes do not exist or are empty then the action is allowed. // If attributes do not exist or are empty then the action is allowed.
if ((allowedHostsString == null || allowedHostsString.isEmpty()) if ((allowedHostsString == null || allowedHostsString.isEmpty())
&& (deniedHostsString == null || deniedHostsString.isEmpty())) && (deniedHostsString == null || deniedHostsString.isEmpty()))
return true; return RestrictionType.IMPLICIT_ALLOW;
// If the remote address cannot be determined, and restrictions are // If the remote address cannot be determined, and restrictions are
// in effect, log an error and deny the action. // in effect, log an error and deny the action.
@@ -148,7 +148,7 @@ public class RestrictionVerificationService {
LOGGER.warn("Host-based restrictions are present, but the remote " LOGGER.warn("Host-based restrictions are present, but the remote "
+ "address is invalid or could not be resolved. " + "address is invalid or could not be resolved. "
+ "The action will not be allowed."); + "The action will not be allowed.");
return false; return RestrictionType.IMPLICIT_DENY;
} }
// Split denied hosts attribute and process each entry, checking them // Split denied hosts attribute and process each entry, checking them
@@ -158,24 +158,24 @@ public class RestrictionVerificationService {
for (HostName hostName : deniedHosts) { for (HostName hostName : deniedHosts) {
try { try {
if (hostName.isAddress() && hostName.toAddress().contains(remoteHostName.asAddress())) if (hostName.isAddress() && hostName.toAddress().contains(remoteHostName.asAddress()))
return false; return RestrictionType.EXPLICIT_DENY;
else else
for (IPAddress currAddr : hostName.toAllAddresses()) for (IPAddress currAddr : hostName.toAllAddresses())
if (currAddr.matches(remoteHostName.asAddressString())) if (currAddr.matches(remoteHostName.asAddressString()))
return false; return RestrictionType.EXPLICIT_DENY;
} }
catch (UnknownHostException | HostNameException e) { catch (UnknownHostException | HostNameException e) {
LOGGER.warn("Unknown or invalid host in denied hosts list: \"{}\"", hostName); LOGGER.warn("Unknown or invalid host in denied hosts list: \"{}\"", hostName);
LOGGER.debug("Exception while trying to resolve host: \"{}\"", hostName, e); LOGGER.debug("Exception while trying to resolve host: \"{}\"", hostName, e);
return false; return RestrictionType.IMPLICIT_DENY;
} }
} }
// If denied hosts have been checked and allowed hosts are empty, we're // If denied hosts have been checked and allowed hosts are empty, we're
// good, and can allow the action. // good, and can allow the action.
if (allowedHostsString == null || allowedHostsString.isEmpty()) if (allowedHostsString == null || allowedHostsString.isEmpty())
return true; return RestrictionType.IMPLICIT_ALLOW;
// Run through allowed hosts, if there are any, and return, allowing the // Run through allowed hosts, if there are any, and return, allowing the
// action if there are any matches. // action if there are any matches.
@@ -184,12 +184,12 @@ public class RestrictionVerificationService {
try { try {
// If the entry is an IP or Subnet, check the remote address against it directly // If the entry is an IP or Subnet, check the remote address against it directly
if (hostName.isAddress() && hostName.toAddress().contains(remoteHostName.asAddress())) if (hostName.isAddress() && hostName.toAddress().contains(remoteHostName.asAddress()))
return true; return RestrictionType.EXPLICIT_ALLOW;
// Entry is a hostname, so resolve to IPs and check each one // Entry is a hostname, so resolve to IPs and check each one
for (IPAddress currAddr : hostName.toAllAddresses()) for (IPAddress currAddr : hostName.toAllAddresses())
if (currAddr.matches(remoteHostName.asAddressString())) if (currAddr.matches(remoteHostName.asAddressString()))
return true; return RestrictionType.EXPLICIT_ALLOW;
} }
// If an entry cannot be resolved we will log a warning. // If an entry cannot be resolved we will log a warning.
@@ -201,7 +201,270 @@ public class RestrictionVerificationService {
// If we've made it here, the allowed hosts do not contain the remote // If we've made it here, the allowed hosts do not contain the remote
// address, and the action should not be allowed; // address, and the action should not be allowed;
return false; return RestrictionType.IMPLICIT_DENY;
}
/**
* Verify the host restrictions for the user associated with the given
* UserContext, throwing an exception if any of the restrictions result
* in the user not being allowed to be logged in to Guacamole from this
* host.
*
* @param context
* The UserContext associated with the user who is being verified.
*
* @param remoteAddress
* The remote address of the client from which the current user is
* logged in.
*
* @throws GuacamoleException
* If the restrictions on the user should prevent the user from
* logging in from the current client, or if an error occurs attempting
* to retrieve permissions.
*/
public static void verifyHostRestrictions(UserContext context, String remoteAddress) throws GuacamoleException {
// Get the current user
User currentUser = context.self();
// Admins always have access.
if (currentUser.getEffectivePermissions().getSystemPermissions().hasPermission(SystemPermission.Type.ADMINISTER)) {
LOGGER.warn("User \"{}\" has System Administration permissions; additional restrictions will be bypassed.",
currentUser.getIdentifier());
return;
}
// Get user's attributes
Map<String, String> userAttributes = currentUser.getAttributes();
// Verify host-based restrictions specific to the user
String allowedHostString = userAttributes.get(RestrictedUser.RESTRICT_HOSTS_ALLOWED_ATTRIBUTE_NAME);
String deniedHostString = userAttributes.get(RestrictedUser.RESTRICT_HOSTS_DENIED_ATTRIBUTE_NAME);
RestrictionType hostRestrictionResult = allowedByHostRestrictions(allowedHostString, deniedHostString, remoteAddress);
switch (hostRestrictionResult) {
// User-level explicit deny overrides everything
case EXPLICIT_DENY:
throw new TranslatableInvalidHostLoginException("User \""
+ currentUser.getIdentifier()
+"\" is not allowed to log in from \""
+ remoteAddress + "\"",
"RESTRICT.ERROR_USER_LOGIN_NOT_ALLOWED_FROM_HOST");
// User-level explicit allow means the user is allowed.
case EXPLICIT_ALLOW:
return;
}
// Gather user's effective groups.
Collection<UserGroup> userGroups = context
.getPrivileged()
.getUserGroupDirectory()
.getAll(currentUser.getUserGroups().getObjects());
// Loop user's effective groups and verify restrictions
for (UserGroup userGroup : userGroups) {
// Get group's attributes
Map<String, String> grpAttributes = userGroup.getAttributes();
// Pull host-based restrictions for this group and verify
String grpAllowedHostString = grpAttributes.get(RestrictedUserGroup.RESTRICT_HOSTS_ALLOWED_ATTRIBUTE_NAME);
String grpDeniedHostString = grpAttributes.get(RestrictedUserGroup.RESTRICT_HOSTS_DENIED_ATTRIBUTE_NAME);
RestrictionType grpRestrictionResult = allowedByHostRestrictions(grpAllowedHostString, grpDeniedHostString, remoteAddress);
// Any explicit denials are thrown immediately
if (grpRestrictionResult == RestrictionType.EXPLICIT_DENY)
throw new TranslatableInvalidHostLoginException("User \""
+ currentUser.getIdentifier()
+ "\" is not allowed to log in from host \""
+ remoteAddress
+ "\" due to restrictions on group \""
+ userGroup.getIdentifier() + "\".",
"RESTRICT.ERROR_USER_LOGIN_NOT_ALLOWED_FROM_HOST");
// Compare the two, returning the highest-priority restriction so far.
hostRestrictionResult = RestrictionType.getHigherPriority(hostRestrictionResult, grpRestrictionResult);
}
// Check the result and log allowed
switch (hostRestrictionResult) {
// Explicit allow was the highest result, so we log it and return, allowing the user to be logged in.
case EXPLICIT_ALLOW:
LOGGER.debug("User \"{}\" is explicitly allowed from host \"{}\".",
currentUser.getIdentifier(), remoteAddress);
return;
// Implicit allow was the highest result, so we log it and return, allowing the user to be logged in.
case IMPLICIT_ALLOW:
LOGGER.debug("User \"{}\" is implicitly allowed from host \"{}\".",
currentUser.getIdentifier(), remoteAddress);
return;
}
// If we reach, here, we've reached an implict deny, so we throw an exception.
throw new TranslatableInvalidHostLoginException("User \""
+ currentUser.getIdentifier()
+ "\" is implicitly denied at this time.",
"RESTRICT.ERROR_USER_LOGIN_NOT_ALLOWED_FROM_HOST");
}
/**
* Verify the host-based restrictions of the Connection, throwing an
* exception if the Connection should be allowed from the host from which
* the user is logged in.
*
* @param restrictable
* The Restrictable object that should be verified against host restrictions.
*
* @param remoteAddress
* The remote address of the client from which the current user is
* logged in.
*
* @throws GuacamoleException
* If the connection should not be allowed from the remote host from
* which the user is logged in.
*/
public void verifyHostRestrictions(Restrictable restrictable, String remoteAddress) throws GuacamoleException {
// Verify time-based restrictions specific to this connection.
String allowedHostsString = restrictable.getAttributes().get(RestrictedConnection.RESTRICT_HOSTS_ALLOWED_ATTRIBUTE_NAME);
String deniedHostsString = restrictable.getAttributes().get(RestrictedConnection.RESTRICT_HOSTS_DENIED_ATTRIBUTE_NAME);
RestrictionType hostRestrictionResult = allowedByHostRestrictions(allowedHostsString, deniedHostsString, remoteAddress);
// If the host is not allowed
if (!hostRestrictionResult.isAllowed())
throw new TranslatableGuacamoleSecurityException(
"Use of this connection is not allowed from this remote host: \"" + remoteAddress + "\".",
"RESTRICT.ERROR_CONNECTION_NOT_ALLOWED_NOW"
);
}
/**
* Verifies the time restrictions for this extension and whether or not the
* account should be allowed to be logged in to Guacamole at the current
* day and time, throwing an exception if any of the restrictions result
* in a violation of the time constraints of the account.
*
* @param context
* The UserContext of the user whose access to Guacamole is being
* checked.
*
* @throws GuacamoleException
* 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
* occur trying to retrieve permissions or attributes.
*/
public static void verifyTimeRestrictions(UserContext context) throws GuacamoleException {
// Retrieve the current User object associated with the UserContext
User currentUser = context.self();
// Admins always have access.
if (currentUser.getEffectivePermissions().getSystemPermissions().hasPermission(SystemPermission.Type.ADMINISTER)) {
LOGGER.warn("User \"{}\" has System Administration permissions; additional restrictions will be bypassed.",
currentUser.getIdentifier());
return;
}
// Get user's attributes
Map<String, String> userAttributes = currentUser.getAttributes();
// Verify time-based restrictions specific to the user
String allowedTimeString = userAttributes.get(RestrictedUser.RESTRICT_TIME_ALLOWED_ATTRIBUTE_NAME);
String deniedTimeString = userAttributes.get(RestrictedUser.RESTRICT_TIME_DENIED_ATTRIBUTE_NAME);
RestrictionType timeRestrictionResult = allowedByTimeRestrictions(allowedTimeString, deniedTimeString);
// Check the time restriction for explicit results.
switch (timeRestrictionResult) {
// User-level explicit deny overrides everything
case EXPLICIT_DENY:
throw new TranslatableInvalidTimeLoginException("User \""
+ currentUser.getIdentifier()
+ "\" is not allowed to log in at this time.",
"RESTRICT.ERROR_USER_LOGIN_NOT_ALLOWED_NOW");
// User-level explicit allow means the user is allowed.
case EXPLICIT_ALLOW:
return;
}
// Gather user's effective groups.
Collection<UserGroup> userGroups = context
.getPrivileged()
.getUserGroupDirectory()
.getAll(currentUser.getUserGroups().getObjects());
// Loop user's effective groups and verify restrictions
for (UserGroup userGroup : userGroups) {
// Get group's attributes
Map<String, String> grpAttributes = userGroup.getAttributes();
// Pull time-based restrictions for this group and verify
String grpAllowedTimeString = grpAttributes.get(RestrictedUserGroup.RESTRICT_TIME_ALLOWED_ATTRIBUTE_NAME);
String grpDeniedTimeString = grpAttributes.get(RestrictedUserGroup.RESTRICT_TIME_DENIED_ATTRIBUTE_NAME);
RestrictionType grpRestrictionResult = allowedByTimeRestrictions(grpAllowedTimeString, grpDeniedTimeString);
// An explicit deny results in immediate denial of the login.
if (grpRestrictionResult == RestrictionType.EXPLICIT_DENY)
throw new TranslatableInvalidTimeLoginException("User \""
+ currentUser.getIdentifier()
+"\" is not allowed to log in at this time due to restrictions on group \""
+ userGroup + "\".",
"RESTRICT.ERROR_USER_LOGIN_NOT_ALLOWED_NOW");
// Compare the two, returning the highest-priority restriction so far.
timeRestrictionResult = RestrictionType.getHigherPriority(timeRestrictionResult, grpRestrictionResult);
}
switch (timeRestrictionResult) {
// Explicit allow was the highest result, so we log it and return, allowing the user to be logged in.
case EXPLICIT_ALLOW:
LOGGER.debug("User \"{}\" is explicitly allowed at this time.", currentUser.getIdentifier());
return;
// Implicit allow was the highest result, so we log it and return, allowing the user to be logged in.
case IMPLICIT_ALLOW:
LOGGER.debug("User \"{}\" is implicitly allowed at this time.", currentUser.getIdentifier());
return;
}
// 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());
}
/**
* Verify the time restrictions for the given Connection object, throwing
* an exception if the connection should not be allowed, or silently
* returning if the connection should be allowed.
*
* @param restrictable
* The item that supports restrictions that is to be verified against
* the current time.
*
* @throws GuacamoleException
* If the connection should not be allowed at the current time.
*/
public static void verifyTimeRestrictions(Restrictable restrictable) throws GuacamoleException {
// Verify time-based restrictions specific to this connection.
String allowedTimeString = restrictable.getAttributes().get(RestrictedConnection.RESTRICT_TIME_ALLOWED_ATTRIBUTE_NAME);
String deniedTimeString = restrictable.getAttributes().get(RestrictedConnection.RESTRICT_TIME_DENIED_ATTRIBUTE_NAME);
RestrictionType timeRestriction = allowedByTimeRestrictions(allowedTimeString, deniedTimeString);
if (!timeRestriction.isAllowed())
throw new TranslatableGuacamoleSecurityException(
"Use of this connection or connection group is not allowed at this time.",
"RESTRICT.ERROR_CONNECTION_NOT_ALLOWED_NOW"
);
} }
@@ -213,80 +476,17 @@ 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 authenticatedUser * @param remoteAddress
* The AuthenticatedUser object associated with the user who is * The remote address of the client from which the current user is
* attempting to log in. * logged in.
* *
* @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, public static void verifyLoginRestrictions(UserContext context, String remoteAddress) throws GuacamoleException {
AuthenticatedUser authenticatedUser) throws GuacamoleException {
// Get user's attributes verifyTimeRestrictions(context);
Map<String, String> userAttributes = context.self().getAttributes(); verifyHostRestrictions(context, remoteAddress);
String remoteAddress = authenticatedUser.getCredentials().getRemoteAddress();
if (context.self().getEffectivePermissions().getSystemPermissions().hasPermission(SystemPermission.Type.ADMINISTER)) {
LOGGER.warn("User \"{}\" has System Administration permissions; additional restrictions will be bypassed.",
authenticatedUser.getIdentifier());
return;
}
// Verify time-based restrictions specific to the user
String allowedTimeString = userAttributes.get(RestrictUser.RESTRICT_TIME_ALLOWED_ATTRIBUTE_NAME);
String deniedTimeString = userAttributes.get(RestrictUser.RESTRICT_TIME_DENIED_ATTRIBUTE_NAME);
if (!allowedByTimeRestrictions(allowedTimeString, deniedTimeString))
throw new TranslatableInvalidTimeLoginException("User \""
+ authenticatedUser.getIdentifier()
+ "\" is not allowed to log in at this time.",
"RESTRICT.ERROR_USER_LOGIN_NOT_ALLOWED_NOW");
// Verify host-based restrictions specific to the user
String allowedHostString = userAttributes.get(RestrictUser.RESTRICT_HOSTS_ALLOWED_ATTRIBUTE_NAME);
String deniedHostString = userAttributes.get(RestrictUser.RESTRICT_HOSTS_DENIED_ATTRIBUTE_NAME);
if (!allowedByHostRestrictions(allowedHostString, deniedHostString, remoteAddress))
throw new TranslatableInvalidHostLoginException("User \""
+ authenticatedUser.getIdentifier()
+"\" is not allowed to log in from \""
+ remoteAddress + "\"",
"RESTRICT.ERROR_USER_LOGIN_NOT_ALLOWED_FROM_HOST");
// Gather user's effective groups.
Set<String> userGroups = authenticatedUser.getEffectiveUserGroups();
Directory<UserGroup> directoryGroups = context.getPrivileged().getUserGroupDirectory();
// Loop user's effective groups and verify restrictions
for (String userGroup : userGroups) {
UserGroup thisGroup = directoryGroups.get(userGroup);
if (thisGroup == null) {
continue;
}
// Get group's attributes
Map<String, String> grpAttributes = thisGroup.getAttributes();
// Pull time-based restrictions for this group and verify
String grpAllowedTimeString = grpAttributes.get(RestrictUserGroup.RESTRICT_TIME_ALLOWED_ATTRIBUTE_NAME);
String grpDeniedTimeString = grpAttributes.get(RestrictUserGroup.RESTRICT_TIME_DENIED_ATTRIBUTE_NAME);
if (!allowedByTimeRestrictions(grpAllowedTimeString, grpDeniedTimeString))
throw new TranslatableInvalidTimeLoginException("User \""
+ authenticatedUser.getIdentifier()
+"\" is not allowed to log in at this time due to restrictions on group \""
+ userGroup + "\".",
"RESTRICT.ERROR_USER_LOGIN_NOT_ALLOWED_NOW");
// Pull host-based restrictions for this group and verify
String grpAllowedHostString = grpAttributes.get(RestrictUserGroup.RESTRICT_HOSTS_ALLOWED_ATTRIBUTE_NAME);
String grpDeniedHostString = grpAttributes.get(RestrictUserGroup.RESTRICT_HOSTS_DENIED_ATTRIBUTE_NAME);
if (!allowedByHostRestrictions(grpAllowedHostString, grpDeniedHostString, remoteAddress))
throw new TranslatableInvalidHostLoginException("User \""
+ authenticatedUser.getIdentifier()
+ "\" is not allowed to log in from this host due to restrictions on group \""
+ userGroup + "\".",
"RESTRICT.ERROR_USER_LOGIN_NOT_ALLOWED_FROM_HOST");
}
} }
@@ -295,38 +495,24 @@ public class RestrictionVerificationService {
* connection the user is attempting to access, throwing an exception if * connection the user is attempting to access, throwing an exception if
* any of the restrictions result in the connection being unavailable. * any of the restrictions result in the connection being unavailable.
* *
* @param connectionAttributes * @param restrictable
* The attributes of the connection that may contain any additional * The object that supports restrictions that is to be verified to be
* restrictions on use of the connection. * usable within the current restrictions.
* *
* @param remoteAddress * @param remoteAddress
* The remote IP address of the user trying to access the connection. * The remote address of the client from which the current user is
* logged in.
* *
* @throws GuacamoleException * @throws GuacamoleException
* 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 static void verifyConnectionRestrictions( public void verifyConnectionRestrictions(Restrictable restrictable, String remoteAddress)
Map<String, String> connectionAttributes, String remoteAddress)
throws GuacamoleException { throws GuacamoleException {
// Verify time-based restrictions specific to this connection. verifyTimeRestrictions(restrictable);
String allowedTimeString = connectionAttributes.get(RestrictConnection.RESTRICT_TIME_ALLOWED_ATTRIBUTE_NAME); verifyHostRestrictions(restrictable, remoteAddress);
String deniedTimeString = connectionAttributes.get(RestrictConnection.RESTRICT_TIME_DENIED_ATTRIBUTE_NAME);
if (!allowedByTimeRestrictions(allowedTimeString, deniedTimeString))
throw new TranslatableGuacamoleSecurityException(
"Use of this connection is not allowed at this time.",
"RESTRICT.ERROR_CONNECTION_NOT_ALLOWED_NOW"
);
// Verify host-based restrictions specific to this connection.
String allowedHostString = connectionAttributes.get(RestrictConnection.RESTRICT_HOSTS_ALLOWED_ATTRIBUTE_NAME);
String deniedHostString = connectionAttributes.get(RestrictConnection.RESTRICT_HOSTS_DENIED_ATTRIBUTE_NAME);
if (!allowedByHostRestrictions(allowedHostString, deniedHostString, remoteAddress))
throw new TranslatableGuacamoleSecurityException(
"Use of this connection is not allowed from this remote host: \"" + remoteAddress + "\".",
"RESTRICT.ERROR_CONNECTION_NOT_ALLOWED_NOW"
);
} }

View File

@@ -19,14 +19,17 @@
package org.apache.guacamole.auth.restrict.connection; package org.apache.guacamole.auth.restrict.connection;
import com.google.inject.Inject;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.restrict.Restrictable;
import org.apache.guacamole.auth.restrict.RestrictionVerificationService; import org.apache.guacamole.auth.restrict.RestrictionVerificationService;
import org.apache.guacamole.auth.restrict.form.HostRestrictionField; import org.apache.guacamole.auth.restrict.form.HostRestrictionField;
import org.apache.guacamole.auth.restrict.form.TimeRestrictionField; import org.apache.guacamole.auth.restrict.form.TimeRestrictionField;
import org.apache.guacamole.calendar.RestrictionType;
import org.apache.guacamole.form.Form; import org.apache.guacamole.form.Form;
import org.apache.guacamole.net.GuacamoleTunnel; import org.apache.guacamole.net.GuacamoleTunnel;
import org.apache.guacamole.net.auth.Connection; import org.apache.guacamole.net.auth.Connection;
@@ -37,7 +40,18 @@ import org.apache.guacamole.protocol.GuacamoleClientInformation;
* A Connection implementation that wraps another connection, providing additional * A Connection implementation that wraps another connection, providing additional
* ability to control access to the connection. * ability to control access to the connection.
*/ */
public class RestrictConnection extends DelegatingConnection { public class RestrictedConnection extends DelegatingConnection implements Restrictable {
/**
* The remote address of the client from which the user logged in.
*/
private final String remoteAddress;
/**
* The restriction verification service.
*/
@Inject
private RestrictionVerificationService verificationService;
/** /**
* The name of the attribute that contains a list of weekdays and times (UTC) * The name of the attribute that contains a list of weekdays and times (UTC)
@@ -101,12 +115,6 @@ public class RestrictConnection extends DelegatingConnection {
) )
); );
/**
* The remote address from which the user attempting to access this
* connection logged in.
*/
private final String remoteAddress;
/** /**
* Wraps the given Connection object, providing capability of further * Wraps the given Connection object, providing capability of further
* restricting connection access beyond the default access control provided * restricting connection access beyond the default access control provided
@@ -116,10 +124,10 @@ public class RestrictConnection extends DelegatingConnection {
* The Connection object to wrap. * The Connection object to wrap.
* *
* @param remoteAddress * @param remoteAddress
* The remote address from which the user attempting to access this * The remote address of the client from which the current user logged
* connection logged in. * in.
*/ */
public RestrictConnection(Connection connection, String remoteAddress) { public RestrictedConnection(Connection connection, String remoteAddress) {
super(connection); super(connection);
this.remoteAddress = remoteAddress; this.remoteAddress = remoteAddress;
} }
@@ -175,11 +183,25 @@ public class RestrictConnection extends DelegatingConnection {
Map<String, String> tokens) throws GuacamoleException { Map<String, String> tokens) throws GuacamoleException {
// Verify the restrictions for this connection. // Verify the restrictions for this connection.
RestrictionVerificationService.verifyConnectionRestrictions(getAttributes(), remoteAddress); verificationService.verifyConnectionRestrictions(this, remoteAddress);
// Connect // Connect
return super.connect(info, tokens); return super.connect(info, tokens);
} }
@Override
public RestrictionType getCurrentTimeRestriction() {
String allowedTimeString = getAttributes().get(RESTRICT_TIME_ALLOWED_ATTRIBUTE_NAME);
String deniedTimeString = getAttributes().get(RESTRICT_TIME_DENIED_ATTRIBUTE_NAME);
return RestrictionVerificationService.allowedByTimeRestrictions(allowedTimeString, deniedTimeString);
}
@Override
public RestrictionType getCurrentHostRestriction() {
String allowedHostString = getAttributes().get(RESTRICT_HOSTS_ALLOWED_ATTRIBUTE_NAME);
String deniedHostString = getAttributes().get(RESTRICT_HOSTS_DENIED_ATTRIBUTE_NAME);
return RestrictionVerificationService.allowedByHostRestrictions(allowedHostString, deniedHostString, remoteAddress);
}
} }

View File

@@ -19,14 +19,17 @@
package org.apache.guacamole.auth.restrict.connectiongroup; package org.apache.guacamole.auth.restrict.connectiongroup;
import com.google.inject.Inject;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.restrict.Restrictable;
import org.apache.guacamole.auth.restrict.RestrictionVerificationService; import org.apache.guacamole.auth.restrict.RestrictionVerificationService;
import org.apache.guacamole.auth.restrict.form.HostRestrictionField; import org.apache.guacamole.auth.restrict.form.HostRestrictionField;
import org.apache.guacamole.auth.restrict.form.TimeRestrictionField; import org.apache.guacamole.auth.restrict.form.TimeRestrictionField;
import org.apache.guacamole.calendar.RestrictionType;
import org.apache.guacamole.form.Form; import org.apache.guacamole.form.Form;
import org.apache.guacamole.net.GuacamoleTunnel; import org.apache.guacamole.net.GuacamoleTunnel;
import org.apache.guacamole.net.auth.ConnectionGroup; import org.apache.guacamole.net.auth.ConnectionGroup;
@@ -37,7 +40,18 @@ import org.apache.guacamole.protocol.GuacamoleClientInformation;
* A ConnectionGroup implementation that wraps an existing ConnectionGroup, * A ConnectionGroup implementation that wraps an existing ConnectionGroup,
* providing additional ability to control access to the ConnectionGroup. * providing additional ability to control access to the ConnectionGroup.
*/ */
public class RestrictConnectionGroup extends DelegatingConnectionGroup { public class RestrictedConnectionGroup extends DelegatingConnectionGroup implements Restrictable {
/**
* The remote address of the client from which the current user logged in.
*/
private final String remoteAddress;
/**
* The verification service.
*/
@Inject
private RestrictionVerificationService verificationService;
/** /**
* The name of the attribute that contains a list of weekdays and times (UTC) * The name of the attribute that contains a list of weekdays and times (UTC)
@@ -102,12 +116,6 @@ public class RestrictConnectionGroup extends DelegatingConnectionGroup {
) )
); );
/**
* The remote address from which the user accessing this connection group
* logged in.
*/
private final String remoteAddress;
/** /**
* Wraps the given ConnectionGroup object, providing capability of further * Wraps the given ConnectionGroup object, providing capability of further
* restricting connection group access beyond the default access control * restricting connection group access beyond the default access control
@@ -117,10 +125,10 @@ public class RestrictConnectionGroup extends DelegatingConnectionGroup {
* The ConnectionGroup object to wrap. * The ConnectionGroup object to wrap.
* *
* @param remoteAddress * @param remoteAddress
* The remote address from which the user accessing this connection * The remote address of the client from which the current user logged
* logged in. * in.
*/ */
public RestrictConnectionGroup(ConnectionGroup connectionGroup, String remoteAddress) { public RestrictedConnectionGroup(ConnectionGroup connectionGroup, String remoteAddress) {
super(connectionGroup); super(connectionGroup);
this.remoteAddress = remoteAddress; this.remoteAddress = remoteAddress;
} }
@@ -177,11 +185,25 @@ public class RestrictConnectionGroup extends DelegatingConnectionGroup {
Map<String, String> tokens) throws GuacamoleException { Map<String, String> tokens) throws GuacamoleException {
// Verify restrictions for this connection group. // Verify restrictions for this connection group.
RestrictionVerificationService.verifyConnectionRestrictions(getAttributes(), remoteAddress); verificationService.verifyConnectionRestrictions(this, remoteAddress);
// Connect // Connect
return super.connect(info, tokens); return super.connect(info, tokens);
} }
@Override
public RestrictionType getCurrentTimeRestriction() {
String allowedTimeString = getAttributes().get(RESTRICT_TIME_ALLOWED_ATTRIBUTE_NAME);
String deniedTimeString = getAttributes().get(RESTRICT_TIME_DENIED_ATTRIBUTE_NAME);
return RestrictionVerificationService.allowedByTimeRestrictions(allowedTimeString, deniedTimeString);
}
@Override
public RestrictionType getCurrentHostRestriction() {
String allowedHostString = getAttributes().get(RESTRICT_HOSTS_ALLOWED_ATTRIBUTE_NAME);
String deniedHostString = getAttributes().get(RESTRICT_HOSTS_DENIED_ATTRIBUTE_NAME);
return RestrictionVerificationService.allowedByHostRestrictions(allowedHostString, deniedHostString, remoteAddress);
}
} }

View File

@@ -23,8 +23,11 @@ import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.apache.guacamole.auth.restrict.Restrictable;
import org.apache.guacamole.auth.restrict.RestrictionVerificationService;
import org.apache.guacamole.auth.restrict.form.HostRestrictionField; import org.apache.guacamole.auth.restrict.form.HostRestrictionField;
import org.apache.guacamole.auth.restrict.form.TimeRestrictionField; import org.apache.guacamole.auth.restrict.form.TimeRestrictionField;
import org.apache.guacamole.calendar.RestrictionType;
import org.apache.guacamole.form.Form; import org.apache.guacamole.form.Form;
import org.apache.guacamole.net.auth.DelegatingUser; import org.apache.guacamole.net.auth.DelegatingUser;
import org.apache.guacamole.net.auth.User; import org.apache.guacamole.net.auth.User;
@@ -33,8 +36,13 @@ import org.apache.guacamole.net.auth.User;
* User implementation which wraps a User from another extension and enforces * User implementation which wraps a User from another extension and enforces
* additional restrictions. * additional restrictions.
*/ */
public class RestrictUser extends DelegatingUser { public class RestrictedUser extends DelegatingUser implements Restrictable {
/**
* The remote address of the client from which the current user is logged in.
*/
private final String remoteAddress;
/** /**
* The name of the attribute that contains a list of weekdays and times (UTC) * The name of the attribute that contains a list of weekdays and times (UTC)
* that a user is allowed to log in. The presence of this attribute will * that a user is allowed to log in. The presence of this attribute will
@@ -97,16 +105,20 @@ public class RestrictUser extends DelegatingUser {
) )
); );
/** /**
* Wraps the given User object, providing capability of further restricting * Wraps the given User object, providing capability of further restricting
* logins beyond the default restrictions provided by default modules. * logins beyond the default restrictions provided by default modules.
* *
* @param user * @param user
* The User object to wrap. * The User object to wrap.
*
* @param remoteAddress
* The remote address of the client from which the current user is logged
* in.
*/ */
public RestrictUser(User user) { public RestrictedUser(User user, String remoteAddress) {
super(user); super(user);
this.remoteAddress = remoteAddress;
} }
/** /**
@@ -154,5 +166,19 @@ public class RestrictUser extends DelegatingUser {
super.setAttributes(attributes); super.setAttributes(attributes);
} }
@Override
public RestrictionType getCurrentTimeRestriction() {
String allowedTimeString = getAttributes().get(RESTRICT_TIME_ALLOWED_ATTRIBUTE_NAME);
String deniedTimeString = getAttributes().get(RESTRICT_TIME_DENIED_ATTRIBUTE_NAME);
return RestrictionVerificationService.allowedByTimeRestrictions(allowedTimeString, deniedTimeString);
}
@Override
public RestrictionType getCurrentHostRestriction() {
String allowedHostString = getAttributes().get(RESTRICT_HOSTS_ALLOWED_ATTRIBUTE_NAME);
String deniedHostString = getAttributes().get(RESTRICT_HOSTS_DENIED_ATTRIBUTE_NAME);
return RestrictionVerificationService.allowedByHostRestrictions(allowedHostString, deniedHostString, remoteAddress);
}
} }

View File

@@ -23,9 +23,10 @@ import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.restrict.connection.RestrictConnection; import org.apache.guacamole.auth.restrict.RestrictionVerificationService;
import org.apache.guacamole.auth.restrict.connectiongroup.RestrictConnectionGroup; import org.apache.guacamole.auth.restrict.connection.RestrictedConnection;
import org.apache.guacamole.auth.restrict.usergroup.RestrictUserGroup; import org.apache.guacamole.auth.restrict.connectiongroup.RestrictedConnectionGroup;
import org.apache.guacamole.auth.restrict.usergroup.RestrictedUserGroup;
import org.apache.guacamole.form.Form; import org.apache.guacamole.form.Form;
import org.apache.guacamole.net.auth.Connection; import org.apache.guacamole.net.auth.Connection;
import org.apache.guacamole.net.auth.ConnectionGroup; import org.apache.guacamole.net.auth.ConnectionGroup;
@@ -35,20 +36,27 @@ import org.apache.guacamole.net.auth.Directory;
import org.apache.guacamole.net.auth.User; import org.apache.guacamole.net.auth.User;
import org.apache.guacamole.net.auth.UserContext; import org.apache.guacamole.net.auth.UserContext;
import org.apache.guacamole.net.auth.UserGroup; import org.apache.guacamole.net.auth.UserGroup;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** /**
* A UserContext implementation for additional login and connection restrictions * A UserContext implementation for additional login and connection restrictions
* which wraps the UserContext of some other extension. * which wraps the UserContext of some other extension.
*/ */
public class RestrictUserContext extends DelegatingUserContext { public class RestrictedUserContext extends DelegatingUserContext {
/**
* The logger for this class.
*/
private static final Logger LOGGER = LoggerFactory.getLogger(RestrictedUserContext.class);
/** /**
* The remote address from which this user logged in. * The remote address from which this user logged in.
*/ */
private final String remoteAddress; private final String remoteAddress;
/** /**
* Creates a new RestrictUserContext 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.
* *
* @param userContext * @param userContext
@@ -57,7 +65,7 @@ public class RestrictUserContext 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.
*/ */
public RestrictUserContext(UserContext userContext, String remoteAddress) { public RestrictedUserContext(UserContext userContext, String remoteAddress) {
super(userContext); super(userContext);
this.remoteAddress = remoteAddress; this.remoteAddress = remoteAddress;
} }
@@ -67,14 +75,14 @@ public class RestrictUserContext extends DelegatingUserContext {
return new DecoratingDirectory<Connection>(super.getConnectionDirectory()) { return new DecoratingDirectory<Connection>(super.getConnectionDirectory()) {
@Override @Override
protected Connection decorate(Connection object) { protected Connection decorate(Connection object) throws GuacamoleException {
return new RestrictConnection(object, remoteAddress); return new RestrictedConnection(object, remoteAddress);
} }
@Override @Override
protected Connection undecorate(Connection object) { protected Connection undecorate(Connection object) {
assert(object instanceof RestrictConnection); assert(object instanceof RestrictedConnection);
return ((RestrictConnection) object).getUndecorated(); return ((RestrictedConnection) object).getUndecorated();
} }
}; };
@@ -83,7 +91,7 @@ public class RestrictUserContext extends DelegatingUserContext {
@Override @Override
public Collection<Form> getConnectionAttributes() { public Collection<Form> getConnectionAttributes() {
Collection<Form> connectionAttrs = new HashSet<>(super.getConnectionAttributes()); Collection<Form> connectionAttrs = new HashSet<>(super.getConnectionAttributes());
connectionAttrs.add(RestrictConnection.RESTRICT_CONNECTION_FORM); connectionAttrs.add(RestrictedConnection.RESTRICT_CONNECTION_FORM);
return Collections.unmodifiableCollection(connectionAttrs); return Collections.unmodifiableCollection(connectionAttrs);
} }
@@ -92,14 +100,14 @@ public class RestrictUserContext extends DelegatingUserContext {
return new DecoratingDirectory<ConnectionGroup>(super.getConnectionGroupDirectory()) { return new DecoratingDirectory<ConnectionGroup>(super.getConnectionGroupDirectory()) {
@Override @Override
protected ConnectionGroup decorate(ConnectionGroup object) { protected ConnectionGroup decorate(ConnectionGroup object) throws GuacamoleException {
return new RestrictConnectionGroup(object, remoteAddress); return new RestrictedConnectionGroup(object, remoteAddress);
} }
@Override @Override
protected ConnectionGroup undecorate(ConnectionGroup object) { protected ConnectionGroup undecorate(ConnectionGroup object) {
assert(object instanceof RestrictConnectionGroup); assert(object instanceof RestrictedConnectionGroup);
return ((RestrictConnectionGroup) object).getUndecorated(); return ((RestrictedConnectionGroup) object).getUndecorated();
} }
}; };
@@ -108,7 +116,7 @@ public class RestrictUserContext extends DelegatingUserContext {
@Override @Override
public Collection<Form> getConnectionGroupAttributes() { public Collection<Form> getConnectionGroupAttributes() {
Collection<Form> connectionGroupAttrs = new HashSet<>(super.getConnectionGroupAttributes()); Collection<Form> connectionGroupAttrs = new HashSet<>(super.getConnectionGroupAttributes());
connectionGroupAttrs.add(RestrictConnectionGroup.RESTRICT_CONNECTIONGROUP_FORM); connectionGroupAttrs.add(RestrictedConnectionGroup.RESTRICT_CONNECTIONGROUP_FORM);
return Collections.unmodifiableCollection(connectionGroupAttrs); return Collections.unmodifiableCollection(connectionGroupAttrs);
} }
@@ -118,13 +126,13 @@ public class RestrictUserContext extends DelegatingUserContext {
@Override @Override
protected User decorate(User object) { protected User decorate(User object) {
return new RestrictUser(object); return new RestrictedUser(object, remoteAddress);
} }
@Override @Override
protected User undecorate(User object) { protected User undecorate(User object) {
assert(object instanceof RestrictUser); assert(object instanceof RestrictedUser);
return ((RestrictUser) object).getUndecorated(); return ((RestrictedUser) object).getUndecorated();
} }
}; };
@@ -133,7 +141,7 @@ public class RestrictUserContext extends DelegatingUserContext {
@Override @Override
public Collection<Form> getUserAttributes() { public Collection<Form> getUserAttributes() {
Collection<Form> userAttrs = new HashSet<>(super.getUserAttributes()); Collection<Form> userAttrs = new HashSet<>(super.getUserAttributes());
userAttrs.add(RestrictUser.RESTRICT_LOGIN_FORM); userAttrs.add(RestrictedUser.RESTRICT_LOGIN_FORM);
return Collections.unmodifiableCollection(userAttrs); return Collections.unmodifiableCollection(userAttrs);
} }
@@ -143,13 +151,13 @@ public class RestrictUserContext extends DelegatingUserContext {
@Override @Override
protected UserGroup decorate(UserGroup object) { protected UserGroup decorate(UserGroup object) {
return new RestrictUserGroup(object); return new RestrictedUserGroup(object);
} }
@Override @Override
protected UserGroup undecorate(UserGroup object) { protected UserGroup undecorate(UserGroup object) {
assert(object instanceof RestrictUserGroup); assert(object instanceof RestrictedUserGroup);
return ((RestrictUserGroup) object).getUndecorated(); return ((RestrictedUserGroup) object).getUndecorated();
} }
}; };
@@ -158,8 +166,21 @@ public class RestrictUserContext extends DelegatingUserContext {
@Override @Override
public Collection<Form> getUserGroupAttributes() { public Collection<Form> getUserGroupAttributes() {
Collection<Form> userGroupAttrs = new HashSet<>(super.getUserGroupAttributes()); Collection<Form> userGroupAttrs = new HashSet<>(super.getUserGroupAttributes());
userGroupAttrs.add(RestrictUserGroup.RESTRICT_LOGIN_FORM); userGroupAttrs.add(RestrictedUserGroup.RESTRICT_LOGIN_FORM);
return Collections.unmodifiableCollection(userGroupAttrs); return Collections.unmodifiableCollection(userGroupAttrs);
} }
@Override
public boolean isValid() {
try {
// Verify whether or not time restrictions still apply.
RestrictionVerificationService.verifyTimeRestrictions(this);
return true;
}
catch (GuacamoleException e) {
LOGGER.debug("User account is now restricted and is no longer valid", e);
return false;
}
}
} }

View File

@@ -33,8 +33,8 @@ import org.apache.guacamole.net.auth.UserGroup;
* UserGroup implementation which wraps a UserGroup from another extension and * UserGroup implementation which wraps a UserGroup from another extension and
* enforces additional restrictions for members of that group. * enforces additional restrictions for members of that group.
*/ */
public class RestrictUserGroup extends DelegatingUserGroup { public class RestrictedUserGroup extends DelegatingUserGroup {
/** /**
* The name of the attribute that contains a list of weekdays and times (UTC) * The name of the attribute that contains a list of weekdays and times (UTC)
* that members of a group are allowed to log in. The presence of this * that members of a group are allowed to log in. The presence of this
@@ -107,7 +107,7 @@ public class RestrictUserGroup extends DelegatingUserGroup {
* @param userGroup * @param userGroup
* The UserGroup object to wrap. * The UserGroup object to wrap.
*/ */
public RestrictUserGroup(UserGroup userGroup) { public RestrictedUserGroup(UserGroup userGroup) {
super(userGroup); super(userGroup);
} }

View File

@@ -0,0 +1,109 @@
/*
* 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.calendar;
/**
* A data type that represents various values of what type of restriction applies
* at a given time.
*/
public enum RestrictionType {
/**
* Access is explicitly allowed.
*/
EXPLICIT_ALLOW(1, true),
/**
* Access is explicitly denied.
*/
EXPLICIT_DENY(0, false),
/**
* Access has not been explicitly allowed or denied, therefore it is
* implicitly allowed.
*/
IMPLICIT_ALLOW(3, true),
/**
* Access has not been explicitly allowed or denied, therefore it is
* implicitly denied.
*/
IMPLICIT_DENY(2, false);
/**
* The overall priority of the restriction, with zero being the highest
* priority and the priority decreasing as numbers increase from zero.
*/
final private int priority;
/**
* true if the restriction allows access, otherwise false.
*/
final private boolean allowed;
/**
* Create the new instance of this RestrictionType, with the given
* priority value for the instance.
*
* @param priority
* The priority of the restriction type, where zero is the highest
* priority.
*
* @param allowed
* true if the restriction allows access, otherwise false.
*/
RestrictionType(int priority, boolean allowed) {
this.priority = priority;
this.allowed = allowed;
}
/**
* Evaluates two restrictions, returning the higher priority of the two.
*
* @param restriction1
* The first restriction to compare.
*
* @param restriction2
* The second restriction to compare.
*
* @return
* Return which of the two restrictions is the higher-priority.
*/
public static RestrictionType getHigherPriority(RestrictionType restriction1, RestrictionType restriction2) {
// If the second is higher than the first, return the second.
if (restriction1.priority > restriction2.priority)
return restriction2;
// Return the first.
return restriction1;
}
/**
* Returns true if this restriction allows access, otherwise false.
*
* @return
* true if this restriction allows access, otherwise false.
*/
public boolean isAllowed() {
return this.allowed;
}
}