mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-07 05:31:22 +00:00
GUAC-1176: Implement password reset logic within UserService, like the rest of user auth.
This commit is contained in:
@@ -24,17 +24,10 @@ package org.glyptodon.guacamole.auth.jdbc.user;
|
|||||||
|
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
import java.util.Arrays;
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import org.glyptodon.guacamole.GuacamoleClientException;
|
|
||||||
import org.glyptodon.guacamole.GuacamoleException;
|
import org.glyptodon.guacamole.GuacamoleException;
|
||||||
import org.glyptodon.guacamole.form.Field;
|
|
||||||
import org.glyptodon.guacamole.net.auth.Credentials;
|
import org.glyptodon.guacamole.net.auth.Credentials;
|
||||||
import org.glyptodon.guacamole.net.auth.credentials.CredentialsInfo;
|
import org.glyptodon.guacamole.net.auth.credentials.CredentialsInfo;
|
||||||
import org.glyptodon.guacamole.net.auth.credentials.GuacamoleInsufficientCredentialsException;
|
|
||||||
import org.glyptodon.guacamole.net.auth.credentials.GuacamoleInvalidCredentialsException;
|
import org.glyptodon.guacamole.net.auth.credentials.GuacamoleInvalidCredentialsException;
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service which creates new UserContext instances for valid users based on
|
* Service which creates new UserContext instances for valid users based on
|
||||||
@@ -44,11 +37,6 @@ import org.slf4j.LoggerFactory;
|
|||||||
*/
|
*/
|
||||||
public class UserContextService {
|
public class UserContextService {
|
||||||
|
|
||||||
/**
|
|
||||||
* Logger for this class.
|
|
||||||
*/
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(UserService.class);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service for accessing users.
|
* Service for accessing users.
|
||||||
*/
|
*/
|
||||||
@@ -61,42 +49,6 @@ public class UserContextService {
|
|||||||
@Inject
|
@Inject
|
||||||
private Provider<UserContext> userContextProvider;
|
private Provider<UserContext> userContextProvider;
|
||||||
|
|
||||||
/**
|
|
||||||
* The name of the HTTP password parameter to expect if the user is
|
|
||||||
* changing their expired password upon login.
|
|
||||||
*/
|
|
||||||
private static final String NEW_PASSWORD_PARAMETER = "new-password";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The password field to provide the user when their password is expired
|
|
||||||
* and must be changed.
|
|
||||||
*/
|
|
||||||
private static final Field NEW_PASSWORD = new Field(NEW_PASSWORD_PARAMETER, "New password", Field.Type.PASSWORD);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The name of the HTTP password confirmation parameter to expect if the
|
|
||||||
* user is changing their expired password upon login.
|
|
||||||
*/
|
|
||||||
private static final String CONFIRM_NEW_PASSWORD_PARAMETER = "confirm-new-password";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The password confirmation field to provide the user when their password
|
|
||||||
* is expired and must be changed.
|
|
||||||
*/
|
|
||||||
private static final Field CONFIRM_NEW_PASSWORD = new Field(CONFIRM_NEW_PASSWORD_PARAMETER, "Confirm new password", Field.Type.PASSWORD);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Information describing the expected credentials if a user's password is
|
|
||||||
* expired. If a user's password is expired, it must be changed during the
|
|
||||||
* login process.
|
|
||||||
*/
|
|
||||||
private static final CredentialsInfo EXPIRED_PASSWORD = new CredentialsInfo(Arrays.asList(
|
|
||||||
CredentialsInfo.USERNAME,
|
|
||||||
CredentialsInfo.PASSWORD,
|
|
||||||
NEW_PASSWORD,
|
|
||||||
CONFIRM_NEW_PASSWORD
|
|
||||||
));
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Authenticates the user having the given credentials, returning a new
|
* Authenticates the user having the given credentials, returning a new
|
||||||
* UserContext instance only if the credentials are valid. If the
|
* UserContext instance only if the credentials are valid. If the
|
||||||
@@ -120,38 +72,7 @@ public class UserContextService {
|
|||||||
|
|
||||||
// Authenticate user
|
// Authenticate user
|
||||||
ModeledUser user = userService.retrieveUser(credentials);
|
ModeledUser user = userService.retrieveUser(credentials);
|
||||||
if (user != null && !user.getModel().isDisabled()) {
|
if (user != null) {
|
||||||
|
|
||||||
// Update password if password is expired
|
|
||||||
if (user.getModel().isExpired()) {
|
|
||||||
|
|
||||||
// Pull new password from HTTP request
|
|
||||||
HttpServletRequest request = credentials.getRequest();
|
|
||||||
String newPassword = request.getParameter(NEW_PASSWORD_PARAMETER);
|
|
||||||
String confirmNewPassword = request.getParameter(CONFIRM_NEW_PASSWORD_PARAMETER);
|
|
||||||
|
|
||||||
// Require new password if account is expired
|
|
||||||
if (newPassword == null || confirmNewPassword == null) {
|
|
||||||
logger.info("The password of user \"{}\" has expired and must be reset.", user.getIdentifier());
|
|
||||||
throw new GuacamoleInsufficientCredentialsException("Password expired", EXPIRED_PASSWORD);
|
|
||||||
}
|
|
||||||
|
|
||||||
// New password must be different from old password
|
|
||||||
if (newPassword.equals(credentials.getPassword()))
|
|
||||||
throw new GuacamoleClientException("LOGIN.ERROR_PASSWORD_SAME");
|
|
||||||
|
|
||||||
// New password must not be blank
|
|
||||||
if (newPassword.isEmpty())
|
|
||||||
throw new GuacamoleClientException("LOGIN.ERROR_PASSWORD_BLANK");
|
|
||||||
|
|
||||||
// Confirm that the password was entered correctly twice
|
|
||||||
if (!newPassword.equals(confirmNewPassword))
|
|
||||||
throw new GuacamoleClientException("LOGIN.ERROR_PASSWORD_MISMATCH");
|
|
||||||
|
|
||||||
// STUB: Change password if new password given
|
|
||||||
logger.info("Resetting expired password of user \"{}\".", user.getIdentifier());
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Upon successful authentication, return new user context
|
// Upon successful authentication, return new user context
|
||||||
UserContext context = userContextProvider.get();
|
UserContext context = userContextProvider.get();
|
||||||
|
@@ -27,6 +27,7 @@ import com.google.inject.Provider;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import org.glyptodon.guacamole.net.auth.Credentials;
|
import org.glyptodon.guacamole.net.auth.Credentials;
|
||||||
import org.glyptodon.guacamole.auth.jdbc.base.ModeledDirectoryObjectMapper;
|
import org.glyptodon.guacamole.auth.jdbc.base.ModeledDirectoryObjectMapper;
|
||||||
import org.glyptodon.guacamole.auth.jdbc.base.ModeledDirectoryObjectService;
|
import org.glyptodon.guacamole.auth.jdbc.base.ModeledDirectoryObjectService;
|
||||||
@@ -37,11 +38,16 @@ import org.glyptodon.guacamole.auth.jdbc.permission.ObjectPermissionMapper;
|
|||||||
import org.glyptodon.guacamole.auth.jdbc.permission.ObjectPermissionModel;
|
import org.glyptodon.guacamole.auth.jdbc.permission.ObjectPermissionModel;
|
||||||
import org.glyptodon.guacamole.auth.jdbc.permission.UserPermissionMapper;
|
import org.glyptodon.guacamole.auth.jdbc.permission.UserPermissionMapper;
|
||||||
import org.glyptodon.guacamole.auth.jdbc.security.PasswordEncryptionService;
|
import org.glyptodon.guacamole.auth.jdbc.security.PasswordEncryptionService;
|
||||||
|
import org.glyptodon.guacamole.form.Field;
|
||||||
import org.glyptodon.guacamole.net.auth.User;
|
import org.glyptodon.guacamole.net.auth.User;
|
||||||
|
import org.glyptodon.guacamole.net.auth.credentials.CredentialsInfo;
|
||||||
|
import org.glyptodon.guacamole.net.auth.credentials.GuacamoleInsufficientCredentialsException;
|
||||||
import org.glyptodon.guacamole.net.auth.permission.ObjectPermission;
|
import org.glyptodon.guacamole.net.auth.permission.ObjectPermission;
|
||||||
import org.glyptodon.guacamole.net.auth.permission.ObjectPermissionSet;
|
import org.glyptodon.guacamole.net.auth.permission.ObjectPermissionSet;
|
||||||
import org.glyptodon.guacamole.net.auth.permission.SystemPermission;
|
import org.glyptodon.guacamole.net.auth.permission.SystemPermission;
|
||||||
import org.glyptodon.guacamole.net.auth.permission.SystemPermissionSet;
|
import org.glyptodon.guacamole.net.auth.permission.SystemPermissionSet;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service which provides convenience methods for creating, retrieving, and
|
* Service which provides convenience methods for creating, retrieving, and
|
||||||
@@ -51,6 +57,11 @@ import org.glyptodon.guacamole.net.auth.permission.SystemPermissionSet;
|
|||||||
*/
|
*/
|
||||||
public class UserService extends ModeledDirectoryObjectService<ModeledUser, User, UserModel> {
|
public class UserService extends ModeledDirectoryObjectService<ModeledUser, User, UserModel> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logger for this class.
|
||||||
|
*/
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(UserService.class);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* All user permissions which are implicitly granted to the new user upon
|
* All user permissions which are implicitly granted to the new user upon
|
||||||
* creation.
|
* creation.
|
||||||
@@ -59,7 +70,43 @@ public class UserService extends ModeledDirectoryObjectService<ModeledUser, User
|
|||||||
ObjectPermission.Type.READ,
|
ObjectPermission.Type.READ,
|
||||||
ObjectPermission.Type.UPDATE
|
ObjectPermission.Type.UPDATE
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the HTTP password parameter to expect if the user is
|
||||||
|
* changing their expired password upon login.
|
||||||
|
*/
|
||||||
|
private static final String NEW_PASSWORD_PARAMETER = "new-password";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The password field to provide the user when their password is expired
|
||||||
|
* and must be changed.
|
||||||
|
*/
|
||||||
|
private static final Field NEW_PASSWORD = new Field(NEW_PASSWORD_PARAMETER, "New password", Field.Type.PASSWORD);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the HTTP password confirmation parameter to expect if the
|
||||||
|
* user is changing their expired password upon login.
|
||||||
|
*/
|
||||||
|
private static final String CONFIRM_NEW_PASSWORD_PARAMETER = "confirm-new-password";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The password confirmation field to provide the user when their password
|
||||||
|
* is expired and must be changed.
|
||||||
|
*/
|
||||||
|
private static final Field CONFIRM_NEW_PASSWORD = new Field(CONFIRM_NEW_PASSWORD_PARAMETER, "Confirm new password", Field.Type.PASSWORD);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Information describing the expected credentials if a user's password is
|
||||||
|
* expired. If a user's password is expired, it must be changed during the
|
||||||
|
* login process.
|
||||||
|
*/
|
||||||
|
private static final CredentialsInfo EXPIRED_PASSWORD = new CredentialsInfo(Arrays.asList(
|
||||||
|
CredentialsInfo.USERNAME,
|
||||||
|
CredentialsInfo.PASSWORD,
|
||||||
|
NEW_PASSWORD,
|
||||||
|
CONFIRM_NEW_PASSWORD
|
||||||
|
));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mapper for accessing users.
|
* Mapper for accessing users.
|
||||||
*/
|
*/
|
||||||
@@ -213,7 +260,9 @@ public class UserService extends ModeledDirectoryObjectService<ModeledUser, User
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the user corresponding to the given credentials from the
|
* Retrieves the user corresponding to the given credentials from the
|
||||||
* database.
|
* database. If the user account is expired, and the credentials contain
|
||||||
|
* the necessary additional parameters to reset the user's password, the
|
||||||
|
* password is reset.
|
||||||
*
|
*
|
||||||
* @param credentials
|
* @param credentials
|
||||||
* The credentials to use when locating the user.
|
* The credentials to use when locating the user.
|
||||||
@@ -221,8 +270,12 @@ public class UserService extends ModeledDirectoryObjectService<ModeledUser, User
|
|||||||
* @return
|
* @return
|
||||||
* The existing ModeledUser object if the credentials given are valid,
|
* The existing ModeledUser object if the credentials given are valid,
|
||||||
* null otherwise.
|
* null otherwise.
|
||||||
|
*
|
||||||
|
* @throws GuacamoleException
|
||||||
|
* If the provided credentials to not conform to expectations.
|
||||||
*/
|
*/
|
||||||
public ModeledUser retrieveUser(Credentials credentials) {
|
public ModeledUser retrieveUser(Credentials credentials)
|
||||||
|
throws GuacamoleException {
|
||||||
|
|
||||||
// Get username and password
|
// Get username and password
|
||||||
String username = credentials.getUsername();
|
String username = credentials.getUsername();
|
||||||
@@ -233,19 +286,55 @@ public class UserService extends ModeledDirectoryObjectService<ModeledUser, User
|
|||||||
if (userModel == null)
|
if (userModel == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
// If password hash matches, return the retrieved user
|
// If user is disabled, pretend user does not exist
|
||||||
byte[] hash = encryptionService.createPasswordHash(password, userModel.getPasswordSalt());
|
if (userModel.isDisabled())
|
||||||
if (Arrays.equals(hash, userModel.getPasswordHash())) {
|
return null;
|
||||||
|
|
||||||
// Return corresponding user, set up cyclic reference
|
// Verify provided password is correct
|
||||||
ModeledUser user = getObjectInstance(null, userModel);
|
byte[] hash = encryptionService.createPasswordHash(password, userModel.getPasswordSalt());
|
||||||
user.setCurrentUser(new AuthenticatedUser(user, credentials));
|
if (!Arrays.equals(hash, userModel.getPasswordHash()))
|
||||||
return user;
|
return null;
|
||||||
|
|
||||||
|
// Create corresponding user object, set up cyclic reference
|
||||||
|
ModeledUser user = getObjectInstance(null, userModel);
|
||||||
|
user.setCurrentUser(new AuthenticatedUser(user, credentials));
|
||||||
|
|
||||||
|
// Update password if password is expired
|
||||||
|
if (userModel.isExpired()) {
|
||||||
|
|
||||||
|
// Pull new password from HTTP request
|
||||||
|
HttpServletRequest request = credentials.getRequest();
|
||||||
|
String newPassword = request.getParameter(NEW_PASSWORD_PARAMETER);
|
||||||
|
String confirmNewPassword = request.getParameter(CONFIRM_NEW_PASSWORD_PARAMETER);
|
||||||
|
|
||||||
|
// Require new password if account is expired
|
||||||
|
if (newPassword == null || confirmNewPassword == null) {
|
||||||
|
logger.info("The password of user \"{}\" has expired and must be reset.", username);
|
||||||
|
throw new GuacamoleInsufficientCredentialsException("LOGIN.INFO_PASSWORD_EXPIRED", EXPIRED_PASSWORD);
|
||||||
|
}
|
||||||
|
|
||||||
|
// New password must be different from old password
|
||||||
|
if (newPassword.equals(credentials.getPassword()))
|
||||||
|
throw new GuacamoleClientException("LOGIN.ERROR_PASSWORD_SAME");
|
||||||
|
|
||||||
|
// New password must not be blank
|
||||||
|
if (newPassword.isEmpty())
|
||||||
|
throw new GuacamoleClientException("LOGIN.ERROR_PASSWORD_BLANK");
|
||||||
|
|
||||||
|
// Confirm that the password was entered correctly twice
|
||||||
|
if (!newPassword.equals(confirmNewPassword))
|
||||||
|
throw new GuacamoleClientException("LOGIN.ERROR_PASSWORD_MISMATCH");
|
||||||
|
|
||||||
|
// Change password and reset expiration flag
|
||||||
|
userModel.setExpired(false);
|
||||||
|
user.setPassword(newPassword);
|
||||||
|
userMapper.update(userModel);
|
||||||
|
logger.info("Expired password of user \"{}\" has been reset.", username);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, the credentials do not match
|
// Return now-authenticated user
|
||||||
return null;
|
return user;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -6,6 +6,8 @@
|
|||||||
"ERROR_PASSWORD_SAME" : "The new password must be different from the expired password.",
|
"ERROR_PASSWORD_SAME" : "The new password must be different from the expired password.",
|
||||||
"ERROR_PASSWORD_MISMATCH" : "@:APP.ERROR_PASSWORD_MISMATCH",
|
"ERROR_PASSWORD_MISMATCH" : "@:APP.ERROR_PASSWORD_MISMATCH",
|
||||||
|
|
||||||
|
"INFO_PASSWORD_EXPIRED" : "Your password has expired and must be reset. Please enter a new password to continue.",
|
||||||
|
|
||||||
"FIELD_HEADER_NEW_PASSWORD" : "New password",
|
"FIELD_HEADER_NEW_PASSWORD" : "New password",
|
||||||
"FIELD_HEADER_CONFIRM_NEW_PASSWORD" : "Confirm new password"
|
"FIELD_HEADER_CONFIRM_NEW_PASSWORD" : "Confirm new password"
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user