diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/ModeledUser.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/ModeledUser.java index 0e0e840ff..6c6eb844c 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/ModeledUser.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/ModeledUser.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 Glyptodon LLC + * Copyright (C) 2015 Glyptodon LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,11 +23,18 @@ package org.glyptodon.guacamole.auth.jdbc.user; import com.google.inject.Inject; +import java.sql.Date; +import java.sql.Time; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; import java.util.Arrays; +import java.util.Calendar; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.TimeZone; import org.glyptodon.guacamole.auth.jdbc.base.ModeledDirectoryObject; import org.glyptodon.guacamole.auth.jdbc.security.PasswordEncryptionService; import org.glyptodon.guacamole.auth.jdbc.security.SaltService; @@ -40,10 +47,13 @@ import org.glyptodon.guacamole.auth.jdbc.permission.UserPermissionService; import org.glyptodon.guacamole.form.BooleanField; import org.glyptodon.guacamole.form.Field; import org.glyptodon.guacamole.form.Form; +import org.glyptodon.guacamole.form.TextField; import org.glyptodon.guacamole.net.auth.User; import org.glyptodon.guacamole.net.auth.permission.ObjectPermissionSet; import org.glyptodon.guacamole.net.auth.permission.SystemPermission; import org.glyptodon.guacamole.net.auth.permission.SystemPermissionSet; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * An implementation of the User object which is backed by a database model. @@ -53,6 +63,21 @@ import org.glyptodon.guacamole.net.auth.permission.SystemPermissionSet; */ public class ModeledUser extends ModeledDirectoryObject implements User { + /** + * Logger for this class. + */ + private static final Logger logger = LoggerFactory.getLogger(ModeledUser.class); + + /** + * The format to use for all date attributes associated with users. + */ + private static final String DATE_FORMAT = "yyyy-MM-dd"; + + /** + * The format to use for all time attributes associated with users. + */ + private static final String TIME_FORMAT = "HH:mm:ss"; + /** * The name of the attribute which controls whether a user account is * disabled. @@ -65,13 +90,48 @@ public class ModeledUser extends ModeledDirectoryObject implements Us */ public static final String EXPIRED_ATTRIBUTE_NAME = "expired"; + /** + * The name of the attribute which controls the time of day after which a + * user may login. + */ + public static final String ACCESS_WINDOW_START_ATTRIBUTE_NAME = "access-window-start"; + + /** + * The name of the attribute which controls the time of day after which a + * user may NOT login. + */ + public static final String ACCESS_WINDOW_END_ATTRIBUTE_NAME = "access-window-end"; + + /** + * The name of the attribute which controls the date after which a user's + * account is valid. + */ + public static final String VALID_FROM_ATTRIBUTE_NAME = "valid-from"; + + /** + * The name of the attribute which controls the date after which a user's + * account is no longer valid. + */ + public static final String VALID_UNTIL_ATTRIBUTE_NAME = "valid-until"; + + /** + * The name of the attribute which defines the time zone used for all + * time and date attributes related to this user. + */ + public static final String TIMEZONE_ATTRIBUTE_NAME = "timezone"; + /** * All attributes related to restricting user accounts, within a logical * form. */ public static final Form ACCOUNT_RESTRICTIONS = new Form("restrictions", Arrays.asList( new BooleanField(DISABLED_ATTRIBUTE_NAME, "true"), - new BooleanField(EXPIRED_ATTRIBUTE_NAME, "true") + new BooleanField(EXPIRED_ATTRIBUTE_NAME, "true"), + new TextField(ACCESS_WINDOW_START_ATTRIBUTE_NAME), + new TextField(ACCESS_WINDOW_END_ATTRIBUTE_NAME), + new TextField(VALID_FROM_ATTRIBUTE_NAME), + new TextField(VALID_UNTIL_ATTRIBUTE_NAME), + new TextField(TIMEZONE_ATTRIBUTE_NAME) )); /** @@ -216,6 +276,117 @@ public class ModeledUser extends ModeledDirectoryObject implements Us return userPermissionService.getPermissionSet(getCurrentUser(), this); } + /** + * Converts the given date into a string which follows the format used by + * date attributes. + * + * @param date + * The date value to format, which may be null. + * + * @return + * The formatted date, or null if the provided time was null. + */ + private String formatDate(Date date) { + DateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT); + return date == null ? null : dateFormat.format(date); + } + + /** + * Converts the given time into a string which follows the format used by + * time attributes. + * + * @param time + * The time value to format, which may be null. + * + * @return + * The formatted time, or null if the provided time was null. + */ + private String formatTime(Time time) { + DateFormat timeFormat = new SimpleDateFormat(TIME_FORMAT); + return time == null ? null : timeFormat.format(time); + } + + /** + * Parses the given string into a corresponding date. The string must + * follow the standard format used by date attributes, as defined by + * DATE_FORMAT and as would be produced by formatDate(). + * + * @param dateString + * The date string to parse, which may be null. + * + * @return + * The date corresponding to the given date string, or null if the + * provided date string was null or blank. + * + * @throws ParseException + * If the given date string does not conform to the standard format + * used by date attributes. + */ + private Date parseDate(String dateString) + throws ParseException { + + // Return null if no date provided + if (dateString == null || dateString.isEmpty()) + return null; + + // Parse date according to format + DateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT); + return new Date(dateFormat.parse(dateString).getTime()); + + } + + /** + * Parses the given string into a corresponding time. The string must + * follow the standard format used by time attributes, as defined by + * TIME_FORMAT and as would be produced by formatTime(). + * + * @param timeString + * The time string to parse, which may be null. + * + * @return + * The time corresponding to the given time string, or null if the + * provided time string was null or blank. + * + * @throws ParseException + * If the given time string does not conform to the standard format + * used by time attributes. + */ + private Time parseTime(String timeString) + throws ParseException { + + // Return null if no time provided + if (timeString == null || timeString.isEmpty()) + return null; + + // Parse time according to format + DateFormat timeFormat = new SimpleDateFormat(TIME_FORMAT); + return new Time(timeFormat.parse(timeString).getTime()); + + } + + /** + * Parses the given string into a time zone ID string. As these strings are + * equivalent, the only transformation currently performed by this function + * is to ensure that a blank time zone string is parsed into null. + * + * @param timeZone + * The time zone string to parse, which may be null. + * + * @return + * The ID of the time zone corresponding to the given string, or null + * if the given time zone string was null or blank. + */ + private String parseTimeZone(String timeZone) { + + // Return null if no time zone provided + if (timeZone == null || timeZone.isEmpty()) + return null; + + // Otherwise, assume time zone is valid + return timeZone; + + } + @Override public Map getAttributes() { @@ -227,6 +398,21 @@ public class ModeledUser extends ModeledDirectoryObject implements Us // Set password expired attribute attributes.put(EXPIRED_ATTRIBUTE_NAME, getModel().isExpired() ? "true" : null); + // Set access window start time + attributes.put(ACCESS_WINDOW_START_ATTRIBUTE_NAME, formatTime(getModel().getAccessWindowStart())); + + // Set access window end time + attributes.put(ACCESS_WINDOW_END_ATTRIBUTE_NAME, formatTime(getModel().getAccessWindowEnd())); + + // Set account validity start date + attributes.put(VALID_FROM_ATTRIBUTE_NAME, formatDate(getModel().getValidFrom())); + + // Set account validity end date + attributes.put(VALID_UNTIL_ATTRIBUTE_NAME, formatDate(getModel().getValidUntil())); + + // Set timezone attribute + attributes.put(TIMEZONE_ATTRIBUTE_NAME, getModel().getTimeZone()); + return attributes; } @@ -239,6 +425,237 @@ public class ModeledUser extends ModeledDirectoryObject implements Us // Translate password expired attribute getModel().setExpired("true".equals(attributes.get(EXPIRED_ATTRIBUTE_NAME))); + // Translate access window start time + try { getModel().setAccessWindowStart(parseTime(attributes.get(ACCESS_WINDOW_START_ATTRIBUTE_NAME))); } + catch (ParseException e) { + logger.warn("Not setting start time of user access window: {}", e.getMessage()); + logger.debug("Unable to parse time attribute.", e); + } + + // Translate access window end time + try { getModel().setAccessWindowEnd(parseTime(attributes.get(ACCESS_WINDOW_END_ATTRIBUTE_NAME))); } + catch (ParseException e) { + logger.warn("Not setting end time of user access window: {}", e.getMessage()); + logger.debug("Unable to parse time attribute.", e); + } + + // Translate account validity start date + try { getModel().setValidFrom(parseDate(attributes.get(VALID_FROM_ATTRIBUTE_NAME))); } + catch (ParseException e) { + logger.warn("Not setting user validity start date: {}", e.getMessage()); + logger.debug("Unable to parse date attribute.", e); + } + + // Translate account validity end date + try { getModel().setValidUntil(parseDate(attributes.get(VALID_UNTIL_ATTRIBUTE_NAME))); } + catch (ParseException e) { + logger.warn("Not setting user validity end date: {}", e.getMessage()); + logger.debug("Unable to parse date attribute.", e); + } + + // Translate timezone attribute + getModel().setTimeZone(parseTimeZone(attributes.get(TIMEZONE_ATTRIBUTE_NAME))); + + } + + /** + * Returns the time zone associated with this user. This time zone must be + * used when interpreting all date/time restrictions related to this user. + * + * @return + * The time zone associated with this user. + */ + private TimeZone getTimeZone() { + + // If no time zone is set, use the default + String timeZone = getModel().getTimeZone(); + if (timeZone == null) + return TimeZone.getDefault(); + + // Otherwise parse and return time zone + return TimeZone.getTimeZone(timeZone); + + } + + /** + * Converts a SQL Time to a Calendar, independently of time zone, using the + * given Calendar as a base. The time components will be copied to the + * given Calendar verbatim, leaving the date and time zone components of + * the given Calendar otherwise intact. + * + * @param base + * The Calendar object to use as a base for the conversion. + * + * @param time + * The SQL Time object containing the time components to be applied to + * the given Calendar. + * + * @return + * The given Calendar, now modified to represent the given time. + */ + private Calendar asCalendar(Calendar base, Time time) { + + // Get calendar from given SQL time + Calendar timeCalendar = Calendar.getInstance(); + timeCalendar.setTime(time); + + // Apply given time to base calendar + base.set(Calendar.HOUR_OF_DAY, timeCalendar.get(Calendar.HOUR_OF_DAY)); + base.set(Calendar.MINUTE, timeCalendar.get(Calendar.MINUTE)); + base.set(Calendar.SECOND, timeCalendar.get(Calendar.SECOND)); + base.set(Calendar.MILLISECOND, timeCalendar.get(Calendar.MILLISECOND)); + + return base; + + } + + /** + * Returns the time during the current day when this user account can start + * being used. + * + * @return + * The time during the current day when this user account can start + * being used. + */ + private Calendar getAccessWindowStart() { + + // Get window start time + Time start = getModel().getAccessWindowStart(); + if (start == null) + return null; + + // Return within defined time zone, current day + return asCalendar(Calendar.getInstance(getTimeZone()), start); + + } + + /** + * Returns the time during the current day when this user account can no + * longer be used. + * + * @return + * The time during the current day when this user account can no longer + * be used. + */ + private Calendar getAccessWindowEnd() { + + // Get window end time + Time end = getModel().getAccessWindowEnd(); + if (end == null) + return null; + + // Return within defined time zone, current day + return asCalendar(Calendar.getInstance(getTimeZone()), end); + + } + + /** + * Returns the date after which this account becomes valid. The time + * components of the resulting Calendar object will be set to midnight of + * the date in question. + * + * @return + * The date after which this account becomes valid. + */ + private Calendar getValidFrom() { + + // Get valid from date + Date validFrom = getModel().getValidFrom(); + if (validFrom == null) + return null; + + // Convert to midnight within defined time zone + Calendar validFromCalendar = Calendar.getInstance(getTimeZone()); + validFromCalendar.setTime(validFrom); + validFromCalendar.set(Calendar.HOUR_OF_DAY, 0); + validFromCalendar.set(Calendar.MINUTE, 0); + validFromCalendar.set(Calendar.SECOND, 0); + validFromCalendar.set(Calendar.MILLISECOND, 0); + return validFromCalendar; + + } + + /** + * Returns the date after which this account becomes invalid. The time + * components of the resulting Calendar object will be set to the last + * millisecond of the day in question (23:59:59.999). + * + * @return + * The date after which this account becomes invalid. + */ + private Calendar getValidUntil() { + + // Get valid until date + Date validUntil = getModel().getValidUntil(); + if (validUntil == null) + return null; + + // Convert to end-of-day within defined time zone + Calendar validUntilCalendar = Calendar.getInstance(getTimeZone()); + validUntilCalendar.setTime(validUntil); + validUntilCalendar.set(Calendar.HOUR_OF_DAY, 23); + validUntilCalendar.set(Calendar.MINUTE, 59); + validUntilCalendar.set(Calendar.SECOND, 59); + validUntilCalendar.set(Calendar.MILLISECOND, 999); + return validUntilCalendar; + + } + + /** + * Given a time when a particular state changes from inactive to active, + * and a time when a particular state changes from active to inactive, + * determines whether that state is currently active. + * + * @param activeStart + * The time at which the state changes from inactive to active. + * + * @param inactiveStart + * The time at which the state changes from active to inactive. + * + * @return + * true if the state is currently active, false otherwise. + */ + private boolean isActive(Calendar activeStart, Calendar inactiveStart) { + + // If end occurs before start, convert to equivalent case where start + // start is before end + if (inactiveStart != null && activeStart != null && inactiveStart.before(activeStart)) + return !isActive(inactiveStart, activeStart); + + // Get current time + Calendar current = Calendar.getInstance(); + + // State is active iff the current time is between the start and end + return !(activeStart != null && current.before(activeStart)) + && !(inactiveStart != null && current.after(inactiveStart)); + + } + + /** + * Returns whether this user account is currently valid as of today. + * Account validity depends on optional date-driven restrictions which + * define when an account becomes valid, and when an account ceases being + * valid. + * + * @return + * true if the account is valid as of today, false otherwise. + */ + public boolean isAccountValid() { + return isActive(getValidFrom(), getValidUntil()); + } + + /** + * Returns whether the current time is within this user's allowed access + * window. If the login times for this user are not limited, this will + * return true. + * + * @return + * true if the current time is within this user's allowed access + * window, or if this user has no restrictions on login time, false + * otherwise. + */ + public boolean isAccountAccessible() { + return isActive(getAccessWindowStart(), getAccessWindowEnd()); } } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/UserModel.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/UserModel.java index 5057b2f5c..d1ca7516d 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/UserModel.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/UserModel.java @@ -22,6 +22,8 @@ package org.glyptodon.guacamole.auth.jdbc.user; +import java.sql.Date; +import java.sql.Time; import org.glyptodon.guacamole.auth.jdbc.base.ObjectModel; /** @@ -55,6 +57,40 @@ public class UserModel extends ObjectModel { */ private boolean expired; + /** + * The time each day after which this user account may be used, stored in + * local time according to the value of timeZone. + */ + private Time accessWindowStart; + + /** + * The time each day after which this user account may NOT be used, stored + * in local time according to the value of timeZone. + */ + private Time accessWindowEnd; + + /** + * The day after which this account becomes valid and usable. Account + * validity begins at midnight of this day. Time information within the + * Date object is ignored. + */ + private Date validFrom; + + /** + * The day after which this account can no longer be used. Account validity + * ends at midnight of the day following this day. Time information within + * the Date object is ignored. + */ + private Date validUntil; + + /** + * The ID of the time zone used for all time comparisons for this user. + * Both accessWindowStart and accessWindowEnd values will use this time + * zone, as will checks for whether account validity dates have passed. If + * unset, the server's local time zone is used. + */ + private String timeZone; + /** * Creates a new, empty user. */ @@ -158,4 +194,136 @@ public class UserModel extends ObjectModel { this.expired = expired; } + /** + * Returns the time each day after which this user account may be used. The + * time returned will be local time according to the time zone set with + * setTimeZone(). + * + * @return + * The time each day after which this user account may be used, or null + * if this restriction does not apply. + */ + public Time getAccessWindowStart() { + return accessWindowStart; + } + + /** + * Sets the time each day after which this user account may be used. The + * time given must be in local time according to the time zone set with + * setTimeZone(). + * + * @param accessWindowStart + * The time each day after which this user account may be used, or null + * if this restriction does not apply. + */ + public void setAccessWindowStart(Time accessWindowStart) { + this.accessWindowStart = accessWindowStart; + } + + /** + * Returns the time each day after which this user account may NOT be used. + * The time returned will be local time according to the time zone set with + * setTimeZone(). + * + * @return + * The time each day after which this user account may NOT be used, or + * null if this restriction does not apply. + */ + public Time getAccessWindowEnd() { + return accessWindowEnd; + } + + /** + * Sets the time each day after which this user account may NOT be used. + * The time given must be in local time according to the time zone set with + * setTimeZone(). + * + * @param accessWindowEnd + * The time each day after which this user account may NOT be used, or + * null if this restriction does not apply. + */ + public void setAccessWindowEnd(Time accessWindowEnd) { + this.accessWindowEnd = accessWindowEnd; + } + + /** + * Returns the day after which this account becomes valid and usable. + * Account validity begins at midnight of this day. Any time information + * within the returned Date object must be ignored. + * + * @return + * The day after which this account becomes valid and usable, or null + * if this restriction does not apply. + */ + public Date getValidFrom() { + return validFrom; + } + + /** + * Sets the day after which this account becomes valid and usable. Account + * validity begins at midnight of this day. Any time information within + * the provided Date object will be ignored. + * + * @param validFrom + * The day after which this account becomes valid and usable, or null + * if this restriction does not apply. + */ + public void setValidFrom(Date validFrom) { + this.validFrom = validFrom; + } + + /** + * Returns the day after which this account can no longer be used. Account + * validity ends at midnight of the day following this day. Any time + * information within the returned Date object must be ignored. + * + * @return + * The day after which this account can no longer be used, or null if + * this restriction does not apply. + */ + public Date getValidUntil() { + return validUntil; + } + + /** + * Sets the day after which this account can no longer be used. Account + * validity ends at midnight of the day following this day. Any time + * information within the provided Date object will be ignored. + * + * @param validUntil + * The day after which this account can no longer be used, or null if + * this restriction does not apply. + */ + public void setValidUntil(Date validUntil) { + this.validUntil = validUntil; + } + + /** + * Returns the Java ID of the time zone to be used for all time comparisons + * for this user. This ID should correspond to a value returned by + * TimeZone.getAvailableIDs(). If unset or invalid, the server's local time + * zone must be used. + * + * @return + * The ID of the time zone to be used for all time comparisons, which + * should correspond to a value returned by TimeZone.getAvailableIDs(). + */ + public String getTimeZone() { + return timeZone; + } + + /** + * Sets the Java ID of the time zone to be used for all time comparisons + * for this user. This ID should correspond to a value returned by + * TimeZone.getAvailableIDs(). If unset or invalid, the server's local time + * zone will be used. + * + * @param timeZone + * The ID of the time zone to be used for all time comparisons, which + * should correspond to a value returned by TimeZone.getAvailableIDs(). + */ + public void setTimeZone(String timeZone) { + this.timeZone = timeZone; + } + } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/UserService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/UserService.java index 969741bf4..1a9e6090a 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/UserService.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/UserService.java @@ -300,6 +300,14 @@ public class UserService extends ModeledDirectoryObjectService - - - - - - + + + + + + + + + + @@ -61,7 +65,12 @@ password_hash, password_salt, disabled, - expired + expired, + access_window_start, + access_window_end, + valid_from, + valid_until, + timezone FROM guacamole_user WHERE username IN @@ -142,7 +171,12 @@ SET password_hash = #{object.passwordHash,jdbcType=BINARY}, password_salt = #{object.passwordSalt,jdbcType=BINARY}, disabled = #{object.disabled,jdbcType=BOOLEAN}, - expired = #{object.expired,jdbcType=BOOLEAN} + expired = #{object.expired,jdbcType=BOOLEAN}, + access_window_start = #{object.accessWindowStart,jdbcType=TIME}, + access_window_end = #{object.accessWindowEnd,jdbcType=TIME}, + valid_from = #{object.validFrom,jdbcType=DATE}, + valid_until = #{object.validUntil,jdbcType=DATE}, + timezone = #{object.timeZone,jdbcType=VARCHAR} WHERE user_id = #{object.objectID,jdbcType=VARCHAR} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/schema/001-create-schema.sql b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/schema/001-create-schema.sql index 7f14986be..dd5e69887 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/schema/001-create-schema.sql +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/schema/001-create-schema.sql @@ -114,12 +114,27 @@ CREATE INDEX ON guacamole_connection(parent_id); CREATE TABLE guacamole_user ( user_id serial NOT NULL, + + -- Username and optionally-salted password username varchar(128) NOT NULL, password_hash bytea NOT NULL, password_salt bytea, + + -- Account disabled/expired status disabled boolean NOT NULL DEFAULT FALSE, expired boolean NOT NULL DEFAULT FALSE, + -- Time-based access restriction + access_window_start time, + access_window_end time, + + -- Date-based access restriction + valid_from date, + valid_until date, + + -- Timezone used for all date/time comparisons and interpretation + timezone varchar(64), + PRIMARY KEY (user_id), CONSTRAINT username diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/schema/upgrade/upgrade-pre-0.9.8.sql b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/schema/upgrade/upgrade-pre-0.9.8.sql new file mode 100644 index 000000000..be2d36898 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/schema/upgrade/upgrade-pre-0.9.8.sql @@ -0,0 +1,41 @@ +-- +-- Copyright (C) 2015 Glyptodon LLC +-- +-- Permission is hereby granted, free of charge, to any person obtaining a copy +-- of this software and associated documentation files (the "Software"), to deal +-- in the Software without restriction, including without limitation the rights +-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +-- copies of the Software, and to permit persons to whom the Software is +-- furnished to do so, subject to the following conditions: +-- +-- The above copyright notice and this permission notice shall be included in +-- all copies or substantial portions of the Software. +-- +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +-- THE SOFTWARE. +-- + +-- +-- Add per-user time-based access restrictions. +-- + +ALTER TABLE guacamole_user ADD COLUMN access_window_start time; +ALTER TABLE guacamole_user ADD COLUMN access_window_end time; + +-- +-- Add per-user date-based account validity restrictions. +-- + +ALTER TABLE guacamole_user ADD COLUMN valid_from date; +ALTER TABLE guacamole_user ADD COLUMN valid_until date; + +-- +-- Add per-user timezone for sake of time comparisons/interpretation. +-- + +ALTER TABLE guacamole_user ADD COLUMN timezone varchar(64); diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/user/UserMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/user/UserMapper.xml index d78b30abb..fd9138cfe 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/user/UserMapper.xml +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/user/UserMapper.xml @@ -28,12 +28,17 @@ - - - - - - + + + + + + + + + + + @@ -61,7 +66,12 @@ password_hash, password_salt, disabled, - expired + expired, + access_window_start, + access_window_end, + valid_from, + valid_until, + timezone FROM guacamole_user WHERE username IN @@ -142,7 +172,12 @@ SET password_hash = #{object.passwordHash,jdbcType=BINARY}, password_salt = #{object.passwordSalt,jdbcType=BINARY}, disabled = #{object.disabled,jdbcType=BOOLEAN}, - expired = #{object.expired,jdbcType=BOOLEAN} + expired = #{object.expired,jdbcType=BOOLEAN}, + access_window_start = #{object.accessWindowStart,jdbcType=TIME}, + access_window_end = #{object.accessWindowEnd,jdbcType=TIME}, + valid_from = #{object.validFrom,jdbcType=DATE}, + valid_until = #{object.validUntil,jdbcType=DATE}, + timezone = #{object.timeZone,jdbcType=VARCHAR} WHERE user_id = #{object.objectID,jdbcType=VARCHAR}