GUAC-586: Separate authentication from authorization.

This commit is contained in:
Michael Jumper
2015-08-20 17:31:24 -07:00
parent 843682c329
commit 90ae5b0e17
8 changed files with 487 additions and 87 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2014 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
@@ -27,7 +27,7 @@ import java.util.concurrent.ConcurrentHashMap;
import org.glyptodon.guacamole.GuacamoleException;
import org.glyptodon.guacamole.environment.Environment;
import org.glyptodon.guacamole.net.GuacamoleTunnel;
import org.glyptodon.guacamole.net.auth.Credentials;
import org.glyptodon.guacamole.net.auth.AuthenticatedUser;
import org.glyptodon.guacamole.net.auth.UserContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -46,9 +46,9 @@ public class GuacamoleSession {
private static final Logger logger = LoggerFactory.getLogger(GuacamoleSession.class);
/**
* The credentials provided when the user authenticated.
* The user associated with this session.
*/
private Credentials credentials;
private AuthenticatedUser authenticatedUser;
/**
* The user context associated with this session.
@@ -72,8 +72,8 @@ public class GuacamoleSession {
* The environment of the Guacamole server associated with this new
* session.
*
* @param credentials
* The credentials provided by the user during login.
* @param authenticatedUser
* The authenticated user to associate this session with.
*
* @param userContext
* The user context to associate this session with.
@@ -81,36 +81,35 @@ public class GuacamoleSession {
* @throws GuacamoleException
* If an error prevents the session from being created.
*/
public GuacamoleSession(Environment environment, Credentials credentials,
UserContext userContext) throws GuacamoleException {
public GuacamoleSession(Environment environment,
AuthenticatedUser authenticatedUser, UserContext userContext)
throws GuacamoleException {
this.lastAccessedTime = System.currentTimeMillis();
this.credentials = credentials;
this.authenticatedUser = authenticatedUser;
this.userContext = userContext;
}
/**
* Returns the credentials used when the user associated with this session
* authenticated.
* Returns the authenticated user associated with this session.
*
* @return
* The credentials used when the user associated with this session
* authenticated.
* The authenticated user associated with this session.
*/
public Credentials getCredentials() {
return credentials;
public AuthenticatedUser getAuthenticatedUser() {
return authenticatedUser;
}
/**
* Replaces the credentials associated with this session with the given
* credentials.
* Replaces the authenticated user associated with this session with the
* given authenticated user.
*
* @param credentials
* The credentials to associate with this session.
* @param authenticatedUser
* The authenticated user to associated with this session.
*/
public void setCredentials(Credentials credentials) {
this.credentials = credentials;
public void setAuthenticatedUser(AuthenticatedUser authenticatedUser) {
this.authenticatedUser = authenticatedUser;
}
/**
* Returns the UserContext associated with this session.
*

View File

@@ -24,6 +24,7 @@ package org.glyptodon.guacamole.net.basic.extension;
import java.lang.reflect.InvocationTargetException;
import org.glyptodon.guacamole.GuacamoleException;
import org.glyptodon.guacamole.net.auth.AuthenticatedUser;
import org.glyptodon.guacamole.net.auth.AuthenticationProvider;
import org.glyptodon.guacamole.net.auth.Credentials;
import org.glyptodon.guacamole.net.auth.UserContext;
@@ -118,7 +119,7 @@ public class AuthenticationProviderFacade implements AuthenticationProvider {
}
@Override
public UserContext getUserContext(Credentials credentials)
public AuthenticatedUser authenticateUser(Credentials credentials)
throws GuacamoleException {
// Ignore auth attempts if no auth provider could be loaded
@@ -128,13 +129,13 @@ public class AuthenticationProviderFacade implements AuthenticationProvider {
}
// Delegate to underlying auth provider
return authProvider.getUserContext(credentials);
return authProvider.authenticateUser(credentials);
}
@Override
public UserContext updateUserContext(UserContext context, Credentials credentials)
throws GuacamoleException {
public AuthenticatedUser updateAuthenticatedUser(AuthenticatedUser authenticatedUser,
Credentials credentials) throws GuacamoleException {
// Ignore auth attempts if no auth provider could be loaded
if (authProvider == null) {
@@ -143,7 +144,38 @@ public class AuthenticationProviderFacade implements AuthenticationProvider {
}
// Delegate to underlying auth provider
return authProvider.updateUserContext(context, credentials);
return authProvider.updateAuthenticatedUser(authenticatedUser, credentials);
}
@Override
public UserContext getUserContext(AuthenticatedUser authenticatedUser)
throws GuacamoleException {
// Ignore auth attempts if no auth provider could be loaded
if (authProvider == null) {
logger.warn("User data retrieval attempt denied because the authentication system could not be loaded. Please check for errors earlier in the logs.");
return null;
}
// Delegate to underlying auth provider
return authProvider.getUserContext(authenticatedUser);
}
@Override
public UserContext updateUserContext(UserContext context,
AuthenticatedUser authenticatedUser)
throws GuacamoleException {
// Ignore auth attempts if no auth provider could be loaded
if (authProvider == null) {
logger.warn("User data refresh attempt denied because the authentication system could not be loaded. Please check for errors earlier in the logs.");
return null;
}
// Delegate to underlying auth provider
return authProvider.updateUserContext(context, authenticatedUser);
}

View File

@@ -38,6 +38,7 @@ import javax.ws.rs.core.MultivaluedMap;
import javax.xml.bind.DatatypeConverter;
import org.glyptodon.guacamole.GuacamoleException;
import org.glyptodon.guacamole.environment.Environment;
import org.glyptodon.guacamole.net.auth.AuthenticatedUser;
import org.glyptodon.guacamole.net.auth.AuthenticationProvider;
import org.glyptodon.guacamole.net.auth.Credentials;
import org.glyptodon.guacamole.net.auth.UserContext;
@@ -222,28 +223,28 @@ public class TokenRESTService {
credentials.setPassword(password);
credentials.setRequest(request);
credentials.setSession(request.getSession(true));
UserContext userContext;
AuthenticatedUser authenticatedUser;
try {
// Update existing user context if session already exists
// Re-authenticate user if session exists
if (existingSession != null)
userContext = authProvider.updateUserContext(existingSession.getUserContext(), credentials);
authenticatedUser = authProvider.updateAuthenticatedUser(existingSession.getAuthenticatedUser(), credentials);
/// Otherwise, generate a new user context
/// Otherwise, authenticate as a new user
else {
userContext = authProvider.getUserContext(credentials);
authenticatedUser = authProvider.authenticateUser(credentials);
// Log successful authentication
if (userContext != null && logger.isInfoEnabled())
if (authenticatedUser != null && logger.isInfoEnabled())
logger.info("User \"{}\" successfully authenticated from {}.",
userContext.self().getIdentifier(), getLoggableAddress(request));
authenticatedUser.getIdentifier(), getLoggableAddress(request));
}
// Request standard username/password if no user context was produced
if (userContext == null)
// Request standard username/password if no user was produced
if (authenticatedUser == null)
throw new GuacamoleInvalidCredentialsException("Permission Denied.",
CredentialsInfo.USERNAME_PASSWORD);
@@ -264,23 +265,35 @@ public class TokenRESTService {
throw e;
}
// Generate or update user context
UserContext userContext;
if (existingSession != null)
userContext = authProvider.updateUserContext(existingSession.getUserContext(), authenticatedUser);
else
userContext = authProvider.getUserContext(authenticatedUser);
// STUB: Request standard username/password if no user context was produced
if (userContext == null)
throw new GuacamoleInvalidCredentialsException("Permission Denied.",
CredentialsInfo.USERNAME_PASSWORD);
// Update existing session, if it exists
String authToken;
if (existingSession != null) {
authToken = token;
existingSession.setCredentials(credentials);
existingSession.setAuthenticatedUser(authenticatedUser);
existingSession.setUserContext(userContext);
}
// If no existing session, generate a new token/session pair
else {
authToken = authTokenGenerator.getToken();
tokenSessionMap.put(authToken, new GuacamoleSession(environment, credentials, userContext));
tokenSessionMap.put(authToken, new GuacamoleSession(environment, authenticatedUser, userContext));
}
logger.debug("Login was successful for user \"{}\".", userContext.self().getIdentifier());
return new APIAuthToken(authToken, userContext.self().getIdentifier());
logger.debug("Login was successful for user \"{}\".", authenticatedUser.getIdentifier());
return new APIAuthToken(authToken, authenticatedUser.getIdentifier());
}

View File

@@ -46,6 +46,7 @@ import org.glyptodon.guacamole.net.auth.Credentials;
import org.glyptodon.guacamole.net.auth.Directory;
import org.glyptodon.guacamole.net.auth.User;
import org.glyptodon.guacamole.net.auth.UserContext;
import org.glyptodon.guacamole.net.auth.credentials.GuacamoleCredentialsException;
import org.glyptodon.guacamole.net.auth.permission.ObjectPermission;
import org.glyptodon.guacamole.net.auth.permission.ObjectPermissionSet;
import org.glyptodon.guacamole.net.auth.permission.Permission;
@@ -337,8 +338,16 @@ public class UserRESTService {
credentials.setRequest(request);
credentials.setSession(request.getSession(true));
// Verify that the old password was correct
if (authProvider.getUserContext(credentials) == null) {
// Verify that the old password was correct
try {
if (authProvider.authenticateUser(credentials) == null) {
throw new APIException(APIError.Type.PERMISSION_DENIED,
"Permission denied.");
}
}
// Pass through any credentials exceptions as simple permission denied
catch (GuacamoleCredentialsException e) {
throw new APIException(APIError.Type.PERMISSION_DENIED,
"Permission denied.");
}