diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledAuthenticatedUser.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledAuthenticatedUser.java index 8c201d004..f51d08677 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledAuthenticatedUser.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledAuthenticatedUser.java @@ -79,6 +79,7 @@ public class ModeledAuthenticatedUser extends RemoteAuthenticatedUser { super(authenticatedUser.getAuthenticationProvider(), authenticatedUser.getCredentials()); this.modelAuthenticationProvider = modelAuthenticationProvider; this.user = user; + super.setAttributes(authenticatedUser.getAttributes()); } /** @@ -93,7 +94,7 @@ public class ModeledAuthenticatedUser extends RemoteAuthenticatedUser { * A ModeledUser object which is backed by the data associated with * this user in the database. * - * @param credentials + * @param credentials * The credentials given by the user when they authenticated. */ public ModeledAuthenticatedUser(AuthenticationProvider authenticationProvider, @@ -107,7 +108,7 @@ public class ModeledAuthenticatedUser extends RemoteAuthenticatedUser { * Returns a ModeledUser object which is backed by the data associated with * this user within the database. * - * @return + * @return * A ModeledUser object which is backed by the data associated with * this user in the database. */ diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/RemoteAuthenticatedUser.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/RemoteAuthenticatedUser.java index 24118af45..3b16a992b 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/RemoteAuthenticatedUser.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/RemoteAuthenticatedUser.java @@ -19,6 +19,8 @@ package org.apache.guacamole.auth.jdbc.user; +import java.util.HashMap; +import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.servlet.http.HttpServletRequest; @@ -67,6 +69,21 @@ public abstract class RemoteAuthenticatedUser implements AuthenticatedUser { */ private static final Pattern X_FORWARDED_FOR = Pattern.compile("^" + IP_ADDRESS_REGEX + "(, " + IP_ADDRESS_REGEX + ")*$"); + /** + * Arbitrary attributes associated with this RemoteAuthenticatedUser object. + */ + private Map attributes = new HashMap(); + + @Override + public Map getAttributes() { + return attributes; + } + + @Override + public void setAttributes(Map attributes) { + this.attributes = attributes; + } + /** * Derives the remote host of the authenticating user from the given * credentials object. The remote host is derived from X-Forwarded-For @@ -98,7 +115,7 @@ public abstract class RemoteAuthenticatedUser implements AuthenticatedUser { return request.getRemoteAddr(); } - + /** * Creates a new RemoteAuthenticatedUser, deriving the associated remote * host from the given credentials. @@ -106,7 +123,7 @@ public abstract class RemoteAuthenticatedUser implements AuthenticatedUser { * @param authenticationProvider * The AuthenticationProvider that has authenticated the given user. * - * @param credentials + * @param credentials * The credentials given by the user when they authenticated. */ public RemoteAuthenticatedUser(AuthenticationProvider authenticationProvider, diff --git a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/AuthenticationProviderService.java b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/AuthenticationProviderService.java index a25c697e6..547b71db5 100644 --- a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/AuthenticationProviderService.java +++ b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/AuthenticationProviderService.java @@ -21,11 +21,18 @@ package org.apache.guacamole.auth.ldap; import com.google.inject.Inject; import com.google.inject.Provider; +import com.novell.ldap.LDAPAttribute; +import com.novell.ldap.LDAPAttributeSet; import com.novell.ldap.LDAPConnection; +import com.novell.ldap.LDAPEntry; +import com.novell.ldap.LDAPException; +import java.util.HashMap; import java.util.List; +import java.util.Map; import org.apache.guacamole.auth.ldap.user.AuthenticatedUser; import org.apache.guacamole.auth.ldap.user.UserContext; import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.GuacamoleServerException; import org.apache.guacamole.auth.ldap.user.UserService; import org.apache.guacamole.net.auth.Credentials; import org.apache.guacamole.net.auth.credentials.CredentialsInfo; @@ -189,7 +196,8 @@ public class AuthenticationProviderService { /** * Returns an AuthenticatedUser representing the user authenticated by the - * given credentials. + * given credentials. Also adds custom LDAP attributes to the + * AuthenticatedUser. * * @param credentials * The credentials to use for authentication. @@ -221,14 +229,16 @@ public class AuthenticationProviderService { throw new GuacamoleInvalidCredentialsException("Permission denied.", CredentialsInfo.USERNAME_PASSWORD); try { - // Return AuthenticatedUser if bind succeeds AuthenticatedUser authenticatedUser = authenticatedUserProvider.get(); authenticatedUser.init(credentials); + + // Set attributes + authenticatedUser.setAttributes(getLDAPAttributes(ldapConnection, credentials.getUsername())); + return authenticatedUser; } - // Always disconnect finally { ldapService.disconnect(ldapConnection); @@ -236,6 +246,58 @@ public class AuthenticationProviderService { } + /** + * Returns all custom LDAP attributes on the user currently bound under + * the given LDAP connection. The custom attributes are specified in + * guacamole.properties. + * + * @param ldapConnection + * LDAP connection to find the custom LDAP attributes. + * + * @param username + * The username of the user whose attributes are queried. + * + * @return + * All attributes on the user currently bound under the + * given LDAP connection, as a map of attribute name to + * corresponding attribute value. + * + * @throws GuacamoleException + * If an error occurs retrieving the user DN or the attributes. + */ + private Map getLDAPAttributes(LDAPConnection ldapConnection, + String username) throws GuacamoleException { + + // Get attributes from configuration information + List attrList = confService.getAttributes(); + + // If there are no attributes there is no reason to search LDAP + if (attrList == null || attrList.isEmpty()) + return null; + + // Build LDAP query parameters + String[] attrArray = attrList.toArray(new String[attrList.size()]); + String userDN = getUserBindDN(username); + + Map attrMap = new HashMap(); + try { + // Get LDAP attributes by querying LDAP + LDAPEntry userEntry = ldapConnection.read(userDN, attrArray); + LDAPAttributeSet attrSet = userEntry.getAttributeSet(); + + // Add each attribute into Map + for (Object attrObj : attrSet) { + LDAPAttribute attr = (LDAPAttribute)attrObj; + attrMap.put(attr.getName(), attr.getStringValue()); + } + } + catch (LDAPException e) { + throw new GuacamoleServerException("Error while querying for User Attributes.", e); + } + + return attrMap; + } + /** * Returns a UserContext object initialized with data accessible to the * given AuthenticatedUser. diff --git a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/ConfigurationService.java b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/ConfigurationService.java index 2ab7aadf6..de7c71ce2 100644 --- a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/ConfigurationService.java +++ b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/ConfigurationService.java @@ -227,7 +227,7 @@ public class ConfigurationService { private int getMaxResults() throws GuacamoleException { return environment.getProperty( LDAPGuacamoleProperties.LDAP_MAX_SEARCH_RESULTS, - 1000 + 1000 ); } @@ -344,4 +344,19 @@ public class ConfigurationService { ); } + /** + * Returns names for custom LDAP user attributes. + * + * @return + * Custom LDAP user attributes as configured in guacamole.properties. + * + * @throws GuacamoleException + * If guacamole.properties cannot be parsed. + */ + public List getAttributes() throws GuacamoleException { + return environment.getProperty( + LDAPGuacamoleProperties.LDAP_USER_ATTRIBUTES + ); + } + } diff --git a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/LDAPGuacamoleProperties.java b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/LDAPGuacamoleProperties.java index 0d3823fed..6372d81e0 100644 --- a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/LDAPGuacamoleProperties.java +++ b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/LDAPGuacamoleProperties.java @@ -205,4 +205,14 @@ public class LDAPGuacamoleProperties { }; + /** + * Custom attribute or attributes to query from Guacamole user's record in + * the LDAP directory. + */ + public static final StringListProperty LDAP_USER_ATTRIBUTES = new StringListProperty() { + + @Override + public String getName() { return "ldap-user-attributes"; } + + }; } diff --git a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/connection/ConnectionService.java b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/connection/ConnectionService.java index 3ce00e3f2..a282f3036 100644 --- a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/connection/ConnectionService.java +++ b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/connection/ConnectionService.java @@ -295,4 +295,3 @@ public class ConnectionService { } } - diff --git a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/user/AuthenticatedUser.java b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/user/AuthenticatedUser.java index 669efcd54..8e9926753 100644 --- a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/user/AuthenticatedUser.java +++ b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/user/AuthenticatedUser.java @@ -20,6 +20,8 @@ package org.apache.guacamole.auth.ldap.user; import com.google.inject.Inject; +import java.util.HashMap; +import java.util.Map; import org.apache.guacamole.net.auth.AbstractAuthenticatedUser; import org.apache.guacamole.net.auth.AuthenticationProvider; import org.apache.guacamole.net.auth.Credentials; @@ -42,6 +44,11 @@ public class AuthenticatedUser extends AbstractAuthenticatedUser { */ private Credentials credentials; + /** + * Arbitrary attributes associated with this AuthenticatedUser object. + */ + private Map attributes = new HashMap(); + /** * Initializes this AuthenticatedUser using the given credentials. * @@ -53,6 +60,16 @@ public class AuthenticatedUser extends AbstractAuthenticatedUser { setIdentifier(credentials.getUsername()); } + @Override + public Map getAttributes() { + return attributes; + } + + @Override + public void setAttributes(Map attributes) { + this.attributes = attributes; + } + @Override public AuthenticationProvider getAuthenticationProvider() { return authProvider; diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AbstractAuthenticatedUser.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AbstractAuthenticatedUser.java index 36c4571e0..828ad8904 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AbstractAuthenticatedUser.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AbstractAuthenticatedUser.java @@ -20,6 +20,7 @@ package org.apache.guacamole.net.auth; import java.util.Collections; +import java.util.Map; import java.util.Set; /** @@ -41,4 +42,14 @@ public abstract class AbstractAuthenticatedUser extends AbstractIdentifiable // Nothing to invalidate } + @Override + public Map getAttributes() { + return Collections.emptyMap(); + } + + @Override + public void setAttributes(Map attributes) { + //do nothing + } + } diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AuthenticatedUser.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AuthenticatedUser.java index a799937e8..14f25978a 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AuthenticatedUser.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AuthenticatedUser.java @@ -25,7 +25,7 @@ import java.util.Set; * A user of the Guacamole web application who has been authenticated by an * AuthenticationProvider. */ -public interface AuthenticatedUser extends Identifiable { +public interface AuthenticatedUser extends Identifiable, Attributes { /** * The identifier reserved for representing a user that has authenticated diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/token/StandardTokens.java b/guacamole-ext/src/main/java/org/apache/guacamole/token/StandardTokens.java index b1b280bee..8faca158f 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/token/StandardTokens.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/token/StandardTokens.java @@ -21,6 +21,8 @@ package org.apache.guacamole.token; import java.text.SimpleDateFormat; 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.Credentials; @@ -74,6 +76,11 @@ public class StandardTokens { */ 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. */ @@ -143,10 +150,11 @@ public class StandardTokens { * Adds tokens which are standardized by guacamole-ext to the given * TokenFilter using the values from the given AuthenticatedUser object, * including any associated credentials. These standardized tokens include - * the current username (GUAC_USERNAME), password (GUAC_PASSWORD), and the - * server date and time (GUAC_DATE and GUAC_TIME respectively). If either - * the username or password are not set within the given user or their - * provided credentials, the corresponding token(s) will remain unset. + * the current username (GUAC_USERNAME), password (GUAC_PASSWORD), the + * server date and time (GUAC_DATE and GUAC_TIME respectively), and custom + * user attributes. If either the username or password are not set within + * the given user or their provided credentials, the corresponding token(s) + * will remain unset. * * @param filter * The TokenFilter to add standard tokens to. @@ -164,6 +172,33 @@ public class StandardTokens { // Add tokens specific to credentials 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 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); + } + } + } }