GUACAMOLE-524: Remove Attributes interface from AuthenticatedUser. Rely instead on tokens injected via decoration of connections.

This commit is contained in:
Michael Jumper
2018-10-04 23:37:16 -07:00
parent 0d7cff5f2d
commit 98bd3ead21
11 changed files with 82 additions and 115 deletions

View File

@@ -79,7 +79,6 @@ public class ModeledAuthenticatedUser extends RemoteAuthenticatedUser {
super(authenticatedUser.getAuthenticationProvider(), authenticatedUser.getCredentials(), authenticatedUser.getEffectiveUserGroups()); super(authenticatedUser.getAuthenticationProvider(), authenticatedUser.getCredentials(), authenticatedUser.getEffectiveUserGroups());
this.modelAuthenticationProvider = modelAuthenticationProvider; this.modelAuthenticationProvider = modelAuthenticationProvider;
this.user = user; this.user = user;
super.setAttributes(authenticatedUser.getAttributes());
} }
/** /**

View File

@@ -19,7 +19,6 @@
package org.apache.guacamole.auth.jdbc.user; package org.apache.guacamole.auth.jdbc.user;
import java.util.Map;
import java.util.Collections; import java.util.Collections;
import java.util.Set; import java.util.Set;
import org.apache.guacamole.net.auth.AuthenticatedUser; import org.apache.guacamole.net.auth.AuthenticatedUser;
@@ -52,16 +51,6 @@ public abstract class RemoteAuthenticatedUser implements AuthenticatedUser {
*/ */
private final Set<String> effectiveGroups; private final Set<String> effectiveGroups;
@Override
public Map<String, String> getAttributes() {
return Collections.<String, String>emptyMap();
}
@Override
public void setAttributes(Map<String, String> attributes) {
// No attributes supported
}
/** /**
* Creates a new RemoteAuthenticatedUser, deriving the associated remote * Creates a new RemoteAuthenticatedUser, deriving the associated remote
* host from the given credentials. * host from the given credentials.

View File

@@ -53,6 +53,12 @@ public class AuthenticationProviderService {
*/ */
private final Logger logger = LoggerFactory.getLogger(AuthenticationProviderService.class); private final Logger logger = LoggerFactory.getLogger(AuthenticationProviderService.class);
/**
* The prefix string to add to each parameter token generated from an LDAP
* attribute name.
*/
private static final String LDAP_ATTRIBUTE_TOKEN_PREFIX = "LDAP_ATTR_";
/** /**
* Service for creating and managing connections to LDAP servers. * Service for creating and managing connections to LDAP servers.
*/ */
@@ -233,7 +239,7 @@ public class AuthenticationProviderService {
try { try {
// Return AuthenticatedUser if bind succeeds // Return AuthenticatedUser if bind succeeds
LDAPAuthenticatedUser authenticatedUser = authenticatedUserProvider.get(); LDAPAuthenticatedUser authenticatedUser = authenticatedUserProvider.get();
authenticatedUser.init(credentials, getLDAPAttributes(ldapConnection, credentials.getUsername())); authenticatedUser.init(credentials, getAttributeTokens(ldapConnection, credentials.getUsername()));
return authenticatedUser; return authenticatedUser;
@@ -246,43 +252,44 @@ public class AuthenticationProviderService {
} }
/** /**
* Returns all custom LDAP attributes on the user currently bound under * Returns parameter tokens generated from LDAP attributes on the user
* the given LDAP connection. The custom attributes are specified in * currently bound under the given LDAP connection. The attributes to be
* guacamole.properties. If no attributes are specified or none are * converted into parameter tokens must be explicitly listed in
* guacamole.properties. If no attributes are specified or none are
* found on the LDAP user object, an empty map is returned. * found on the LDAP user object, an empty map is returned.
* *
* @param ldapConnection * @param ldapConnection
* LDAP connection to find the custom LDAP attributes. * LDAP connection to use to read the attributes of the user.
* *
* @param username * @param username
* The username of the user whose attributes are queried. * The username of the user whose attributes are to be queried.
* *
* @return * @return
* All attributes on the user currently bound under the * A map of parameter tokens generated from attributes on the user
* given LDAP connection, as a map of attribute name to * currently bound under the given LDAP connection, as a map of token
* corresponding attribute value, or an empty map if no * name to corresponding value, or an empty map if no attributes are
* attributes are specified or none are found on the user * specified or none are found on the user object.
* object.
* *
* @throws GuacamoleException * @throws GuacamoleException
* If an error occurs retrieving the user DN or the attributes. * If an error occurs retrieving the user DN or the attributes.
*/ */
private Map<String, String> getLDAPAttributes(LDAPConnection ldapConnection, private Map<String, String> getAttributeTokens(LDAPConnection ldapConnection,
String username) throws GuacamoleException { String username) throws GuacamoleException {
// Get attributes from configuration information // Get attributes from configuration information
List<String> attrList = confService.getAttributes(); List<String> attrList = confService.getAttributes();
// If there are no attributes there is no reason to search LDAP // If there are no attributes there is no reason to search LDAP
if (attrList == null || attrList.isEmpty()) if (attrList.isEmpty())
return Collections.<String, String>emptyMap(); return Collections.<String, String>emptyMap();
// Build LDAP query parameters // Build LDAP query parameters
String[] attrArray = attrList.toArray(new String[attrList.size()]); String[] attrArray = attrList.toArray(new String[attrList.size()]);
String userDN = getUserBindDN(username); String userDN = getUserBindDN(username);
Map<String, String> attrMap = new HashMap<String, String>(); Map<String, String> tokens = new HashMap<String, String>();
try { try {
// Get LDAP attributes by querying LDAP // Get LDAP attributes by querying LDAP
LDAPEntry userEntry = ldapConnection.read(userDN, attrArray); LDAPEntry userEntry = ldapConnection.read(userDN, attrArray);
if (userEntry == null) if (userEntry == null)
@@ -292,17 +299,19 @@ public class AuthenticationProviderService {
if (attrSet == null) if (attrSet == null)
return Collections.<String, String>emptyMap(); return Collections.<String, String>emptyMap();
// Add each attribute into Map // Convert each retrieved attribute into a corresponding token
for (Object attrObj : attrSet) { for (Object attrObj : attrSet) {
LDAPAttribute attr = (LDAPAttribute)attrObj; LDAPAttribute attr = (LDAPAttribute)attrObj;
attrMap.put(attr.getName(), attr.getStringValue()); tokens.put(LDAP_ATTRIBUTE_TOKEN_PREFIX + attr.getName(), attr.getStringValue());
} }
} }
catch (LDAPException e) { catch (LDAPException e) {
throw new GuacamoleServerException("Error while querying for User Attributes.", e); throw new GuacamoleServerException("Could not query LDAP user attributes.", e);
} }
return attrMap; return tokens;
} }
/** /**

View File

@@ -355,7 +355,8 @@ public class ConfigurationService {
*/ */
public List<String> getAttributes() throws GuacamoleException { public List<String> getAttributes() throws GuacamoleException {
return environment.getProperty( return environment.getProperty(
LDAPGuacamoleProperties.LDAP_USER_ATTRIBUTES LDAPGuacamoleProperties.LDAP_USER_ATTRIBUTES,
Collections.<String>emptyList()
); );
} }

View File

@@ -23,9 +23,11 @@ package org.apache.guacamole.auth.ldap;
import com.google.inject.Guice; import com.google.inject.Guice;
import com.google.inject.Injector; import com.google.inject.Injector;
import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.ldap.user.LDAPAuthenticatedUser;
import org.apache.guacamole.net.auth.AbstractAuthenticationProvider; import org.apache.guacamole.net.auth.AbstractAuthenticationProvider;
import org.apache.guacamole.net.auth.AuthenticatedUser; import org.apache.guacamole.net.auth.AuthenticatedUser;
import org.apache.guacamole.net.auth.Credentials; import org.apache.guacamole.net.auth.Credentials;
import org.apache.guacamole.net.auth.TokenInjectingUserContext;
import org.apache.guacamole.net.auth.UserContext; import org.apache.guacamole.net.auth.UserContext;
/** /**
@@ -85,4 +87,19 @@ public class LDAPAuthenticationProvider extends AbstractAuthenticationProvider {
} }
@Override
public UserContext decorate(UserContext context,
AuthenticatedUser authenticatedUser, Credentials credentials)
throws GuacamoleException {
// Only decorate if the user authenticated against LDAP
if (!(authenticatedUser instanceof LDAPAuthenticatedUser))
return context;
// Apply LDAP-specific tokens to all connections / connection groups
return new TokenInjectingUserContext(context,
((LDAPAuthenticatedUser) authenticatedUser).getTokens());
}
} }

View File

@@ -35,8 +35,10 @@ import org.apache.guacamole.auth.ldap.ConfigurationService;
import org.apache.guacamole.auth.ldap.EscapingService; import org.apache.guacamole.auth.ldap.EscapingService;
import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.GuacamoleServerException; import org.apache.guacamole.GuacamoleServerException;
import org.apache.guacamole.auth.ldap.user.LDAPAuthenticatedUser;
import org.apache.guacamole.net.auth.AuthenticatedUser; import org.apache.guacamole.net.auth.AuthenticatedUser;
import org.apache.guacamole.net.auth.Connection; import org.apache.guacamole.net.auth.Connection;
import org.apache.guacamole.net.auth.TokenInjectingConnection;
import org.apache.guacamole.net.auth.simple.SimpleConnection; import org.apache.guacamole.net.auth.simple.SimpleConnection;
import org.apache.guacamole.protocol.GuacamoleConfiguration; import org.apache.guacamole.protocol.GuacamoleConfiguration;
import org.slf4j.Logger; import org.slf4j.Logger;
@@ -178,6 +180,13 @@ public class ConnectionService {
String name = cn.getStringValue(); String name = cn.getStringValue();
Connection connection = new SimpleConnection(name, name, config); Connection connection = new SimpleConnection(name, name, config);
connection.setParentIdentifier(LDAPAuthenticationProvider.ROOT_CONNECTION_GROUP); connection.setParentIdentifier(LDAPAuthenticationProvider.ROOT_CONNECTION_GROUP);
// Inject LDAP-specific tokens only if LDAP handled user
// authentication
if (user instanceof LDAPAuthenticatedUser)
connection = new TokenInjectingConnection(connection,
((LDAPAuthenticatedUser) user).getTokens());
connections.put(name, connection); connections.put(name, connection);
} }

View File

@@ -20,6 +20,7 @@
package org.apache.guacamole.auth.ldap.user; package org.apache.guacamole.auth.ldap.user;
import com.google.inject.Inject; import com.google.inject.Inject;
import java.util.Collections;
import java.util.Map; import java.util.Map;
import org.apache.guacamole.net.auth.AbstractAuthenticatedUser; import org.apache.guacamole.net.auth.AbstractAuthenticatedUser;
import org.apache.guacamole.net.auth.AuthenticationProvider; import org.apache.guacamole.net.auth.AuthenticationProvider;
@@ -44,35 +45,40 @@ public class LDAPAuthenticatedUser extends AbstractAuthenticatedUser {
private Credentials credentials; private Credentials credentials;
/** /**
* Arbitrary attributes associated with this AuthenticatedUser object. * Name/value pairs to be applied as parameter tokens when connections
* are established using this AuthenticatedUser.
*/ */
private Map<String, String> attributes; private Map<String, String> tokens;
/** /**
* Initializes this AuthenticatedUser using the given credentials and * Initializes this AuthenticatedUser using the given credentials and
* arbitrary attributes. * connection parameter tokens.
* *
* @param credentials * @param credentials
* The credentials provided when this user was authenticated. * The credentials provided when this user was authenticated.
* *
* @param attributes * @param tokens
* The map of arbitrary attribute name/value pairs to associate with * A Map of all name/value pairs that should be applied as parameter
* this AuthenticatedUser. * tokens when connections are established using the AuthenticatedUser.
*/ */
public void init(Credentials credentials, Map<String, String> attributes) { public void init(Credentials credentials, Map<String, String> tokens) {
this.credentials = credentials; this.credentials = credentials;
this.attributes = attributes; this.tokens = Collections.unmodifiableMap(tokens);
setIdentifier(credentials.getUsername()); setIdentifier(credentials.getUsername());
} }
@Override /**
public Map<String, String> getAttributes() { * Returns a Map of all name/value pairs that should be applied as
return attributes; * parameter tokens when connections are established using this
} * AuthenticatedUser.
*
@Override * @return
public void setAttributes(Map<String, String> attributes) { * A Map of all name/value pairs that should be applied as parameter
// All attributes are read-only * tokens when connections are established using this
* AuthenticatedUser.
*/
public Map<String, String> getTokens() {
return tokens;
} }
@Override @Override

View File

@@ -20,7 +20,6 @@
package org.apache.guacamole.net.auth; package org.apache.guacamole.net.auth;
import java.util.Collections; import java.util.Collections;
import java.util.Map;
import java.util.Set; import java.util.Set;
/** /**
@@ -42,14 +41,4 @@ public abstract class AbstractAuthenticatedUser extends AbstractIdentifiable
// Nothing to invalidate // Nothing to invalidate
} }
@Override
public Map<String, String> getAttributes() {
return Collections.<String, String>emptyMap();
}
@Override
public void setAttributes(Map<String, String> attributes) {
//do nothing
}
} }

View File

@@ -25,7 +25,7 @@ import java.util.Set;
* A user of the Guacamole web application who has been authenticated by an * A user of the Guacamole web application who has been authenticated by an
* AuthenticationProvider. * AuthenticationProvider.
*/ */
public interface AuthenticatedUser extends Identifiable, Attributes { public interface AuthenticatedUser extends Identifiable {
/** /**
* The identifier reserved for representing a user that has authenticated * The identifier reserved for representing a user that has authenticated

View File

@@ -21,8 +21,6 @@ package org.apache.guacamole.token;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Date; import java.util.Date;
import java.util.Map;
import java.util.Set;
import org.apache.guacamole.net.auth.AuthenticatedUser; import org.apache.guacamole.net.auth.AuthenticatedUser;
import org.apache.guacamole.net.auth.Credentials; import org.apache.guacamole.net.auth.Credentials;
@@ -81,11 +79,6 @@ public class StandardTokens {
*/ */
private static final String TIME_FORMAT = "HHmmss"; private static final String TIME_FORMAT = "HHmmss";
/**
* The prefix of the arbitrary attribute tokens.
*/
public static final String ATTR_TOKEN_PREFIX = "GUAC_ATTR_";
/** /**
* This utility class should not be instantiated. * This utility class should not be instantiated.
*/ */
@@ -155,11 +148,10 @@ public class StandardTokens {
* Adds tokens which are standardized by guacamole-ext to the given * Adds tokens which are standardized by guacamole-ext to the given
* TokenFilter using the values from the given AuthenticatedUser object, * TokenFilter using the values from the given AuthenticatedUser object,
* including any associated credentials. These standardized tokens include * including any associated credentials. These standardized tokens include
* the current username (GUAC_USERNAME), password (GUAC_PASSWORD), the * the current username (GUAC_USERNAME), password (GUAC_PASSWORD), and the
* server date and time (GUAC_DATE and GUAC_TIME respectively), and custom * server date and time (GUAC_DATE and GUAC_TIME respectively). If either
* user attributes. If either the username or password are not set within * the username or password are not set within the given user or their
* the given user or their provided credentials, the corresponding token(s) * provided credentials, the corresponding token(s) will remain unset.
* will remain unset.
* *
* @param filter * @param filter
* The TokenFilter to add standard tokens to. * The TokenFilter to add standard tokens to.
@@ -177,33 +169,6 @@ public class StandardTokens {
// Add tokens specific to credentials // Add tokens specific to credentials
addStandardTokens(filter, user.getCredentials()); addStandardTokens(filter, user.getCredentials());
// Add custom attribute tokens
addAttributeTokens(filter, user.getAttributes());
}
/**
* Add attribute tokens to StandardTokens. These are arbitrary
* key/value pairs that may be configured by the various authentication
* extensions.
*
* @param filter
* The TokenFilter to add attribute tokens to.
*
* @param attributes
* The map of key/value pairs to add tokens for.
*/
public static void addAttributeTokens(TokenFilter filter,
Map<String, String> attributes) {
if (attributes != null) {
for (Map.Entry entry : attributes.entrySet()) {
String key = entry.getKey().toString();
String tokenName = ATTR_TOKEN_PREFIX + key.toUpperCase();
String tokenValue = entry.getValue().toString();
filter.setToken(tokenName, tokenValue);
}
}
} }
} }

View File

@@ -22,7 +22,6 @@ package org.apache.guacamole.tunnel;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map;
import org.apache.guacamole.net.auth.AuthenticatedUser; import org.apache.guacamole.net.auth.AuthenticatedUser;
import org.apache.guacamole.net.auth.Credentials; import org.apache.guacamole.net.auth.Credentials;
@@ -76,11 +75,6 @@ public class StandardTokenMap extends HashMap<String, String> {
*/ */
private static final String TIME_FORMAT = "HHmmss"; private static final String TIME_FORMAT = "HHmmss";
/**
* The prefix of the arbitrary attribute tokens.
*/
public static final String ATTR_TOKEN_PREFIX = "GUAC_ATTR_";
/** /**
* Creates a new StandardTokenMap which is pre-populated with the * Creates a new StandardTokenMap which is pre-populated with the
* name/value pairs of all standardized tokens available for the given * name/value pairs of all standardized tokens available for the given
@@ -97,7 +91,6 @@ public class StandardTokenMap extends HashMap<String, String> {
put(TIME_TOKEN, new SimpleDateFormat(TIME_FORMAT).format(currentTime)); put(TIME_TOKEN, new SimpleDateFormat(TIME_FORMAT).format(currentTime));
Credentials credentials = authenticatedUser.getCredentials(); Credentials credentials = authenticatedUser.getCredentials();
Map<String, String> attributes = authenticatedUser.getAttributes();
// Add username token // Add username token
String username = credentials.getUsername(); String username = credentials.getUsername();
@@ -124,16 +117,6 @@ public class StandardTokenMap extends HashMap<String, String> {
if (address != null) if (address != null)
put(CLIENT_ADDRESS_TOKEN, address); put(CLIENT_ADDRESS_TOKEN, address);
// Add tokens for all attributes on the AuthenticatedUser
if (attributes != null) {
for (Map.Entry entry : attributes.entrySet()) {
String key = entry.getKey().toString();
String tokenName = ATTR_TOKEN_PREFIX + key.toUpperCase();
String tokenValue = entry.getValue().toString();
put(tokenName, tokenValue);
}
}
} }
} }