From e4e98e959c8a5fac7fa27f48f52f515d1e06174c Mon Sep 17 00:00:00 2001 From: Virtually Nick Date: Mon, 29 Jul 2024 09:07:29 -0400 Subject: [PATCH] GUACAMOLE-1020: Implement a new Restrictable interface and make slight name changes to classes. --- .../guacamole/auth/restrict/Restrictable.java | 67 +++ .../RestrictionAuthenticationProvider.java | 15 +- .../RestrictionVerificationService.java | 398 +++++++++++++----- ...nection.java => RestrictedConnection.java} | 44 +- ...up.java => RestrictedConnectionGroup.java} | 44 +- ...{RestrictUser.java => RestrictedUser.java} | 34 +- ...ontext.java => RestrictedUserContext.java} | 69 +-- ...serGroup.java => RestrictedUserGroup.java} | 6 +- .../guacamole/calendar/RestrictionType.java | 109 +++++ 9 files changed, 622 insertions(+), 164 deletions(-) create mode 100644 extensions/guacamole-auth-restrict/src/main/java/org/apache/guacamole/auth/restrict/Restrictable.java rename extensions/guacamole-auth-restrict/src/main/java/org/apache/guacamole/auth/restrict/connection/{RestrictConnection.java => RestrictedConnection.java} (82%) rename extensions/guacamole-auth-restrict/src/main/java/org/apache/guacamole/auth/restrict/connectiongroup/{RestrictConnectionGroup.java => RestrictedConnectionGroup.java} (82%) rename extensions/guacamole-auth-restrict/src/main/java/org/apache/guacamole/auth/restrict/user/{RestrictUser.java => RestrictedUser.java} (81%) rename extensions/guacamole-auth-restrict/src/main/java/org/apache/guacamole/auth/restrict/user/{RestrictUserContext.java => RestrictedUserContext.java} (66%) rename extensions/guacamole-auth-restrict/src/main/java/org/apache/guacamole/auth/restrict/usergroup/{RestrictUserGroup.java => RestrictedUserGroup.java} (98%) create mode 100644 extensions/guacamole-auth-restrict/src/main/java/org/apache/guacamole/calendar/RestrictionType.java diff --git a/extensions/guacamole-auth-restrict/src/main/java/org/apache/guacamole/auth/restrict/Restrictable.java b/extensions/guacamole-auth-restrict/src/main/java/org/apache/guacamole/auth/restrict/Restrictable.java new file mode 100644 index 0000000000..ff1acf7450 --- /dev/null +++ b/extensions/guacamole-auth-restrict/src/main/java/org/apache/guacamole/auth/restrict/Restrictable.java @@ -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()); + } + +} \ No newline at end of file diff --git a/extensions/guacamole-auth-restrict/src/main/java/org/apache/guacamole/auth/restrict/RestrictionAuthenticationProvider.java b/extensions/guacamole-auth-restrict/src/main/java/org/apache/guacamole/auth/restrict/RestrictionAuthenticationProvider.java index f6f0d36999..a2dfcdf0dc 100644 --- a/extensions/guacamole-auth-restrict/src/main/java/org/apache/guacamole/auth/restrict/RestrictionAuthenticationProvider.java +++ b/extensions/guacamole-auth-restrict/src/main/java/org/apache/guacamole/auth/restrict/RestrictionAuthenticationProvider.java @@ -19,8 +19,10 @@ 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.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.AuthenticatedUser; import org.apache.guacamole.net.auth.Credentials; @@ -32,7 +34,7 @@ * administrators to further control access to Guacamole resources. */ public class RestrictionAuthenticationProvider extends AbstractAuthenticationProvider { - + @Override public String getIdentifier() { return "restrict"; @@ -43,12 +45,15 @@ public UserContext decorate(UserContext context, AuthenticatedUser authenticatedUser, Credentials credentials) throws GuacamoleException { + String remoteAddress = credentials.getRemoteAddress(); + + // Verify identity of user - RestrictionVerificationService.verifyLoginRestrictions(context, authenticatedUser); + RestrictionVerificationService.verifyLoginRestrictions(context, remoteAddress); // User has been verified, and authentication should be allowed to // continue - return new RestrictUserContext(context, credentials.getRemoteAddress()); + return new RestrictedUserContext(context, remoteAddress); } @@ -56,7 +61,7 @@ public UserContext decorate(UserContext context, public UserContext redecorate(UserContext decorated, UserContext context, AuthenticatedUser authenticatedUser, Credentials credentials) throws GuacamoleException { - return new RestrictUserContext(context, credentials.getRemoteAddress()); + return new RestrictedUserContext(context, credentials.getRemoteAddress()); } } diff --git a/extensions/guacamole-auth-restrict/src/main/java/org/apache/guacamole/auth/restrict/RestrictionVerificationService.java b/extensions/guacamole-auth-restrict/src/main/java/org/apache/guacamole/auth/restrict/RestrictionVerificationService.java index 2b09b3023e..eb05e2b354 100644 --- a/extensions/guacamole-auth-restrict/src/main/java/org/apache/guacamole/auth/restrict/RestrictionVerificationService.java +++ b/extensions/guacamole-auth-restrict/src/main/java/org/apache/guacamole/auth/restrict/RestrictionVerificationService.java @@ -23,19 +23,19 @@ import inet.ipaddr.HostNameException; import inet.ipaddr.IPAddress; import java.net.UnknownHostException; +import java.util.Collection; import java.util.List; import java.util.Map; -import java.util.Set; import org.apache.guacamole.GuacamoleException; -import org.apache.guacamole.auth.restrict.connection.RestrictConnection; -import org.apache.guacamole.auth.restrict.user.RestrictUser; -import org.apache.guacamole.auth.restrict.usergroup.RestrictUserGroup; +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.calendar.DailyRestriction; +import org.apache.guacamole.calendar.RestrictionType; import org.apache.guacamole.calendar.TimeRestrictionParser; import org.apache.guacamole.host.HostRestrictionParser; import org.apache.guacamole.language.TranslatableGuacamoleSecurityException; -import org.apache.guacamole.net.auth.AuthenticatedUser; -import org.apache.guacamole.net.auth.Directory; +import org.apache.guacamole.net.auth.User; import org.apache.guacamole.net.auth.UserContext; import org.apache.guacamole.net.auth.UserGroup; 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 * 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 - * allowed, otherwise false will be returned. + * day and time, and returning the appropriate restriction type. * * @param allowedTimeString * The string containing the times that should be parsed to determine if @@ -71,47 +70,48 @@ public class RestrictionVerificationService { * action should be denied. * * @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) { - // Check for denied entries, first, returning false if the login or - // connection should not be allowed. + // Check for denied entries, first, returning the explicit deny if the + // login or connection should not be allowed. if (deniedTimeString != null && !deniedTimeString.isEmpty()) { List deniedTimes = TimeRestrictionParser.parseString(deniedTimeString); for (DailyRestriction restriction : deniedTimes) { if (restriction.appliesNow()) - return false; + return RestrictionType.EXPLICIT_DENY; } } - // If no allowed entries are present, return true, allowing the login - // or connection to continue. + // If no allowed entries are present, return the implicit allow, allowing + // the login or connection to continue. if (allowedTimeString == null || allowedTimeString.isEmpty()) - return true; + return RestrictionType.IMPLICIT_ALLOW; + // Pull the list of allowed times. List allowedTimes = TimeRestrictionParser.parseString(allowedTimeString); // Allowed entries are present, loop through them and check for a valid time. 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()) - return true; + return RestrictionType.EXPLICIT_ALLOW; } - // We have allowed entries, but login hasn't matched, so deny it. - return false; + // We have allowed entries, but login hasn't matched, so implicitly deny it. + return RestrictionType.IMPLICIT_DENY; } /** * 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 - * should not be allowed, return false - otherwise, return true. + * connection should be allowed from the given remote address, returning + * the RestrictionType that matches the provided allowed and denied strings. * * @param allowedHostsString * The string containing a semicolon-separated list of hosts from @@ -129,18 +129,18 @@ private static boolean allowedByTimeRestrictions(String allowedTimeString, * and restrictions are defined, the login or connection will be denied. * * @return - * True if the login or connection should be allowed by the host-based - * restrictions, otherwise false. + * A RestrictionType that matches the provided allow and deny strings. */ - private static boolean allowedByHostRestrictions(String allowedHostsString, + public static RestrictionType allowedByHostRestrictions(String allowedHostsString, String deniedHostsString, String remoteAddress) { + // Convert the string to a HostName HostName remoteHostName = new HostName(remoteAddress); // If attributes do not exist or are empty then the action is allowed. if ((allowedHostsString == null || allowedHostsString.isEmpty()) && (deniedHostsString == null || deniedHostsString.isEmpty())) - return true; + return RestrictionType.IMPLICIT_ALLOW; // If the remote address cannot be determined, and restrictions are // in effect, log an error and deny the action. @@ -148,7 +148,7 @@ private static boolean allowedByHostRestrictions(String allowedHostsString, LOGGER.warn("Host-based restrictions are present, but the remote " + "address is invalid or could not be resolved. " + "The action will not be allowed."); - return false; + return RestrictionType.IMPLICIT_DENY; } // Split denied hosts attribute and process each entry, checking them @@ -158,24 +158,24 @@ private static boolean allowedByHostRestrictions(String allowedHostsString, for (HostName hostName : deniedHosts) { try { if (hostName.isAddress() && hostName.toAddress().contains(remoteHostName.asAddress())) - return false; + return RestrictionType.EXPLICIT_DENY; else for (IPAddress currAddr : hostName.toAllAddresses()) if (currAddr.matches(remoteHostName.asAddressString())) - return false; + return RestrictionType.EXPLICIT_DENY; } catch (UnknownHostException | HostNameException e) { LOGGER.warn("Unknown or invalid host in denied hosts list: \"{}\"", hostName); 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 // good, and can allow the action. if (allowedHostsString == null || allowedHostsString.isEmpty()) - return true; + return RestrictionType.IMPLICIT_ALLOW; // Run through allowed hosts, if there are any, and return, allowing the // action if there are any matches. @@ -184,12 +184,12 @@ private static boolean allowedByHostRestrictions(String allowedHostsString, try { // If the entry is an IP or Subnet, check the remote address against it directly 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 for (IPAddress currAddr : hostName.toAllAddresses()) if (currAddr.matches(remoteHostName.asAddressString())) - return true; + return RestrictionType.EXPLICIT_ALLOW; } // If an entry cannot be resolved we will log a warning. @@ -201,93 +201,293 @@ private static boolean allowedByHostRestrictions(String allowedHostsString, // If we've made it here, the allowed hosts do not contain the remote // address, and the action should not be allowed; - return false; + return RestrictionType.IMPLICIT_DENY; } /** - * Verifies the login restrictions supported by this extension for the user - * who is attempting to log in, throwing an exception if any of the - * restrictions result in the user not being allowed to log in. + * 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 context of the user who is attempting to log in. + * The UserContext associated with the user who is being verified. * - * @param authenticatedUser - * The AuthenticatedUser object associated with the user who is - * attempting to log in. + * @param remoteAddress + * The remote address of the client from which the current user is + * logged in. * * @throws GuacamoleException - * If any of the restrictions should prevent the user from logging in. + * 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 verifyLoginRestrictions(UserContext context, - AuthenticatedUser authenticatedUser) throws GuacamoleException { + public static void verifyHostRestrictions(UserContext context, String remoteAddress) throws GuacamoleException { - // Get user's attributes - Map userAttributes = context.self().getAttributes(); - String remoteAddress = authenticatedUser.getCredentials().getRemoteAddress(); + // Get the current user + User currentUser = context.self(); - if (context.self().getEffectivePermissions().getSystemPermissions().hasPermission(SystemPermission.Type.ADMINISTER)) { + // Admins always have access. + if (currentUser.getEffectivePermissions().getSystemPermissions().hasPermission(SystemPermission.Type.ADMINISTER)) { LOGGER.warn("User \"{}\" has System Administration permissions; additional restrictions will be bypassed.", - authenticatedUser.getIdentifier()); + currentUser.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"); + + // Get user's attributes + Map userAttributes = currentUser.getAttributes(); // 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() + 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. - Set userGroups = authenticatedUser.getEffectiveUserGroups(); - Directory directoryGroups = context.getPrivileged().getUserGroupDirectory(); + Collection userGroups = context + .getPrivileged() + .getUserGroupDirectory() + .getAll(currentUser.getUserGroups().getObjects()); // Loop user's effective groups and verify restrictions - for (String userGroup : userGroups) { - UserGroup thisGroup = directoryGroups.get(userGroup); - if (thisGroup == null) { - continue; - } + for (UserGroup userGroup : userGroups) { + + // Get group's attributes + Map 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 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 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 grpAttributes = thisGroup.getAttributes(); + Map grpAttributes = userGroup.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)) + 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 \"" - + authenticatedUser.getIdentifier() + + currentUser.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"); + // 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" + ); + + } + + /** + * Verifies the login restrictions supported by this extension for the user + * who is attempting to log in, throwing an exception if any of the + * restrictions result in the user not being allowed to log in. + * + * @param context + * The context of the user who is attempting to log in. + * + * @param remoteAddress + * The remote address of the client from which the current user is + * logged in. + * + * @throws GuacamoleException + * If any of the restrictions should prevent the user from logging in. + */ + public static void verifyLoginRestrictions(UserContext context, String remoteAddress) throws GuacamoleException { + + verifyTimeRestrictions(context); + verifyHostRestrictions(context, remoteAddress); + } /** @@ -295,38 +495,24 @@ public static void verifyLoginRestrictions(UserContext context, * connection the user is attempting to access, throwing an exception if * any of the restrictions result in the connection being unavailable. * - * @param connectionAttributes - * The attributes of the connection that may contain any additional - * restrictions on use of the connection. + * @param restrictable + * The object that supports restrictions that is to be verified to be + * usable within the current restrictions. * * @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 * If any of the restrictions should prevent the connection from being * used by the user at the current time. */ - public static void verifyConnectionRestrictions( - Map connectionAttributes, String remoteAddress) + public void verifyConnectionRestrictions(Restrictable restrictable, String remoteAddress) throws GuacamoleException { - // Verify time-based restrictions specific to this connection. - String allowedTimeString = connectionAttributes.get(RestrictConnection.RESTRICT_TIME_ALLOWED_ATTRIBUTE_NAME); - 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" - ); + verifyTimeRestrictions(restrictable); + verifyHostRestrictions(restrictable, remoteAddress); - // 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" - ); } diff --git a/extensions/guacamole-auth-restrict/src/main/java/org/apache/guacamole/auth/restrict/connection/RestrictConnection.java b/extensions/guacamole-auth-restrict/src/main/java/org/apache/guacamole/auth/restrict/connection/RestrictedConnection.java similarity index 82% rename from extensions/guacamole-auth-restrict/src/main/java/org/apache/guacamole/auth/restrict/connection/RestrictConnection.java rename to extensions/guacamole-auth-restrict/src/main/java/org/apache/guacamole/auth/restrict/connection/RestrictedConnection.java index 534c617ba4..31f7c33a49 100644 --- a/extensions/guacamole-auth-restrict/src/main/java/org/apache/guacamole/auth/restrict/connection/RestrictConnection.java +++ b/extensions/guacamole-auth-restrict/src/main/java/org/apache/guacamole/auth/restrict/connection/RestrictedConnection.java @@ -19,14 +19,17 @@ package org.apache.guacamole.auth.restrict.connection; +import com.google.inject.Inject; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; 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.HostRestrictionField; import org.apache.guacamole.auth.restrict.form.TimeRestrictionField; +import org.apache.guacamole.calendar.RestrictionType; import org.apache.guacamole.form.Form; import org.apache.guacamole.net.GuacamoleTunnel; import org.apache.guacamole.net.auth.Connection; @@ -37,7 +40,18 @@ * A Connection implementation that wraps another connection, providing additional * 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) @@ -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 * restricting connection access beyond the default access control provided @@ -116,10 +124,10 @@ public class RestrictConnection extends DelegatingConnection { * The Connection object to wrap. * * @param remoteAddress - * The remote address from which the user attempting to access this - * connection logged in. + * The remote address of the client from which the current user logged + * in. */ - public RestrictConnection(Connection connection, String remoteAddress) { + public RestrictedConnection(Connection connection, String remoteAddress) { super(connection); this.remoteAddress = remoteAddress; } @@ -175,11 +183,25 @@ public GuacamoleTunnel connect(GuacamoleClientInformation info, Map tokens) throws GuacamoleException { // Verify the restrictions for this connection. - RestrictionVerificationService.verifyConnectionRestrictions(getAttributes(), remoteAddress); + verificationService.verifyConnectionRestrictions(this, remoteAddress); // Connect 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); + } + } diff --git a/extensions/guacamole-auth-restrict/src/main/java/org/apache/guacamole/auth/restrict/connectiongroup/RestrictConnectionGroup.java b/extensions/guacamole-auth-restrict/src/main/java/org/apache/guacamole/auth/restrict/connectiongroup/RestrictedConnectionGroup.java similarity index 82% rename from extensions/guacamole-auth-restrict/src/main/java/org/apache/guacamole/auth/restrict/connectiongroup/RestrictConnectionGroup.java rename to extensions/guacamole-auth-restrict/src/main/java/org/apache/guacamole/auth/restrict/connectiongroup/RestrictedConnectionGroup.java index 48997c173f..b877bb51f1 100644 --- a/extensions/guacamole-auth-restrict/src/main/java/org/apache/guacamole/auth/restrict/connectiongroup/RestrictConnectionGroup.java +++ b/extensions/guacamole-auth-restrict/src/main/java/org/apache/guacamole/auth/restrict/connectiongroup/RestrictedConnectionGroup.java @@ -19,14 +19,17 @@ package org.apache.guacamole.auth.restrict.connectiongroup; +import com.google.inject.Inject; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; 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.HostRestrictionField; import org.apache.guacamole.auth.restrict.form.TimeRestrictionField; +import org.apache.guacamole.calendar.RestrictionType; import org.apache.guacamole.form.Form; import org.apache.guacamole.net.GuacamoleTunnel; import org.apache.guacamole.net.auth.ConnectionGroup; @@ -37,7 +40,18 @@ * A ConnectionGroup implementation that wraps an existing 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) @@ -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 * restricting connection group access beyond the default access control @@ -117,10 +125,10 @@ public class RestrictConnectionGroup extends DelegatingConnectionGroup { * The ConnectionGroup object to wrap. * * @param remoteAddress - * The remote address from which the user accessing this connection - * logged in. + * The remote address of the client from which the current user logged + * in. */ - public RestrictConnectionGroup(ConnectionGroup connectionGroup, String remoteAddress) { + public RestrictedConnectionGroup(ConnectionGroup connectionGroup, String remoteAddress) { super(connectionGroup); this.remoteAddress = remoteAddress; } @@ -177,11 +185,25 @@ public GuacamoleTunnel connect(GuacamoleClientInformation info, Map tokens) throws GuacamoleException { // Verify restrictions for this connection group. - RestrictionVerificationService.verifyConnectionRestrictions(getAttributes(), remoteAddress); + verificationService.verifyConnectionRestrictions(this, remoteAddress); // Connect 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); + } + } diff --git a/extensions/guacamole-auth-restrict/src/main/java/org/apache/guacamole/auth/restrict/user/RestrictUser.java b/extensions/guacamole-auth-restrict/src/main/java/org/apache/guacamole/auth/restrict/user/RestrictedUser.java similarity index 81% rename from extensions/guacamole-auth-restrict/src/main/java/org/apache/guacamole/auth/restrict/user/RestrictUser.java rename to extensions/guacamole-auth-restrict/src/main/java/org/apache/guacamole/auth/restrict/user/RestrictedUser.java index 1e4b558b81..84e2bac500 100644 --- a/extensions/guacamole-auth-restrict/src/main/java/org/apache/guacamole/auth/restrict/user/RestrictUser.java +++ b/extensions/guacamole-auth-restrict/src/main/java/org/apache/guacamole/auth/restrict/user/RestrictedUser.java @@ -23,8 +23,11 @@ import java.util.HashMap; import java.util.List; 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.TimeRestrictionField; +import org.apache.guacamole.calendar.RestrictionType; import org.apache.guacamole.form.Form; import org.apache.guacamole.net.auth.DelegatingUser; import org.apache.guacamole.net.auth.User; @@ -33,8 +36,13 @@ * User implementation which wraps a User from another extension and enforces * 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) * 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 * logins beyond the default restrictions provided by default modules. * * @param user * 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); + this.remoteAddress = remoteAddress; } /** @@ -154,5 +166,19 @@ public void setAttributes(Map 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); + } } diff --git a/extensions/guacamole-auth-restrict/src/main/java/org/apache/guacamole/auth/restrict/user/RestrictUserContext.java b/extensions/guacamole-auth-restrict/src/main/java/org/apache/guacamole/auth/restrict/user/RestrictedUserContext.java similarity index 66% rename from extensions/guacamole-auth-restrict/src/main/java/org/apache/guacamole/auth/restrict/user/RestrictUserContext.java rename to extensions/guacamole-auth-restrict/src/main/java/org/apache/guacamole/auth/restrict/user/RestrictedUserContext.java index dd06f4e88b..a61681d354 100644 --- a/extensions/guacamole-auth-restrict/src/main/java/org/apache/guacamole/auth/restrict/user/RestrictUserContext.java +++ b/extensions/guacamole-auth-restrict/src/main/java/org/apache/guacamole/auth/restrict/user/RestrictedUserContext.java @@ -23,9 +23,10 @@ import java.util.Collections; import java.util.HashSet; import org.apache.guacamole.GuacamoleException; -import org.apache.guacamole.auth.restrict.connection.RestrictConnection; -import org.apache.guacamole.auth.restrict.connectiongroup.RestrictConnectionGroup; -import org.apache.guacamole.auth.restrict.usergroup.RestrictUserGroup; +import org.apache.guacamole.auth.restrict.RestrictionVerificationService; +import org.apache.guacamole.auth.restrict.connection.RestrictedConnection; +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.net.auth.Connection; import org.apache.guacamole.net.auth.ConnectionGroup; @@ -35,20 +36,27 @@ import org.apache.guacamole.net.auth.User; import org.apache.guacamole.net.auth.UserContext; import org.apache.guacamole.net.auth.UserGroup; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * A UserContext implementation for additional login and connection restrictions * 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. */ 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. * * @param userContext @@ -57,7 +65,7 @@ public class RestrictUserContext extends DelegatingUserContext { * @param remoteAddress * The address the user is logging in from, if known. */ - public RestrictUserContext(UserContext userContext, String remoteAddress) { + public RestrictedUserContext(UserContext userContext, String remoteAddress) { super(userContext); this.remoteAddress = remoteAddress; } @@ -67,14 +75,14 @@ public Directory getConnectionDirectory() throws GuacamoleException return new DecoratingDirectory(super.getConnectionDirectory()) { @Override - protected Connection decorate(Connection object) { - return new RestrictConnection(object, remoteAddress); + protected Connection decorate(Connection object) throws GuacamoleException { + return new RestrictedConnection(object, remoteAddress); } @Override protected Connection undecorate(Connection object) { - assert(object instanceof RestrictConnection); - return ((RestrictConnection) object).getUndecorated(); + assert(object instanceof RestrictedConnection); + return ((RestrictedConnection) object).getUndecorated(); } }; @@ -83,7 +91,7 @@ protected Connection undecorate(Connection object) { @Override public Collection
getConnectionAttributes() { Collection connectionAttrs = new HashSet<>(super.getConnectionAttributes()); - connectionAttrs.add(RestrictConnection.RESTRICT_CONNECTION_FORM); + connectionAttrs.add(RestrictedConnection.RESTRICT_CONNECTION_FORM); return Collections.unmodifiableCollection(connectionAttrs); } @@ -92,14 +100,14 @@ public Directory getConnectionGroupDirectory() throws Guacamole return new DecoratingDirectory(super.getConnectionGroupDirectory()) { @Override - protected ConnectionGroup decorate(ConnectionGroup object) { - return new RestrictConnectionGroup(object, remoteAddress); + protected ConnectionGroup decorate(ConnectionGroup object) throws GuacamoleException { + return new RestrictedConnectionGroup(object, remoteAddress); } @Override protected ConnectionGroup undecorate(ConnectionGroup object) { - assert(object instanceof RestrictConnectionGroup); - return ((RestrictConnectionGroup) object).getUndecorated(); + assert(object instanceof RestrictedConnectionGroup); + return ((RestrictedConnectionGroup) object).getUndecorated(); } }; @@ -108,7 +116,7 @@ protected ConnectionGroup undecorate(ConnectionGroup object) { @Override public Collection getConnectionGroupAttributes() { Collection connectionGroupAttrs = new HashSet<>(super.getConnectionGroupAttributes()); - connectionGroupAttrs.add(RestrictConnectionGroup.RESTRICT_CONNECTIONGROUP_FORM); + connectionGroupAttrs.add(RestrictedConnectionGroup.RESTRICT_CONNECTIONGROUP_FORM); return Collections.unmodifiableCollection(connectionGroupAttrs); } @@ -118,13 +126,13 @@ public Directory getUserDirectory() throws GuacamoleException { @Override protected User decorate(User object) { - return new RestrictUser(object); + return new RestrictedUser(object, remoteAddress); } @Override protected User undecorate(User object) { - assert(object instanceof RestrictUser); - return ((RestrictUser) object).getUndecorated(); + assert(object instanceof RestrictedUser); + return ((RestrictedUser) object).getUndecorated(); } }; @@ -133,7 +141,7 @@ protected User undecorate(User object) { @Override public Collection getUserAttributes() { Collection userAttrs = new HashSet<>(super.getUserAttributes()); - userAttrs.add(RestrictUser.RESTRICT_LOGIN_FORM); + userAttrs.add(RestrictedUser.RESTRICT_LOGIN_FORM); return Collections.unmodifiableCollection(userAttrs); } @@ -143,13 +151,13 @@ public Directory getUserGroupDirectory() throws GuacamoleException { @Override protected UserGroup decorate(UserGroup object) { - return new RestrictUserGroup(object); + return new RestrictedUserGroup(object); } @Override protected UserGroup undecorate(UserGroup object) { - assert(object instanceof RestrictUserGroup); - return ((RestrictUserGroup) object).getUndecorated(); + assert(object instanceof RestrictedUserGroup); + return ((RestrictedUserGroup) object).getUndecorated(); } }; @@ -158,8 +166,21 @@ protected UserGroup undecorate(UserGroup object) { @Override public Collection getUserGroupAttributes() { Collection userGroupAttrs = new HashSet<>(super.getUserGroupAttributes()); - userGroupAttrs.add(RestrictUserGroup.RESTRICT_LOGIN_FORM); + userGroupAttrs.add(RestrictedUserGroup.RESTRICT_LOGIN_FORM); 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."); + return false; + } + } } diff --git a/extensions/guacamole-auth-restrict/src/main/java/org/apache/guacamole/auth/restrict/usergroup/RestrictUserGroup.java b/extensions/guacamole-auth-restrict/src/main/java/org/apache/guacamole/auth/restrict/usergroup/RestrictedUserGroup.java similarity index 98% rename from extensions/guacamole-auth-restrict/src/main/java/org/apache/guacamole/auth/restrict/usergroup/RestrictUserGroup.java rename to extensions/guacamole-auth-restrict/src/main/java/org/apache/guacamole/auth/restrict/usergroup/RestrictedUserGroup.java index 9a1f026fd0..2e637872b9 100644 --- a/extensions/guacamole-auth-restrict/src/main/java/org/apache/guacamole/auth/restrict/usergroup/RestrictUserGroup.java +++ b/extensions/guacamole-auth-restrict/src/main/java/org/apache/guacamole/auth/restrict/usergroup/RestrictedUserGroup.java @@ -33,8 +33,8 @@ * UserGroup implementation which wraps a UserGroup from another extension and * 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) * 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 * The UserGroup object to wrap. */ - public RestrictUserGroup(UserGroup userGroup) { + public RestrictedUserGroup(UserGroup userGroup) { super(userGroup); } diff --git a/extensions/guacamole-auth-restrict/src/main/java/org/apache/guacamole/calendar/RestrictionType.java b/extensions/guacamole-auth-restrict/src/main/java/org/apache/guacamole/calendar/RestrictionType.java new file mode 100644 index 0000000000..8f78928a49 --- /dev/null +++ b/extensions/guacamole-auth-restrict/src/main/java/org/apache/guacamole/calendar/RestrictionType.java @@ -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, resturn 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; + } + +}