Skip to content

Commit

Permalink
GUACAMOLE-1701: Implement connection date and time before and after r…
Browse files Browse the repository at this point in the history
…estrictions.
  • Loading branch information
necouchman committed Oct 14, 2024
1 parent bbede31 commit ea0022e
Show file tree
Hide file tree
Showing 11 changed files with 376 additions and 137 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,65 @@
*/
public interface Restrictable extends Attributes {

/**
* The name of the attribute that contains the absolute date and time after
* which this restrictable object may be used. If this attribute is present
* access to to this object will be denied at any time prior to the parsed
* value of this attribute, regardless of what other restrictions may be
* present to allow access to the object at certain days/times of the week
* or from certain hosts.
*/
public static final String RESTRICT_TIME_AFTER_ATTRIBUTE_NAME = "guac-restrict-time-after";

/**
* The name of the attribute that contains a list of weekdays and times (UTC)
* that this restrictable object can be used. The presence of values within
* this attribute will automatically restrict use of the object at any times
* that are not specified.
*/
public static final String RESTRICT_TIME_ALLOWED_ATTRIBUTE_NAME = "guac-restrict-time-allowed";

/**
* The name of the attribute that contains the absolute date and time before
* which use of this restrictable object may be used. If this attribute is
* present use of the object will be denied at any time after the parsed
* value of this attribute, regardless of the presence of other restrictions
* that may allow access at certain days/times of the week or from certain
* hosts.
*/
public static final String RESTRICT_TIME_BEFORE_ATTRIBUTE_NAME = "guac-restrict-time-before";

/**
* The name of the attribute that contains a list of weekdays and times (UTC)
* that this restrictable object cannot be used. Denied times will always take
* precedence over allowed times. The presence of this attribute without
* guac-restrict-time-allowed will deny access only during the times listed
* in this attribute, allowing access at all other times. The presence of
* this attribute along with the guac-restrict-time-allowed attribute will
* deny access at any times that overlap with the allowed times.
*/
public static final String RESTRICT_TIME_DENIED_ATTRIBUTE_NAME = "guac-restrict-time-denied";

/**
* The name of the attribute that contains a list of hosts from which this
* restrictable object may be used. The presence of this attribute will
* restrict use to only users accessing Guacamole from the list of hosts
* contained in the attribute, subject to further restriction by the
* guac-restrict-hosts-denied attribute.
*/
public static final String RESTRICT_HOSTS_ALLOWED_ATTRIBUTE_NAME = "guac-restrict-hosts-allowed";

/**
* The name of the attribute that contains a list of hosts from which this
* restrictable object may not be used. The presence of this attribute,
* absent the guac-restrict-hosts-allowed attribute, will allow use from
* all hosts except the ones listed in this attribute. The presence of this
* attribute coupled with the guac-restrict-hosts-allowed attribute will
* block access from any IPs in this list, overriding any that may be
* allowed.
*/
public static final String RESTRICT_HOSTS_DENIED_ATTRIBUTE_NAME = "guac-restrict-hosts-denied";

/**
* Return the restriction state for this restrictable object at the
* current date and time. By default returns an implicit denial.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,14 @@
import inet.ipaddr.HostNameException;
import inet.ipaddr.IPAddress;
import java.net.UnknownHostException;
import java.text.ParseException;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.restrict.connection.RestrictedConnection;
import org.apache.guacamole.auth.restrict.user.RestrictedUser;
import org.apache.guacamole.auth.restrict.usergroup.RestrictedUserGroup;
import org.apache.guacamole.auth.restrict.form.DateTimeRestrictionField;
import org.apache.guacamole.calendar.DailyRestriction;
import org.apache.guacamole.calendar.RestrictionType;
import org.apache.guacamole.calendar.TimeRestrictionParser;
Expand All @@ -54,6 +54,67 @@ public class RestrictionVerificationService {
*/
private static final Logger LOGGER = LoggerFactory.getLogger(RestrictionVerificationService.class);

/**
* Given the provided strings of an absolute date after which an action is
* valid and before which an action is valid, parse the strings into Date
* objects and determine if the current date and time falls within the
* provided window, returning the appropriate restriction type.
*
* @param afterTimeString
* The string that has the date and time value after which the activity
* is allowed.
*
* @param beforeTimeString
* The string that has the date and time value before which the activity
* is allowed.
*
* @return
* The RestrictionType that represents the allowed or denied state of
* the activity.
*/
private static RestrictionType allowedByDateTimeRestrictions(
String afterTimeString, String beforeTimeString) {

// Set a default restriction.
RestrictionType dateTimeRestriction = RestrictionType.IMPLICIT_ALLOW;

// Check the after string and make sure that now is after that date.
if (afterTimeString != null && !afterTimeString.isEmpty()) {
Date now = new Date();
try {
Date afterTime = DateTimeRestrictionField.parse(afterTimeString);
if (now.before(afterTime))
return RestrictionType.EXPLICIT_DENY;
}
catch (ParseException e) {
LOGGER.warn("Failed to parse date and time string: {}:", e.getMessage());
LOGGER.debug("Parse exception while parsing date and time string.", e);
return RestrictionType.IMPLICIT_DENY;
}
dateTimeRestriction = RestrictionType.EXPLICIT_ALLOW;
}

// Check the before string and make sure that now is prior to that date.
if (beforeTimeString != null && !beforeTimeString.isEmpty()) {
Date now = new Date();
try {
Date beforeTime = DateTimeRestrictionField.parse(beforeTimeString);
if (now.after(beforeTime))
return RestrictionType.EXPLICIT_DENY;
}
catch (ParseException e) {
LOGGER.warn("Failed to parse date and time string: {}:", e.getMessage());
LOGGER.debug("Parse exception while parsing date and time string.", e);
return RestrictionType.IMPLICIT_DENY;
}
dateTimeRestriction = RestrictionType.EXPLICIT_ALLOW;

}

// No restrictions present, so we allow the connection.
return dateTimeRestriction;
}

/**
* Parse out the provided strings of allowed and denied times, verifying
* whether or not a login or connection should be allowed at the current
Expand Down Expand Up @@ -251,8 +312,8 @@ public static void verifyHostRestrictions(UserContext context,
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);
String allowedHostString = userAttributes.get(Restrictable.RESTRICT_HOSTS_ALLOWED_ATTRIBUTE_NAME);
String deniedHostString = userAttributes.get(Restrictable.RESTRICT_HOSTS_DENIED_ATTRIBUTE_NAME);
RestrictionType hostRestrictionResult = allowedByHostRestrictions(allowedHostString, deniedHostString, remoteAddress);

switch (hostRestrictionResult) {
Expand Down Expand Up @@ -284,8 +345,8 @@ public static void verifyHostRestrictions(UserContext context,
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);
String grpAllowedHostString = grpAttributes.get(Restrictable.RESTRICT_HOSTS_ALLOWED_ATTRIBUTE_NAME);
String grpDeniedHostString = grpAttributes.get(Restrictable.RESTRICT_HOSTS_DENIED_ATTRIBUTE_NAME);
RestrictionType grpRestrictionResult = allowedByHostRestrictions(grpAllowedHostString, grpDeniedHostString, remoteAddress);

// Any explicit denials are thrown immediately
Expand Down Expand Up @@ -344,8 +405,8 @@ public static 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);
String allowedHostsString = restrictable.getAttributes().get(Restrictable.RESTRICT_HOSTS_ALLOWED_ATTRIBUTE_NAME);
String deniedHostsString = restrictable.getAttributes().get(Restrictable.RESTRICT_HOSTS_DENIED_ATTRIBUTE_NAME);
RestrictionType hostRestrictionResult = allowedByHostRestrictions(allowedHostsString, deniedHostsString, remoteAddress);

// If the host is not allowed
Expand Down Expand Up @@ -393,8 +454,8 @@ public static void verifyTimeRestrictions(UserContext context,
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);
String allowedTimeString = userAttributes.get(Restrictable.RESTRICT_TIME_ALLOWED_ATTRIBUTE_NAME);
String deniedTimeString = userAttributes.get(Restrictable.RESTRICT_TIME_DENIED_ATTRIBUTE_NAME);
RestrictionType timeRestrictionResult = allowedByTimeRestrictions(allowedTimeString, deniedTimeString);

// Check the time restriction for explicit results.
Expand Down Expand Up @@ -426,8 +487,8 @@ public static void verifyTimeRestrictions(UserContext context,
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);
String grpAllowedTimeString = grpAttributes.get(Restrictable.RESTRICT_TIME_ALLOWED_ATTRIBUTE_NAME);
String grpDeniedTimeString = grpAttributes.get(Restrictable.RESTRICT_TIME_DENIED_ATTRIBUTE_NAME);
RestrictionType grpRestrictionResult = allowedByTimeRestrictions(grpAllowedTimeString, grpDeniedTimeString);

// An explicit deny results in immediate denial of the login.
Expand Down Expand Up @@ -463,6 +524,18 @@ public static void verifyTimeRestrictions(UserContext context,

}

public static void verifyDateTimeRestrictions(Restrictable restrictable) throws GuacamoleException {

String afterTimeString = restrictable.getAttributes().get(Restrictable.RESTRICT_TIME_AFTER_ATTRIBUTE_NAME);
String beforeTimeString = restrictable.getAttributes().get(Restrictable.RESTRICT_TIME_BEFORE_ATTRIBUTE_NAME);
RestrictionType dateRestriction = allowedByDateTimeRestrictions(afterTimeString, beforeTimeString);
if (!dateRestriction.isAllowed())
throw new TranslatableGuacamoleSecurityException(
"Use of this connection or connection group is not allowed at this time.",
"RESTRICT.ERROR_CONNECTION_NOT_ALLOWED_NOW"
);
}

/**
* Verify the time restrictions for the given Connection object, throwing
* an exception if the connection should not be allowed, or silently
Expand All @@ -478,8 +551,8 @@ public static void verifyTimeRestrictions(UserContext context,
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);
String allowedTimeString = restrictable.getAttributes().get(Restrictable.RESTRICT_TIME_ALLOWED_ATTRIBUTE_NAME);
String deniedTimeString = restrictable.getAttributes().get(Restrictable.RESTRICT_TIME_DENIED_ATTRIBUTE_NAME);
RestrictionType timeRestriction = allowedByTimeRestrictions(allowedTimeString, deniedTimeString);
if (!timeRestriction.isAllowed())
throw new TranslatableGuacamoleSecurityException(
Expand Down Expand Up @@ -536,6 +609,7 @@ public static void verifyLoginRestrictions(UserContext context,
*/
public static void verifyConnectionRestrictions(Restrictable restrictable,
String remoteAddress) throws GuacamoleException {
verifyDateTimeRestrictions(restrictable);
verifyTimeRestrictions(restrictable);
verifyHostRestrictions(restrictable, remoteAddress);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
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.form.DateTimeRestrictionField;
import org.apache.guacamole.auth.restrict.form.HostRestrictionField;
import org.apache.guacamole.auth.restrict.form.TimeRestrictionField;
import org.apache.guacamole.calendar.RestrictionType;
Expand All @@ -46,49 +47,12 @@ public class RestrictedConnection extends DelegatingConnection implements Restri
*/
private final String remoteAddress;

/**
* The name of the attribute that contains a list of weekdays and times (UTC)
* that this connection can be accessed. The presence of values within this
* attribute will automatically restrict use of the connections at any
* times that are not specified.
*/
public static final String RESTRICT_TIME_ALLOWED_ATTRIBUTE_NAME = "guac-restrict-time-allowed";

/**
* The name of the attribute that contains a list of weekdays and times (UTC)
* that this connection cannot be accessed. Denied times will always take
* precedence over allowed times. The presence of this attribute without
* guac-restrict-time-allowed will deny access only during the times listed
* in this attribute, allowing access at all other times. The presence of
* this attribute along with the guac-restrict-time-allowed attribute will
* deny access at any times that overlap with the allowed times.
*/
public static final String RESTRICT_TIME_DENIED_ATTRIBUTE_NAME = "guac-restrict-time-denied";

/**
* The name of the attribute that contains a list of hosts from which a user
* may access this connection. The presence of this attribute will restrict
* access to only users accessing Guacamole from the list of hosts contained
* in the attribute, subject to further restriction by the
* guac-restrict-hosts-denied attribute.
*/
public static final String RESTRICT_HOSTS_ALLOWED_ATTRIBUTE_NAME = "guac-restrict-hosts-allowed";

/**
* The name of the attribute that contains a list of hosts from which
* a user may not access this connection. The presence of this attribute,
* absent the guac-restrict-hosts-allowed attribute, will allow access from
* all hosts except the ones listed in this attribute. The presence of this
* attribute coupled with the guac-restrict-hosts-allowed attribute will
* block access from any IPs in this list, overriding any that may be
* allowed.
*/
public static final String RESTRICT_HOSTS_DENIED_ATTRIBUTE_NAME = "guac-restrict-hosts-denied";

/**
* The list of all connection attributes provided by this Connection implementation.
*/
public static final List<String> RESTRICT_CONNECTION_ATTRIBUTES = Arrays.asList(
RESTRICT_TIME_AFTER_ATTRIBUTE_NAME,
RESTRICT_TIME_BEFORE_ATTRIBUTE_NAME,
RESTRICT_TIME_ALLOWED_ATTRIBUTE_NAME,
RESTRICT_TIME_DENIED_ATTRIBUTE_NAME,
RESTRICT_HOSTS_ALLOWED_ATTRIBUTE_NAME,
Expand All @@ -101,6 +65,8 @@ public class RestrictedConnection extends DelegatingConnection implements Restri
*/
public static final Form RESTRICT_CONNECTION_FORM = new Form("restrict-login-form",
Arrays.asList(
new DateTimeRestrictionField(RESTRICT_TIME_AFTER_ATTRIBUTE_NAME),
new DateTimeRestrictionField(RESTRICT_TIME_BEFORE_ATTRIBUTE_NAME),
new TimeRestrictionField(RESTRICT_TIME_ALLOWED_ATTRIBUTE_NAME),
new TimeRestrictionField(RESTRICT_TIME_DENIED_ATTRIBUTE_NAME),
new HostRestrictionField(RESTRICT_HOSTS_ALLOWED_ATTRIBUTE_NAME),
Expand Down
Loading

0 comments on commit ea0022e

Please sign in to comment.