mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-07 05:31:22 +00:00
Merge pull request #236 from glyptodon/date-time-restrictions
GUAC-1213: Add date/time restrictions for user accounts.
This commit is contained in:
@@ -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<UserModel> 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<UserModel> 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.<Field>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<UserModel> 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<String, String> getAttributes() {
|
||||
|
||||
@@ -227,6 +398,21 @@ public class ModeledUser extends ModeledDirectoryObject<UserModel> 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<UserModel> 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());
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -300,6 +300,14 @@ public class UserService extends ModeledDirectoryObjectService<ModeledUser, User
|
||||
ModeledUser user = getObjectInstance(null, userModel);
|
||||
user.setCurrentUser(new AuthenticatedUser(user, credentials));
|
||||
|
||||
// Verify user account is still valid as of today
|
||||
if (!user.isAccountValid())
|
||||
throw new GuacamoleClientException("LOGIN.ERROR_NOT_VALID");
|
||||
|
||||
// Verify user account is allowed to be used at the current time
|
||||
if (!user.isAccountAccessible())
|
||||
throw new GuacamoleClientException("LOGIN.ERROR_NOT_ACCESSIBLE");
|
||||
|
||||
// Update password if password is expired
|
||||
if (userModel.isExpired()) {
|
||||
|
||||
|
@@ -5,6 +5,8 @@
|
||||
"ERROR_PASSWORD_BLANK" : "@:APP.ERROR_PASSWORD_BLANK",
|
||||
"ERROR_PASSWORD_SAME" : "The new password must be different from the expired password.",
|
||||
"ERROR_PASSWORD_MISMATCH" : "@:APP.ERROR_PASSWORD_MISMATCH",
|
||||
"ERROR_NOT_VALID" : "This user account is not currently valid.",
|
||||
"ERROR_NOT_ACCESSIBLE" : "Access to this account is not currently allowed. Please try again later.",
|
||||
|
||||
"INFO_PASSWORD_EXPIRED" : "Your password has expired and must be reset. Please enter a new password to continue.",
|
||||
|
||||
@@ -15,8 +17,13 @@
|
||||
|
||||
"USER_ATTRIBUTES" : {
|
||||
|
||||
"FIELD_HEADER_DISABLED" : "Login disabled:",
|
||||
"FIELD_HEADER_EXPIRED" : "Password expired:",
|
||||
"FIELD_HEADER_DISABLED" : "Login disabled:",
|
||||
"FIELD_HEADER_EXPIRED" : "Password expired:",
|
||||
"FIELD_HEADER_ACCESS_WINDOW_END" : "Do not allow access after:",
|
||||
"FIELD_HEADER_ACCESS_WINDOW_START" : "Allow access after:",
|
||||
"FIELD_HEADER_TIMEZONE" : "User time zone:",
|
||||
"FIELD_HEADER_VALID_FROM" : "Enable account after:",
|
||||
"FIELD_HEADER_VALID_UNTIL" : "Disable account after:",
|
||||
|
||||
"SECTION_HEADER_RESTRICTIONS" : "Account Restrictions"
|
||||
|
||||
|
@@ -73,12 +73,27 @@ CREATE TABLE `guacamole_connection` (
|
||||
CREATE TABLE `guacamole_user` (
|
||||
|
||||
`user_id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
|
||||
-- Username and optionally-salted password
|
||||
`username` varchar(128) NOT NULL,
|
||||
`password_hash` binary(32) NOT NULL,
|
||||
`password_salt` binary(32),
|
||||
|
||||
-- Account disabled/expired status
|
||||
`disabled` boolean NOT NULL DEFAULT 0,
|
||||
`expired` boolean NOT NULL DEFAULT 0,
|
||||
|
||||
-- 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`),
|
||||
UNIQUE KEY `username` (`username`)
|
||||
|
||||
|
@@ -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);
|
@@ -28,12 +28,16 @@
|
||||
|
||||
<!-- Result mapper for user objects -->
|
||||
<resultMap id="UserResultMap" type="org.glyptodon.guacamole.auth.jdbc.user.UserModel" >
|
||||
<id column="user_id" property="objectID" jdbcType="INTEGER"/>
|
||||
<result column="username" property="identifier" jdbcType="VARCHAR"/>
|
||||
<result column="password_hash" property="passwordHash" jdbcType="BINARY"/>
|
||||
<result column="password_salt" property="passwordSalt" jdbcType="BINARY"/>
|
||||
<result column="disabled" property="disabled" jdbcType="BOOLEAN"/>
|
||||
<result column="expired" property="expired" jdbcType="BOOLEAN"/>
|
||||
<id column="user_id" property="objectID" jdbcType="INTEGER"/>
|
||||
<result column="username" property="identifier" jdbcType="VARCHAR"/>
|
||||
<result column="password_hash" property="passwordHash" jdbcType="BINARY"/>
|
||||
<result column="password_salt" property="passwordSalt" jdbcType="BINARY"/>
|
||||
<result column="disabled" property="disabled" jdbcType="BOOLEAN"/>
|
||||
<result column="access_window_start" property="accessWindowStart" jdbcType="TIME"/>
|
||||
<result column="access_window_end" property="accessWindowEnd" jdbcType="TIME"/>
|
||||
<result column="valid_from" property="validFrom" jdbcType="DATE"/>
|
||||
<result column="valid_until" property="validUntil" jdbcType="DATE"/>
|
||||
<result column="timezone" property="timeZone" jdbcType="VARCHAR"/>
|
||||
</resultMap>
|
||||
|
||||
<!-- Select all usernames -->
|
||||
@@ -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
|
||||
<foreach collection="identifiers" item="identifier"
|
||||
@@ -80,7 +89,12 @@
|
||||
password_hash,
|
||||
password_salt,
|
||||
disabled,
|
||||
expired
|
||||
expired,
|
||||
access_window_start,
|
||||
access_window_end,
|
||||
valid_from,
|
||||
valid_until,
|
||||
timezone
|
||||
FROM guacamole_user
|
||||
JOIN guacamole_user_permission ON affected_user_id = guacamole_user.user_id
|
||||
WHERE username IN
|
||||
@@ -102,7 +116,12 @@
|
||||
password_hash,
|
||||
password_salt,
|
||||
disabled,
|
||||
expired
|
||||
expired,
|
||||
access_window_start,
|
||||
access_window_end,
|
||||
valid_from,
|
||||
valid_until,
|
||||
timezone
|
||||
FROM guacamole_user
|
||||
WHERE
|
||||
username = #{username,jdbcType=VARCHAR}
|
||||
@@ -124,14 +143,24 @@
|
||||
password_hash,
|
||||
password_salt,
|
||||
disabled,
|
||||
expired
|
||||
expired,
|
||||
access_window_start,
|
||||
access_window_end,
|
||||
valid_from,
|
||||
valid_until,
|
||||
timezone
|
||||
)
|
||||
VALUES (
|
||||
#{object.identifier,jdbcType=VARCHAR},
|
||||
#{object.passwordHash,jdbcType=BINARY},
|
||||
#{object.passwordSalt,jdbcType=BINARY},
|
||||
#{object.disabled,jdbcType=BOOLEAN},
|
||||
#{object.expired,jdbcType=BOOLEAN}
|
||||
#{object.expired,jdbcType=BOOLEAN},
|
||||
#{object.accessWindowStart,jdbcType=TIME},
|
||||
#{object.accessWindowEnd,jdbcType=TIME},
|
||||
#{object.validFrom,jdbcType=DATE},
|
||||
#{object.validUntil,jdbcType=DATE},
|
||||
#{object.timeZone,jdbcType=VARCHAR}
|
||||
)
|
||||
|
||||
</insert>
|
||||
@@ -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}
|
||||
</update>
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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);
|
@@ -28,12 +28,17 @@
|
||||
|
||||
<!-- Result mapper for user objects -->
|
||||
<resultMap id="UserResultMap" type="org.glyptodon.guacamole.auth.jdbc.user.UserModel" >
|
||||
<id column="user_id" property="objectID" jdbcType="INTEGER"/>
|
||||
<result column="username" property="identifier" jdbcType="VARCHAR"/>
|
||||
<result column="password_hash" property="passwordHash" jdbcType="BINARY"/>
|
||||
<result column="password_salt" property="passwordSalt" jdbcType="BINARY"/>
|
||||
<result column="disabled" property="disabled" jdbcType="BOOLEAN"/>
|
||||
<result column="expired" property="expired" jdbcType="BOOLEAN"/>
|
||||
<id column="user_id" property="objectID" jdbcType="INTEGER"/>
|
||||
<result column="username" property="identifier" jdbcType="VARCHAR"/>
|
||||
<result column="password_hash" property="passwordHash" jdbcType="BINARY"/>
|
||||
<result column="password_salt" property="passwordSalt" jdbcType="BINARY"/>
|
||||
<result column="disabled" property="disabled" jdbcType="BOOLEAN"/>
|
||||
<result column="expired" property="expired" jdbcType="BOOLEAN"/>
|
||||
<result column="access_window_start" property="accessWindowStart" jdbcType="TIME"/>
|
||||
<result column="access_window_end" property="accessWindowEnd" jdbcType="TIME"/>
|
||||
<result column="valid_from" property="validFrom" jdbcType="DATE"/>
|
||||
<result column="valid_until" property="validUntil" jdbcType="DATE"/>
|
||||
<result column="timezone" property="timeZone" jdbcType="VARCHAR"/>
|
||||
</resultMap>
|
||||
|
||||
<!-- Select all usernames -->
|
||||
@@ -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
|
||||
<foreach collection="identifiers" item="identifier"
|
||||
@@ -80,7 +90,12 @@
|
||||
password_hash,
|
||||
password_salt,
|
||||
disabled,
|
||||
expired
|
||||
expired,
|
||||
access_window_start,
|
||||
access_window_end,
|
||||
valid_from,
|
||||
valid_until,
|
||||
timezone
|
||||
FROM guacamole_user
|
||||
JOIN guacamole_user_permission ON affected_user_id = guacamole_user.user_id
|
||||
WHERE username IN
|
||||
@@ -102,7 +117,12 @@
|
||||
password_hash,
|
||||
password_salt,
|
||||
disabled,
|
||||
expired
|
||||
expired,
|
||||
access_window_start,
|
||||
access_window_end,
|
||||
valid_from,
|
||||
valid_until,
|
||||
timezone
|
||||
FROM guacamole_user
|
||||
WHERE
|
||||
username = #{username,jdbcType=VARCHAR}
|
||||
@@ -124,14 +144,24 @@
|
||||
password_hash,
|
||||
password_salt,
|
||||
disabled,
|
||||
expired
|
||||
expired,
|
||||
access_window_start,
|
||||
access_window_end,
|
||||
valid_from,
|
||||
valid_until,
|
||||
timezone
|
||||
)
|
||||
VALUES (
|
||||
#{object.identifier,jdbcType=VARCHAR},
|
||||
#{object.passwordHash,jdbcType=BINARY},
|
||||
#{object.passwordSalt,jdbcType=BINARY},
|
||||
#{object.disabled,jdbcType=BOOLEAN},
|
||||
#{object.expired,jdbcType=BOOLEAN}
|
||||
#{object.expired,jdbcType=BOOLEAN},
|
||||
#{object.accessWindowStart,jdbcType=TIME},
|
||||
#{object.accessWindowEnd,jdbcType=TIME},
|
||||
#{object.validFrom,jdbcType=DATE},
|
||||
#{object.validUntil,jdbcType=DATE},
|
||||
#{object.timeZone,jdbcType=VARCHAR}
|
||||
)
|
||||
|
||||
</insert>
|
||||
@@ -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}
|
||||
</update>
|
||||
|
||||
|
Reference in New Issue
Block a user