GUAC-586: Implement AuthenticatedUser. Refactor to support authenticateUser(), etc. within the database AuthenticationProvider implementations.

This commit is contained in:
Michael Jumper
2015-08-23 23:55:42 -07:00
parent 90ae5b0e17
commit 6eee1e758c
5 changed files with 172 additions and 35 deletions

View File

@@ -25,6 +25,7 @@ package org.glyptodon.guacamole.auth.jdbc.user;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import org.glyptodon.guacamole.net.auth.AuthenticationProvider;
import org.glyptodon.guacamole.net.auth.Credentials; import org.glyptodon.guacamole.net.auth.Credentials;
/** /**
@@ -32,7 +33,7 @@ import org.glyptodon.guacamole.net.auth.Credentials;
* *
* @author Michael Jumper * @author Michael Jumper
*/ */
public class AuthenticatedUser { public class AuthenticatedUser implements org.glyptodon.guacamole.net.auth.AuthenticatedUser {
/** /**
* The user that authenticated. * The user that authenticated.
@@ -44,6 +45,11 @@ public class AuthenticatedUser {
*/ */
private final Credentials credentials; private final Credentials credentials;
/**
* The AuthenticationProvider that authenticated this user.
*/
private final AuthenticationProvider authenticationProvider;
/** /**
* The host from which this user authenticated. * The host from which this user authenticated.
*/ */
@@ -106,13 +112,18 @@ public class AuthenticatedUser {
* Creates a new AuthenticatedUser associating the given user with their * Creates a new AuthenticatedUser associating the given user with their
* corresponding credentials. * corresponding credentials.
* *
* @param authenticationProvider
* The AuthenticationProvider that has authenticated the given user.
*
* @param user * @param user
* The user this object should represent. * The user this object should represent.
* *
* @param credentials * @param credentials
* The credentials given by the user when they authenticated. * The credentials given by the user when they authenticated.
*/ */
public AuthenticatedUser(ModeledUser user, Credentials credentials) { public AuthenticatedUser(AuthenticationProvider authenticationProvider,
ModeledUser user, Credentials credentials) {
this.authenticationProvider = authenticationProvider;
this.user = user; this.user = user;
this.credentials = credentials; this.credentials = credentials;
this.remoteHost = getRemoteHost(credentials); this.remoteHost = getRemoteHost(credentials);
@@ -134,6 +145,7 @@ public class AuthenticatedUser {
* @return * @return
* The credentials given during authentication by this user. * The credentials given during authentication by this user.
*/ */
@Override
public Credentials getCredentials() { public Credentials getCredentials() {
return credentials; return credentials;
} }
@@ -148,4 +160,19 @@ public class AuthenticatedUser {
return remoteHost; return remoteHost;
} }
@Override
public AuthenticationProvider getAuthenticationProvider() {
return authenticationProvider;
}
@Override
public String getIdentifier() {
return user.getIdentifier();
}
@Override
public void setIdentifier(String identifier) {
user.setIdentifier(identifier);
}
} }

View File

@@ -25,17 +25,19 @@ 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 org.glyptodon.guacamole.GuacamoleException; import org.glyptodon.guacamole.GuacamoleException;
import org.glyptodon.guacamole.net.auth.AuthenticationProvider;
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.GuacamoleInvalidCredentialsException; import org.glyptodon.guacamole.net.auth.credentials.GuacamoleInvalidCredentialsException;
/** /**
* Service which creates new UserContext instances for valid users based on * Service which authenticates users based on credentials and provides for
* credentials. * the creation of corresponding, new UserContext objects for authenticated
* users.
* *
* @author Michael Jumper * @author Michael Jumper
*/ */
public class UserContextService { public class AuthenticationProviderService {
/** /**
* Service for accessing users. * Service for accessing users.
@@ -51,11 +53,44 @@ public class UserContextService {
/** /**
* 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 * AuthenticatedUser instance only if the credentials are valid. If the
* credentials are invalid or expired, an appropriate GuacamoleException * credentials are invalid or expired, an appropriate GuacamoleException
* will be thrown. * will be thrown.
* *
* @param authenticationProvider
* The AuthenticationProvider on behalf of which the user is being
* authenticated.
*
* @param credentials * @param credentials
* The credentials to use to produce the AuthenticatedUser.
*
* @return
* A new AuthenticatedUser instance for the user identified by the
* given credentials.
*
* @throws GuacamoleException
* If an error occurs during authentication, or if the given
* credentials are invalid or expired.
*/
public AuthenticatedUser authenticateUser(AuthenticationProvider authenticationProvider,
Credentials credentials) throws GuacamoleException {
// Authenticate user
AuthenticatedUser user = userService.retrieveAuthenticatedUser(authenticationProvider, credentials);
if (user != null)
return user;
// Otherwise, unauthorized
throw new GuacamoleInvalidCredentialsException("Invalid login", CredentialsInfo.USERNAME_PASSWORD);
}
/**
* Returning a new UserContext instance for the given already-authenticated
* user. A new placeholder account will be created for any user that does
* not already exist within the database.
*
* @param authenticatedUser
* The credentials to use to produce the UserContext. * The credentials to use to produce the UserContext.
* *
* @return * @return
@@ -66,23 +101,18 @@ public class UserContextService {
* If an error occurs during authentication, or if the given * If an error occurs during authentication, or if the given
* credentials are invalid or expired. * credentials are invalid or expired.
*/ */
public org.glyptodon.guacamole.net.auth.UserContext public UserContext getUserContext(org.glyptodon.guacamole.net.auth.AuthenticatedUser authenticatedUser)
getUserContext(Credentials credentials)
throws GuacamoleException { throws GuacamoleException {
// Authenticate user // Retrieve user account for already-authenticated user
ModeledUser user = userService.retrieveUser(credentials); ModeledUser user = userService.retrieveUser(authenticatedUser);
if (user != null) { if (user == null)
return null;
// Upon successful authentication, return new user context // Link to user context
UserContext context = userContextProvider.get(); UserContext context = userContextProvider.get();
context.init(user.getCurrentUser()); context.init(user.getCurrentUser());
return context; return context;
}
// Otherwise, unauthorized
throw new GuacamoleInvalidCredentialsException("Invalid login", CredentialsInfo.USERNAME_PASSWORD);
} }

View File

@@ -40,6 +40,7 @@ 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.form.Field;
import org.glyptodon.guacamole.form.PasswordField; import org.glyptodon.guacamole.form.PasswordField;
import org.glyptodon.guacamole.net.auth.AuthenticationProvider;
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.CredentialsInfo;
import org.glyptodon.guacamole.net.auth.credentials.GuacamoleInsufficientCredentialsException; import org.glyptodon.guacamole.net.auth.credentials.GuacamoleInsufficientCredentialsException;
@@ -265,18 +266,22 @@ public class UserService extends ModeledDirectoryObjectService<ModeledUser, User
* the necessary additional parameters to reset the user's password, the * the necessary additional parameters to reset the user's password, the
* password is reset. * password is reset.
* *
* @param authenticationProvider
* The AuthenticationProvider on behalf of which the user is being
* retrieved.
*
* @param credentials * @param credentials
* The credentials to use when locating the user. * The credentials to use when locating the user.
* *
* @return * @return
* The existing ModeledUser object if the credentials given are valid, * An AuthenticatedUser containing the existing ModeledUser object if
* null otherwise. * the credentials given are valid, null otherwise.
* *
* @throws GuacamoleException * @throws GuacamoleException
* If the provided credentials to not conform to expectations. * If the provided credentials to not conform to expectations.
*/ */
public ModeledUser retrieveUser(Credentials credentials) public AuthenticatedUser retrieveAuthenticatedUser(AuthenticationProvider authenticationProvider,
throws GuacamoleException { Credentials credentials) throws GuacamoleException {
// Get username and password // Get username and password
String username = credentials.getUsername(); String username = credentials.getUsername();
@@ -298,7 +303,7 @@ public class UserService extends ModeledDirectoryObjectService<ModeledUser, User
// Create corresponding user object, set up cyclic reference // Create corresponding user object, set up cyclic reference
ModeledUser user = getObjectInstance(null, userModel); ModeledUser user = getObjectInstance(null, userModel);
user.setCurrentUser(new AuthenticatedUser(user, credentials)); user.setCurrentUser(new AuthenticatedUser(authenticationProvider, user, credentials));
// Verify user account is still valid as of today // Verify user account is still valid as of today
if (!user.isAccountValid()) if (!user.isAccountValid())
@@ -343,6 +348,41 @@ public class UserService extends ModeledDirectoryObjectService<ModeledUser, User
} }
// Return now-authenticated user // Return now-authenticated user
return user.getCurrentUser();
}
/**
* Retrieves the user corresponding to the given AuthenticatedUser from the
* database. If no such user exists, a placeholder entry will be created
* first.
*
* @param authenticatedUser
* The AuthenticatedUser to retrieve the corresponding ModeledUser of.
*
* @return
* The ModeledUser which corresponds to the given AuthenticatedUser,
* created first if necessary.
*/
public ModeledUser retrieveUser(org.glyptodon.guacamole.net.auth.AuthenticatedUser authenticatedUser) {
// If we already queried this user, return that rather than querying again
if (authenticatedUser instanceof AuthenticatedUser)
return ((AuthenticatedUser) authenticatedUser).getUser();
// Get username
String username = authenticatedUser.getIdentifier();
// Retrieve corresponding user model, if such a user exists
UserModel userModel = userMapper.selectOne(username);
if (userModel == null)
return null;
// Create corresponding user object, set up cyclic reference
ModeledUser user = getObjectInstance(null, userModel);
user.setCurrentUser(new AuthenticatedUser(authenticatedUser.getAuthenticationProvider(), user, authenticatedUser.getCredentials()));
// Return already-authenticated user
return user; return user;
} }

View File

@@ -31,9 +31,10 @@ import org.glyptodon.guacamole.net.auth.UserContext;
import org.glyptodon.guacamole.auth.jdbc.JDBCAuthenticationProviderModule; import org.glyptodon.guacamole.auth.jdbc.JDBCAuthenticationProviderModule;
import org.glyptodon.guacamole.auth.jdbc.tunnel.GuacamoleTunnelService; import org.glyptodon.guacamole.auth.jdbc.tunnel.GuacamoleTunnelService;
import org.glyptodon.guacamole.auth.jdbc.tunnel.ConfigurableGuacamoleTunnelService; import org.glyptodon.guacamole.auth.jdbc.tunnel.ConfigurableGuacamoleTunnelService;
import org.glyptodon.guacamole.auth.jdbc.user.UserContextService; import org.glyptodon.guacamole.auth.jdbc.user.AuthenticationProviderService;
import org.glyptodon.guacamole.environment.Environment; import org.glyptodon.guacamole.environment.Environment;
import org.glyptodon.guacamole.environment.LocalEnvironment; import org.glyptodon.guacamole.environment.LocalEnvironment;
import org.glyptodon.guacamole.net.auth.AuthenticatedUser;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -191,18 +192,37 @@ public class MySQLAuthenticationProvider implements AuthenticationProvider {
} }
@Override @Override
public UserContext getUserContext(Credentials credentials) public AuthenticatedUser authenticateUser(Credentials credentials)
throws GuacamoleException {
// Create AuthenticatedUser based on credentials, if valid
AuthenticationProviderService authProviderService = injector.getInstance(AuthenticationProviderService.class);
return authProviderService.authenticateUser(this, credentials);
}
@Override
public AuthenticatedUser updateAuthenticatedUser(AuthenticatedUser authenticatedUser,
Credentials credentials) throws GuacamoleException {
// No need to update authenticated users
return authenticatedUser;
}
@Override
public UserContext getUserContext(AuthenticatedUser authenticatedUser)
throws GuacamoleException { throws GuacamoleException {
// Create UserContext based on credentials, if valid // Create UserContext based on credentials, if valid
UserContextService userContextService = injector.getInstance(UserContextService.class); AuthenticationProviderService authProviderService = injector.getInstance(AuthenticationProviderService.class);
return userContextService.getUserContext(credentials); return authProviderService.getUserContext(authenticatedUser);
} }
@Override @Override
public UserContext updateUserContext(UserContext context, public UserContext updateUserContext(UserContext context,
Credentials credentials) throws GuacamoleException { AuthenticatedUser authenticatedUser) throws GuacamoleException {
// No need to update the context // No need to update the context
return context; return context;

View File

@@ -31,9 +31,10 @@ import org.glyptodon.guacamole.net.auth.UserContext;
import org.glyptodon.guacamole.auth.jdbc.JDBCAuthenticationProviderModule; import org.glyptodon.guacamole.auth.jdbc.JDBCAuthenticationProviderModule;
import org.glyptodon.guacamole.auth.jdbc.tunnel.ConfigurableGuacamoleTunnelService; import org.glyptodon.guacamole.auth.jdbc.tunnel.ConfigurableGuacamoleTunnelService;
import org.glyptodon.guacamole.auth.jdbc.tunnel.GuacamoleTunnelService; import org.glyptodon.guacamole.auth.jdbc.tunnel.GuacamoleTunnelService;
import org.glyptodon.guacamole.auth.jdbc.user.UserContextService; import org.glyptodon.guacamole.auth.jdbc.user.AuthenticationProviderService;
import org.glyptodon.guacamole.environment.Environment; import org.glyptodon.guacamole.environment.Environment;
import org.glyptodon.guacamole.environment.LocalEnvironment; import org.glyptodon.guacamole.environment.LocalEnvironment;
import org.glyptodon.guacamole.net.auth.AuthenticatedUser;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -191,18 +192,37 @@ public class PostgreSQLAuthenticationProvider implements AuthenticationProvider
} }
@Override @Override
public UserContext getUserContext(Credentials credentials) public AuthenticatedUser authenticateUser(Credentials credentials)
throws GuacamoleException {
// Create AuthenticatedUser based on credentials, if valid
AuthenticationProviderService authProviderService = injector.getInstance(AuthenticationProviderService.class);
return authProviderService.authenticateUser(this, credentials);
}
@Override
public AuthenticatedUser updateAuthenticatedUser(AuthenticatedUser authenticatedUser,
Credentials credentials) throws GuacamoleException {
// No need to update authenticated users
return authenticatedUser;
}
@Override
public UserContext getUserContext(AuthenticatedUser authenticatedUser)
throws GuacamoleException { throws GuacamoleException {
// Create UserContext based on credentials, if valid // Create UserContext based on credentials, if valid
UserContextService userContextService = injector.getInstance(UserContextService.class); AuthenticationProviderService authProviderService = injector.getInstance(AuthenticationProviderService.class);
return userContextService.getUserContext(credentials); return authProviderService.getUserContext(authenticatedUser);
} }
@Override @Override
public UserContext updateUserContext(UserContext context, public UserContext updateUserContext(UserContext context,
Credentials credentials) throws GuacamoleException { AuthenticatedUser authenticatedUser) throws GuacamoleException {
// No need to update the context // No need to update the context
return context; return context;