GUACAMOLE-36: Merge password aging support.

This commit is contained in:
James Muehlner
2017-01-05 10:25:57 -08:00
16 changed files with 384 additions and 3 deletions

View File

@@ -22,6 +22,7 @@ package org.apache.guacamole.auth.jdbc;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Provider; import com.google.inject.Provider;
import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.jdbc.security.PasswordPolicyService;
import org.apache.guacamole.auth.jdbc.sharing.user.SharedAuthenticatedUser; import org.apache.guacamole.auth.jdbc.sharing.user.SharedAuthenticatedUser;
import org.apache.guacamole.auth.jdbc.user.ModeledUser; import org.apache.guacamole.auth.jdbc.user.ModeledUser;
import org.apache.guacamole.auth.jdbc.user.ModeledUserContext; import org.apache.guacamole.auth.jdbc.user.ModeledUserContext;
@@ -55,6 +56,12 @@ public class JDBCAuthenticationProviderService implements AuthenticationProvider
@Inject @Inject
private UserService userService; private UserService userService;
/**
* Service for enforcing password complexity policies.
*/
@Inject
private PasswordPolicyService passwordPolicyService;
/** /**
* Provider for retrieving UserContext instances. * Provider for retrieving UserContext instances.
*/ */
@@ -101,7 +108,7 @@ public class JDBCAuthenticationProviderService implements AuthenticationProvider
// Update password if password is expired // Update password if password is expired
UserModel userModel = user.getModel(); UserModel userModel = user.getModel();
if (userModel.isExpired()) if (userModel.isExpired() || passwordPolicyService.isPasswordExpired(user))
userService.resetExpiredPassword(user, authenticatedUser.getCredentials()); userService.resetExpiredPassword(user, authenticatedUser.getCredentials());
// Link to user context // Link to user context

View File

@@ -42,6 +42,36 @@ public interface PasswordPolicy {
*/ */
int getMinimumLength() throws GuacamoleException; int getMinimumLength() throws GuacamoleException;
/**
* Returns the minimum number of days which must elapse before the user's
* password may be reset. If this restriction does not apply, this will be
* zero.
*
* @return
* The minimum number of days which must elapse before the user's
* password may be reset, or zero if this restriction does not apply.
*
* @throws GuacamoleException
* If the minimum password age cannot be parsed from
* guacamole.properties.
*/
int getMinimumAge() throws GuacamoleException;
/**
* Returns the maximum number of days which may elapse before the user's
* password must be reset. If this restriction does not apply, this will be
* zero.
*
* @return
* The maximum number of days which may elapse before the user's
* password must be reset, or zero if this restriction does not apply.
*
* @throws GuacamoleException
* If the maximum password age cannot be parsed from
* guacamole.properties.
*/
int getMaximumAge() throws GuacamoleException;
/** /**
* Returns whether both uppercase and lowercase characters must be present * Returns whether both uppercase and lowercase characters must be present
* in new passwords. If true, passwords which do not have at least one * in new passwords. If true, passwords which do not have at least one

View File

@@ -20,10 +20,12 @@
package org.apache.guacamole.auth.jdbc.security; package org.apache.guacamole.auth.jdbc.security;
import com.google.inject.Inject; import com.google.inject.Inject;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.jdbc.JDBCEnvironment; import org.apache.guacamole.auth.jdbc.JDBCEnvironment;
import org.apache.guacamole.auth.jdbc.user.ModeledUser;
/** /**
* Service which verifies compliance with the password policy configured via * Service which verifies compliance with the password policy configured via
@@ -146,4 +148,85 @@ public class PasswordPolicyService {
} }
/**
* Returns the age of the given user's password, in days. The age of a
* user's password is the amount of time elapsed since the password was last
* changed or reset.
*
* @param user
* The user to calculate the password age of.
*
* @return
* The age of the given user's password, in days.
*/
private long getPasswordAge(ModeledUser user) {
// Pull both current time and the time the password was last reset
long currentTime = System.currentTimeMillis();
long lastResetTime = user.getPreviousPasswordDate().getTime();
// Calculate the number of days elapsed since the password was last reset
return TimeUnit.DAYS.convert(currentTime - lastResetTime, TimeUnit.MILLISECONDS);
}
/**
* Verifies that the given user can change their password without violating
* password aging policy. If changing the user's password would violate the
* aging policy, a GuacamoleException will be thrown.
*
* @param user
* The user whose password is changing.
*
* @throws GuacamoleException
* If the user's password cannot be changed due to the password aging
* policy, or of the password policy cannot be parsed from
* guacamole.properties.
*/
public void verifyPasswordAge(ModeledUser user) throws GuacamoleException {
// Retrieve password policy from environment
PasswordPolicy policy = environment.getPasswordPolicy();
long minimumAge = policy.getMinimumAge();
long passwordAge = getPasswordAge(user);
// Require that sufficient time has elapsed before allowing the password
// to be changed
if (passwordAge < minimumAge)
throw new PasswordTooYoungException("Password was already recently changed.",
minimumAge - passwordAge);
}
/**
* Returns whether the given user's password is expired due to the password
* aging policy.
*
* @param user
* The user to check.
*
* @return
* true if the user needs to change their password to comply with the
* password aging policy, false otherwise.
*
* @throws GuacamoleException
* If the password policy cannot be parsed.
*/
public boolean isPasswordExpired(ModeledUser user)
throws GuacamoleException {
// Retrieve password policy from environment
PasswordPolicy policy = environment.getPasswordPolicy();
// There is no maximum password age if 0
int maxPasswordAge = policy.getMaximumAge();
if (maxPasswordAge == 0)
return false;
// Determine whether password is expired based on maximum age
return getPasswordAge(user) >= maxPasswordAge;
}
} }

View File

@@ -0,0 +1,53 @@
/*
* 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.jdbc.security;
import java.util.Collections;
import org.apache.guacamole.language.TranslatableMessage;
/**
* Thrown when an attempt is made to set a user's password before sufficient
* time has elapsed since the password was last reset, in violation of the
* defined password policy.
*
* @author Michael Jumper
*/
public class PasswordTooYoungException extends PasswordPolicyException {
/**
* Creates a new PasswordTooYoungException with the given human-readable
* message. The translatable message is already defined.
*
* @param message
* A human-readable message describing the password policy violation
* that occurred.
*
* @param wait
* The number of days the user should wait before attempting to reset
* the password again.
*/
public PasswordTooYoungException(String message, long wait) {
super(message, new TranslatableMessage(
"PASSWORD_POLICY.ERROR_TOO_YOUNG",
Collections.singletonMap("WAIT", wait)
));
}
}

View File

@@ -22,6 +22,7 @@ package org.apache.guacamole.auth.jdbc.user;
import com.google.inject.Inject; import com.google.inject.Inject;
import java.sql.Date; import java.sql.Date;
import java.sql.Time; import java.sql.Time;
import java.sql.Timestamp;
import java.text.ParseException; import java.text.ParseException;
import java.util.Arrays; import java.util.Arrays;
import java.util.Calendar; import java.util.Calendar;
@@ -186,6 +187,12 @@ public class ModeledUser extends ModeledDirectoryObject<UserModel> implements Us
* user was retrieved from the database, this will be null. * user was retrieved from the database, this will be null.
*/ */
private String password = null; private String password = null;
/**
* The time and date that this user's password was previously set (prior to
* being queried). If the user is new, this will be null.
*/
private Timestamp previousPasswordDate = null;
/** /**
* Creates a new, empty ModeledUser. * Creates a new, empty ModeledUser.
@@ -193,6 +200,12 @@ public class ModeledUser extends ModeledDirectoryObject<UserModel> implements Us
public ModeledUser() { public ModeledUser() {
} }
@Override
public void setModel(UserModel model) {
super.setModel(model);
this.previousPasswordDate = model.getPasswordDate();
}
@Override @Override
public String getPassword() { public String getPassword() {
return password; return password;
@@ -222,6 +235,24 @@ public class ModeledUser extends ModeledDirectoryObject<UserModel> implements Us
userModel.setPasswordHash(hash); userModel.setPasswordHash(hash);
} }
userModel.setPasswordDate(new Timestamp(System.currentTimeMillis()));
}
/**
* Returns the time and date that this user's password was previously set.
* If the user is new, this will be null. Unlike getPasswordDate() of
* UserModel (which is updated automatically along with the password salt
* and hash whenever setPassword() is invoked), this value is unaffected by
* calls to setPassword(), and will always be the value stored in the
* database at the time this user was queried.
*
* @return
* The time and date that this user's password was previously set, or
* null if the user is new.
*/
public Timestamp getPreviousPasswordDate() {
return previousPasswordDate;
} }
/** /**

View File

@@ -21,6 +21,7 @@ package org.apache.guacamole.auth.jdbc.user;
import java.sql.Date; import java.sql.Date;
import java.sql.Time; import java.sql.Time;
import java.sql.Timestamp;
import org.apache.guacamole.auth.jdbc.base.ObjectModel; import org.apache.guacamole.auth.jdbc.base.ObjectModel;
/** /**
@@ -41,6 +42,11 @@ public class UserModel extends ObjectModel {
*/ */
private byte[] passwordSalt; private byte[] passwordSalt;
/**
* The time this user's password was last reset.
*/
private Timestamp passwordDate;
/** /**
* Whether the user account is disabled. Disabled accounts exist and can * Whether the user account is disabled. Disabled accounts exist and can
* be modified, but cannot be used. * be modified, but cannot be used.
@@ -143,6 +149,30 @@ public class UserModel extends ObjectModel {
this.passwordSalt = passwordSalt; this.passwordSalt = passwordSalt;
} }
/**
* Returns the date that this user's password was last set/reset. This
* value is required to be manually updated whenever the user's password is
* changed; it will not be automatically updated by the database.
*
* @return
* The date that this user's password was last set/reset.
*/
public Timestamp getPasswordDate() {
return passwordDate;
}
/**
* Sets the date that this user's password was last set/reset. This
* value is required to be manually updated whenever the user's password is
* changed; it will not be automatically updated by the database.
*
* @param passwordDate
* The date that this user's password was last set/reset.
*/
public void setPasswordDate(Timestamp passwordDate) {
this.passwordDate = passwordDate;
}
/** /**
* Returns whether the user has been disabled. Disabled users are not * Returns whether the user has been disabled. Disabled users are not
* allowed to login. Although their account data exists, all login attempts * allowed to login. Although their account data exists, all login attempts

View File

@@ -233,9 +233,17 @@ public class UserService extends ModeledDirectoryObjectService<ModeledUser, User
} }
// Verify new password does not violate defined policies (if specified) // Verify new password does not violate defined policies (if specified)
if (object.getPassword() != null) if (object.getPassword() != null) {
// Enforce password age only for non-adminstrators
if (!user.getUser().isAdministrator())
passwordPolicyService.verifyPasswordAge(object);
// Always verify password complexity
passwordPolicyService.verifyPassword(object.getIdentifier(), object.getPassword()); passwordPolicyService.verifyPassword(object.getIdentifier(), object.getPassword());
}
} }
@Override @Override

View File

@@ -60,7 +60,8 @@
"ERROR_REQUIRES_DIGIT" : "Passwords must contain at least one digit.", "ERROR_REQUIRES_DIGIT" : "Passwords must contain at least one digit.",
"ERROR_REQUIRES_MULTIPLE_CASE" : "Passwords must contain both uppercase and lowercase characters.", "ERROR_REQUIRES_MULTIPLE_CASE" : "Passwords must contain both uppercase and lowercase characters.",
"ERROR_REQUIRES_NON_ALNUM" : "Passwords must contain at least one symbol.", "ERROR_REQUIRES_NON_ALNUM" : "Passwords must contain at least one symbol.",
"ERROR_TOO_SHORT" : "Passwords must be at least {LENGTH} {LENGTH, plural, one{character} other{characters}} long." "ERROR_TOO_SHORT" : "Passwords must be at least {LENGTH} {LENGTH, plural, one{character} other{characters}} long.",
"ERROR_TOO_YOUNG" : "The password for this account has already been reset. Please wait at least {WAIT} more {WAIT, plural, one{day} other{days}} before changing the password again."
}, },

View File

@@ -85,6 +85,7 @@ CREATE TABLE `guacamole_user` (
`username` varchar(128) NOT NULL, `username` varchar(128) NOT NULL,
`password_hash` binary(32) NOT NULL, `password_hash` binary(32) NOT NULL,
`password_salt` binary(32), `password_salt` binary(32),
`password_date` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
-- Account disabled/expired status -- Account disabled/expired status
`disabled` boolean NOT NULL DEFAULT 0, `disabled` boolean NOT NULL DEFAULT 0,

View File

@@ -0,0 +1,25 @@
--
-- 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.
--
--
-- Add per-user password set date
--
ALTER TABLE guacamole_user
ADD COLUMN password_date DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP;

View File

@@ -45,6 +45,32 @@ public class MySQLPasswordPolicy implements PasswordPolicy {
}; };
/**
* The property which specifies the minimum number of days which must
* elapse before a user may reset their password. If set to zero, the
* default, then this restriction does not apply.
*/
private static final IntegerGuacamoleProperty MIN_AGE =
new IntegerGuacamoleProperty() {
@Override
public String getName() { return "mysql-user-password-min-age"; }
};
/**
* The property which specifies the maximum number of days which may
* elapse before a user is required to reset their password. If set to zero,
* the default, then this restriction does not apply.
*/
private static final IntegerGuacamoleProperty MAX_AGE =
new IntegerGuacamoleProperty() {
@Override
public String getName() { return "mysql-user-password-max-age"; }
};
/** /**
* The property which specifies whether all user passwords must have at * The property which specifies whether all user passwords must have at
* least one lowercase character and one uppercase character. By default, * least one lowercase character and one uppercase character. By default,
@@ -119,6 +145,16 @@ public class MySQLPasswordPolicy implements PasswordPolicy {
return environment.getProperty(MIN_LENGTH, 0); return environment.getProperty(MIN_LENGTH, 0);
} }
@Override
public int getMinimumAge() throws GuacamoleException {
return environment.getProperty(MIN_AGE, 0);
}
@Override
public int getMaximumAge() throws GuacamoleException {
return environment.getProperty(MAX_AGE, 0);
}
@Override @Override
public boolean isMultipleCaseRequired() throws GuacamoleException { public boolean isMultipleCaseRequired() throws GuacamoleException {
return environment.getProperty(REQUIRE_MULTIPLE_CASE, false); return environment.getProperty(REQUIRE_MULTIPLE_CASE, false);

View File

@@ -29,6 +29,7 @@
<result column="username" property="identifier" jdbcType="VARCHAR"/> <result column="username" property="identifier" jdbcType="VARCHAR"/>
<result column="password_hash" property="passwordHash" jdbcType="BINARY"/> <result column="password_hash" property="passwordHash" jdbcType="BINARY"/>
<result column="password_salt" property="passwordSalt" jdbcType="BINARY"/> <result column="password_salt" property="passwordSalt" jdbcType="BINARY"/>
<result column="password_date" property="passwordDate" jdbcType="TIMESTAMP"/>
<result column="disabled" property="disabled" jdbcType="BOOLEAN"/> <result column="disabled" property="disabled" jdbcType="BOOLEAN"/>
<result column="access_window_start" property="accessWindowStart" jdbcType="TIME"/> <result column="access_window_start" property="accessWindowStart" jdbcType="TIME"/>
<result column="access_window_end" property="accessWindowEnd" jdbcType="TIME"/> <result column="access_window_end" property="accessWindowEnd" jdbcType="TIME"/>
@@ -61,6 +62,7 @@
username, username,
password_hash, password_hash,
password_salt, password_salt,
password_date,
disabled, disabled,
expired, expired,
access_window_start, access_window_start,
@@ -85,6 +87,7 @@
username, username,
password_hash, password_hash,
password_salt, password_salt,
password_date,
disabled, disabled,
expired, expired,
access_window_start, access_window_start,
@@ -112,6 +115,7 @@
username, username,
password_hash, password_hash,
password_salt, password_salt,
password_date,
disabled, disabled,
expired, expired,
access_window_start, access_window_start,
@@ -139,6 +143,7 @@
username, username,
password_hash, password_hash,
password_salt, password_salt,
password_date,
disabled, disabled,
expired, expired,
access_window_start, access_window_start,
@@ -151,6 +156,7 @@
#{object.identifier,jdbcType=VARCHAR}, #{object.identifier,jdbcType=VARCHAR},
#{object.passwordHash,jdbcType=BINARY}, #{object.passwordHash,jdbcType=BINARY},
#{object.passwordSalt,jdbcType=BINARY}, #{object.passwordSalt,jdbcType=BINARY},
#{object.passwordDate,jdbcType=TIMESTAMP},
#{object.disabled,jdbcType=BOOLEAN}, #{object.disabled,jdbcType=BOOLEAN},
#{object.expired,jdbcType=BOOLEAN}, #{object.expired,jdbcType=BOOLEAN},
#{object.accessWindowStart,jdbcType=TIME}, #{object.accessWindowStart,jdbcType=TIME},
@@ -167,6 +173,7 @@
UPDATE guacamole_user UPDATE guacamole_user
SET password_hash = #{object.passwordHash,jdbcType=BINARY}, SET password_hash = #{object.passwordHash,jdbcType=BINARY},
password_salt = #{object.passwordSalt,jdbcType=BINARY}, password_salt = #{object.passwordSalt,jdbcType=BINARY},
password_date = #{object.passwordDate,jdbcType=TIMESTAMP},
disabled = #{object.disabled,jdbcType=BOOLEAN}, disabled = #{object.disabled,jdbcType=BOOLEAN},
expired = #{object.expired,jdbcType=BOOLEAN}, expired = #{object.expired,jdbcType=BOOLEAN},
access_window_start = #{object.accessWindowStart,jdbcType=TIME}, access_window_start = #{object.accessWindowStart,jdbcType=TIME},

View File

@@ -126,6 +126,7 @@ CREATE TABLE guacamole_user (
username varchar(128) NOT NULL, username varchar(128) NOT NULL,
password_hash bytea NOT NULL, password_hash bytea NOT NULL,
password_salt bytea, password_salt bytea,
password_date timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP,
-- Account disabled/expired status -- Account disabled/expired status
disabled boolean NOT NULL DEFAULT FALSE, disabled boolean NOT NULL DEFAULT FALSE,

View File

@@ -0,0 +1,25 @@
--
-- 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.
--
--
-- Add per-user password set date
--
ALTER TABLE guacamole_user
ADD COLUMN password_date timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP;

View File

@@ -45,6 +45,32 @@ public class PostgreSQLPasswordPolicy implements PasswordPolicy {
}; };
/**
* The property which specifies the minimum number of days which must
* elapse before a user may reset their password. If set to zero, the
* default, then this restriction does not apply.
*/
private static final IntegerGuacamoleProperty MIN_AGE =
new IntegerGuacamoleProperty() {
@Override
public String getName() { return "postgresql-user-password-min-age"; }
};
/**
* The property which specifies the maximum number of days which may
* elapse before a user is required to reset their password. If set to zero,
* the default, then this restriction does not apply.
*/
private static final IntegerGuacamoleProperty MAX_AGE =
new IntegerGuacamoleProperty() {
@Override
public String getName() { return "postgresql-user-password-max-age"; }
};
/** /**
* The property which specifies whether all user passwords must have at * The property which specifies whether all user passwords must have at
* least one lowercase character and one uppercase character. By default, * least one lowercase character and one uppercase character. By default,
@@ -119,6 +145,16 @@ public class PostgreSQLPasswordPolicy implements PasswordPolicy {
return environment.getProperty(MIN_LENGTH, 0); return environment.getProperty(MIN_LENGTH, 0);
} }
@Override
public int getMinimumAge() throws GuacamoleException {
return environment.getProperty(MIN_AGE, 0);
}
@Override
public int getMaximumAge() throws GuacamoleException {
return environment.getProperty(MAX_AGE, 0);
}
@Override @Override
public boolean isMultipleCaseRequired() throws GuacamoleException { public boolean isMultipleCaseRequired() throws GuacamoleException {
return environment.getProperty(REQUIRE_MULTIPLE_CASE, false); return environment.getProperty(REQUIRE_MULTIPLE_CASE, false);

View File

@@ -29,6 +29,7 @@
<result column="username" property="identifier" jdbcType="VARCHAR"/> <result column="username" property="identifier" jdbcType="VARCHAR"/>
<result column="password_hash" property="passwordHash" jdbcType="BINARY"/> <result column="password_hash" property="passwordHash" jdbcType="BINARY"/>
<result column="password_salt" property="passwordSalt" jdbcType="BINARY"/> <result column="password_salt" property="passwordSalt" jdbcType="BINARY"/>
<result column="password_date" property="passwordDate" jdbcType="TIMESTAMP"/>
<result column="disabled" property="disabled" jdbcType="BOOLEAN"/> <result column="disabled" property="disabled" jdbcType="BOOLEAN"/>
<result column="expired" property="expired" jdbcType="BOOLEAN"/> <result column="expired" property="expired" jdbcType="BOOLEAN"/>
<result column="access_window_start" property="accessWindowStart" jdbcType="TIME"/> <result column="access_window_start" property="accessWindowStart" jdbcType="TIME"/>
@@ -62,6 +63,7 @@
username, username,
password_hash, password_hash,
password_salt, password_salt,
password_date,
disabled, disabled,
expired, expired,
access_window_start, access_window_start,
@@ -86,6 +88,7 @@
username, username,
password_hash, password_hash,
password_salt, password_salt,
password_date,
disabled, disabled,
expired, expired,
access_window_start, access_window_start,
@@ -113,6 +116,7 @@
username, username,
password_hash, password_hash,
password_salt, password_salt,
password_date,
disabled, disabled,
expired, expired,
access_window_start, access_window_start,
@@ -140,6 +144,7 @@
username, username,
password_hash, password_hash,
password_salt, password_salt,
password_date,
disabled, disabled,
expired, expired,
access_window_start, access_window_start,
@@ -152,6 +157,7 @@
#{object.identifier,jdbcType=VARCHAR}, #{object.identifier,jdbcType=VARCHAR},
#{object.passwordHash,jdbcType=BINARY}, #{object.passwordHash,jdbcType=BINARY},
#{object.passwordSalt,jdbcType=BINARY}, #{object.passwordSalt,jdbcType=BINARY},
#{object.passwordDate,jdbcType=TIMESTAMP},
#{object.disabled,jdbcType=BOOLEAN}, #{object.disabled,jdbcType=BOOLEAN},
#{object.expired,jdbcType=BOOLEAN}, #{object.expired,jdbcType=BOOLEAN},
#{object.accessWindowStart,jdbcType=TIME}, #{object.accessWindowStart,jdbcType=TIME},
@@ -168,6 +174,7 @@
UPDATE guacamole_user UPDATE guacamole_user
SET password_hash = #{object.passwordHash,jdbcType=BINARY}, SET password_hash = #{object.passwordHash,jdbcType=BINARY},
password_salt = #{object.passwordSalt,jdbcType=BINARY}, password_salt = #{object.passwordSalt,jdbcType=BINARY},
password_date = #{object.passwordDate,jdbcType=TIMESTAMP},
disabled = #{object.disabled,jdbcType=BOOLEAN}, disabled = #{object.disabled,jdbcType=BOOLEAN},
expired = #{object.expired,jdbcType=BOOLEAN}, expired = #{object.expired,jdbcType=BOOLEAN},
access_window_start = #{object.accessWindowStart,jdbcType=TIME}, access_window_start = #{object.accessWindowStart,jdbcType=TIME},