diff --git a/extensions/guacamole-auth-header/src/main/java/org/apache/guacamole/auth/header/ConfigurationService.java b/extensions/guacamole-auth-header/src/main/java/org/apache/guacamole/auth/header/ConfigurationService.java index 2ab1350dc..d9f49813c 100644 --- a/extensions/guacamole-auth-header/src/main/java/org/apache/guacamole/auth/header/ConfigurationService.java +++ b/extensions/guacamole-auth-header/src/main/java/org/apache/guacamole/auth/header/ConfigurationService.java @@ -53,5 +53,26 @@ public class ConfigurationService { "REMOTE_USER" ); } + + /** + * Returns true if the usernames provided to the header authentication + * module should be treated as case-sensitive, or false if usernames + * should be treated as case-insensitive. This will default to the global + * Guacamole configuration for case-sensitivity, which defaults to true, but + * can be overridden for this extension, if desired. + * + * @return + * true if usernames should be treated as case-sensitive, otherwise + * false. + * + * @throws GuacamoleException + * If guacamole.properties cannot be parsed. + */ + public boolean getCaseSensitiveUsernames() throws GuacamoleException { + return environment.getProperty( + HTTPHeaderGuacamoleProperties.HTTP_AUTH_CASE_SENSITIVE_USERNAMES, + environment.getCaseSensitiveUsernames() + ); + } } diff --git a/extensions/guacamole-auth-header/src/main/java/org/apache/guacamole/auth/header/HTTPHeaderGuacamoleProperties.java b/extensions/guacamole-auth-header/src/main/java/org/apache/guacamole/auth/header/HTTPHeaderGuacamoleProperties.java index 733a1f8cd..acf514a75 100644 --- a/extensions/guacamole-auth-header/src/main/java/org/apache/guacamole/auth/header/HTTPHeaderGuacamoleProperties.java +++ b/extensions/guacamole-auth-header/src/main/java/org/apache/guacamole/auth/header/HTTPHeaderGuacamoleProperties.java @@ -19,7 +19,7 @@ package org.apache.guacamole.auth.header; -import org.apache.guacamole.properties.IntegerGuacamoleProperty; +import org.apache.guacamole.properties.BooleanGuacamoleProperty; import org.apache.guacamole.properties.StringGuacamoleProperty; @@ -36,7 +36,7 @@ public class HTTPHeaderGuacamoleProperties { private HTTPHeaderGuacamoleProperties() {} /** - * The header used for HTTP header authentication. + * A property used to configure the header used for HTTP header authentication. */ public static final StringGuacamoleProperty HTTP_AUTH_HEADER = new StringGuacamoleProperty() { @@ -44,5 +44,17 @@ public class HTTPHeaderGuacamoleProperties { public String getName() { return "http-auth-header"; } }; + + /** + * A property used to configure whether or not usernames within the header + * module should be treated as case-sensitive. + */ + public static final BooleanGuacamoleProperty HTTP_AUTH_CASE_SENSITIVE_USERNAMES = + new BooleanGuacamoleProperty() { + + @Override + public String getName() { return "http-auth-case-sensitive-usernames"; } + + }; } diff --git a/extensions/guacamole-auth-header/src/main/java/org/apache/guacamole/auth/header/user/AuthenticatedUser.java b/extensions/guacamole-auth-header/src/main/java/org/apache/guacamole/auth/header/user/AuthenticatedUser.java index 793fef01e..d498c5c4d 100644 --- a/extensions/guacamole-auth-header/src/main/java/org/apache/guacamole/auth/header/user/AuthenticatedUser.java +++ b/extensions/guacamole-auth-header/src/main/java/org/apache/guacamole/auth/header/user/AuthenticatedUser.java @@ -20,9 +20,13 @@ package org.apache.guacamole.auth.header.user; import com.google.inject.Inject; +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.auth.header.ConfigurationService; import org.apache.guacamole.net.auth.AbstractAuthenticatedUser; import org.apache.guacamole.net.auth.AuthenticationProvider; import org.apache.guacamole.net.auth.Credentials; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * An HTTP header implementation of AuthenticatedUser, associating a @@ -31,12 +35,23 @@ import org.apache.guacamole.net.auth.Credentials; */ public class AuthenticatedUser extends AbstractAuthenticatedUser { + /** + * Logger for this class. + */ + private static final Logger LOGGER = LoggerFactory.getLogger(AuthenticatedUser.class); + /** * Reference to the authentication provider associated with this * authenticated user. */ @Inject private AuthenticationProvider authProvider; + + /** + * Service for retrieving header configuration information. + */ + @Inject + private ConfigurationService confService; /** * The credentials provided when this user was authenticated. @@ -58,6 +73,19 @@ public class AuthenticatedUser extends AbstractAuthenticatedUser { setIdentifier(username.toLowerCase()); } + @Override + public boolean isCaseSensitive() { + try { + return confService.getCaseSensitiveUsernames(); + } + catch (GuacamoleException e) { + LOGGER.error("Error when trying to retrieve header configuration: {}." + + " Usernames comparison will be case-sensitive.", e); + LOGGER.debug("Exception caught when retrieving header configuration.", e); + return true; + } + } + @Override public AuthenticationProvider getAuthenticationProvider() { return authProvider; diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/JDBCEnvironment.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/JDBCEnvironment.java index 27f3d0563..dcc754451 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/JDBCEnvironment.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/JDBCEnvironment.java @@ -271,5 +271,18 @@ public abstract class JDBCEnvironment extends DelegatingEnvironment { return true; } + + /** + * Returns a boolean value that indicates whether or not usernames should + * be treated as case-sensitive. + * + * @return + * true if usernames should be treated as case-sensitive, or false if + * usernames should be treated as case-insensitive. + * + * @throws GuacamoleException + * If guacamole.properties cannot be parsed. + */ + public abstract boolean getCaseSensitiveUsernames() throws GuacamoleException; } 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 7ede92c6c..6c5ca16f4 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 @@ -194,5 +194,10 @@ public class ModeledAuthenticatedUser extends RemoteAuthenticatedUser { public boolean isPrivileged() throws GuacamoleException { return getUser().isPrivileged(); } + + @Override + public boolean isCaseSensitive() { + return user.isCaseSensitive(); + } } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUser.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUser.java index 811ccaa31..4ac0b95dc 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUser.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUser.java @@ -36,6 +36,7 @@ import java.util.TimeZone; import org.apache.guacamole.auth.jdbc.security.PasswordEncryptionService; import org.apache.guacamole.auth.jdbc.security.SaltService; import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.auth.jdbc.JDBCEnvironment; import org.apache.guacamole.auth.jdbc.base.ModeledPermissions; import org.apache.guacamole.form.BooleanField; import org.apache.guacamole.form.DateField; @@ -180,6 +181,13 @@ public class ModeledUser extends ModeledPermissions implements User { */ @Inject private Provider userRecordSetProvider; + + /** + * The environment associated with this instance of the JDBC authentication + * module. + */ + @Inject + private JDBCEnvironment environment; /** * Whether attributes which control access restrictions should be exposed @@ -780,5 +788,15 @@ public class ModeledUser extends ModeledPermissions implements User { public boolean isSkeleton() { return (getModel().getEntityID() == null); } + + @Override + public boolean isCaseSensitive() { + try { + return environment.getCaseSensitiveUsernames(); + } + catch (GuacamoleException e) { + return true; + } + } } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/org/apache/guacamole/auth/mysql/conf/MySQLEnvironment.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/org/apache/guacamole/auth/mysql/conf/MySQLEnvironment.java index a3dea8964..c5c142ef3 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/org/apache/guacamole/auth/mysql/conf/MySQLEnvironment.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/org/apache/guacamole/auth/mysql/conf/MySQLEnvironment.java @@ -439,7 +439,18 @@ public class MySQLEnvironment extends JDBCEnvironment { // Enforce access window restrictions for active sessions unless explicitly disabled return getProperty( MySQLGuacamoleProperties.MYSQL_ENFORCE_ACCESS_WINDOWS_FOR_ACTIVE_SESSIONS, - true); + true + ); + } + + @Override + public boolean getCaseSensitiveUsernames() throws GuacamoleException { + + return getProperty( + MySQLGuacamoleProperties.MYSQL_CASE_SENSITIVE_USERNAMES, + false + ); + } } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/org/apache/guacamole/auth/mysql/conf/MySQLGuacamoleProperties.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/org/apache/guacamole/auth/mysql/conf/MySQLGuacamoleProperties.java index 5e20b52c6..d2b80987a 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/org/apache/guacamole/auth/mysql/conf/MySQLGuacamoleProperties.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/org/apache/guacamole/auth/mysql/conf/MySQLGuacamoleProperties.java @@ -301,6 +301,14 @@ public class MySQLGuacamoleProperties { @Override public String getName() { return "mysql-batch-size"; } - }; + }; + + public static final BooleanGuacamoleProperty MYSQL_CASE_SENSITIVE_USERNAMES = + new BooleanGuacamoleProperty() { + + @Override + public String getName() { return "mysql-case-sensitive-usernames"; } + + }; } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/apache/guacamole/auth/postgresql/conf/PostgreSQLEnvironment.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/apache/guacamole/auth/postgresql/conf/PostgreSQLEnvironment.java index a0e166aa6..0955ab13e 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/apache/guacamole/auth/postgresql/conf/PostgreSQLEnvironment.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/apache/guacamole/auth/postgresql/conf/PostgreSQLEnvironment.java @@ -398,5 +398,17 @@ public class PostgreSQLEnvironment extends JDBCEnvironment { PostgreSQLGuacamoleProperties.POSTGRESQL_ENFORCE_ACCESS_WINDOWS_FOR_ACTIVE_SESSIONS, true); } + + @Override + public boolean getCaseSensitiveUsernames() throws GuacamoleException { + + // By default, PostgreSQL does use case-sensitive string searches, so + // we will honor case-sensitive usernames. + return getProperty( + PostgreSQLGuacamoleProperties.POSTGRESQL_CASE_SENSITIVE_USERNAMES, + true + ); + + } } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/apache/guacamole/auth/postgresql/conf/PostgreSQLGuacamoleProperties.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/apache/guacamole/auth/postgresql/conf/PostgreSQLGuacamoleProperties.java index 707b83ca1..c3f64a154 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/apache/guacamole/auth/postgresql/conf/PostgreSQLGuacamoleProperties.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/apache/guacamole/auth/postgresql/conf/PostgreSQLGuacamoleProperties.java @@ -314,5 +314,17 @@ public class PostgreSQLGuacamoleProperties { public String getName() { return "postgresql-batch-size"; } }; + + /** + * A property that configures whether or not usernames should be treated as + * case-sensitive with the Postgres JDBC backend. + */ + public static final BooleanGuacamoleProperty POSTGRESQL_CASE_SENSITIVE_USERNAMES = + new BooleanGuacamoleProperty() { + + @Override + public String getName() { return "postgresql-case-sensitive-usernames"; } + + }; } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/java/org/apache/guacamole/auth/sqlserver/conf/SQLServerEnvironment.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/java/org/apache/guacamole/auth/sqlserver/conf/SQLServerEnvironment.java index 498479d79..5b0f6969b 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/java/org/apache/guacamole/auth/sqlserver/conf/SQLServerEnvironment.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/java/org/apache/guacamole/auth/sqlserver/conf/SQLServerEnvironment.java @@ -328,5 +328,17 @@ public class SQLServerEnvironment extends JDBCEnvironment { SQLServerGuacamoleProperties.SQLSERVER_TRUST_ALL_SERVER_CERTIFICATES, false); } + + @Override + public boolean getCaseSensitiveUsernames() throws GuacamoleException { + + // SQL Server uses case-insensitive string searches by default, so + // we do not enforce case-sensitivity unless otherwise configured. + return getProperty( + SQLServerGuacamoleProperties.SQLSERVER_CASE_SENSITIVE_USERNAMES, + false + ); + + } } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/java/org/apache/guacamole/auth/sqlserver/conf/SQLServerGuacamoleProperties.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/java/org/apache/guacamole/auth/sqlserver/conf/SQLServerGuacamoleProperties.java index c4df81381..d978a5388 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/java/org/apache/guacamole/auth/sqlserver/conf/SQLServerGuacamoleProperties.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/java/org/apache/guacamole/auth/sqlserver/conf/SQLServerGuacamoleProperties.java @@ -257,5 +257,13 @@ public class SQLServerGuacamoleProperties { public String getName() { return "sqlserver-trust-all-server-certificates"; } }; + + public static final BooleanGuacamoleProperty SQLSERVER_CASE_SENSITIVE_USERNAMES = + new BooleanGuacamoleProperty() { + + @Override + public String getName() { return "sqlserver-case-sensitive-usernames" ; } + + }; } diff --git a/extensions/guacamole-auth-json/src/main/java/org/apache/guacamole/auth/json/ConfigurationService.java b/extensions/guacamole-auth-json/src/main/java/org/apache/guacamole/auth/json/ConfigurationService.java index 2705e61ed..6fb2cd403 100644 --- a/extensions/guacamole-auth-json/src/main/java/org/apache/guacamole/auth/json/ConfigurationService.java +++ b/extensions/guacamole-auth-json/src/main/java/org/apache/guacamole/auth/json/ConfigurationService.java @@ -24,6 +24,7 @@ import java.util.Collection; import java.util.Collections; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.environment.Environment; +import org.apache.guacamole.properties.BooleanGuacamoleProperty; import org.apache.guacamole.properties.ByteArrayProperty; import org.apache.guacamole.properties.StringGuacamoleProperty; @@ -39,6 +40,20 @@ public class ConfigurationService { @Inject private Environment environment; + /** + * A property used to configure whether or not usernames within the JSON + * module should be treated as case-sensitive. + */ + private static final BooleanGuacamoleProperty JSON_CASE_SENSITIVE_USERNAMES = + new BooleanGuacamoleProperty() { + + @Override + public String getName() { + return "json-case-sensitive-usernames"; + } + + }; + /** * The encryption key to use for all decryption and signature verification. */ @@ -64,6 +79,25 @@ public class ConfigurationService { } }; + + /** + * Returns true if the usernames provided to the JSON authentication + * module should be treated as case-sensitive, or false if usernames + * should be treated as case-insensitive. The default will be taken from + * the global Guacamole configuration, which defaults to true, but + * can be overridden for this extension. + * + * @return + * true if usernames should be treated as case-sensitive, otherwise + * false. + * + * @throws GuacamoleException + * If guacamole.properties cannot be parsed. + */ + public boolean getCaseSensitiveUsernames() throws GuacamoleException { + return environment.getProperty(JSON_CASE_SENSITIVE_USERNAMES, + environment.getCaseSensitiveUsernames()); + } /** * Returns the symmetric key which will be used to encrypt and sign all diff --git a/extensions/guacamole-auth-json/src/main/java/org/apache/guacamole/auth/json/user/AuthenticatedUser.java b/extensions/guacamole-auth-json/src/main/java/org/apache/guacamole/auth/json/user/AuthenticatedUser.java index 562d6a426..0eeab697a 100644 --- a/extensions/guacamole-auth-json/src/main/java/org/apache/guacamole/auth/json/user/AuthenticatedUser.java +++ b/extensions/guacamole-auth-json/src/main/java/org/apache/guacamole/auth/json/user/AuthenticatedUser.java @@ -20,9 +20,13 @@ package org.apache.guacamole.auth.json.user; import com.google.inject.Inject; +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.auth.json.ConfigurationService; import org.apache.guacamole.net.auth.AbstractAuthenticatedUser; import org.apache.guacamole.net.auth.AuthenticationProvider; import org.apache.guacamole.net.auth.Credentials; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * An implementation of AuthenticatedUser specific to the @@ -31,12 +35,24 @@ import org.apache.guacamole.net.auth.Credentials; */ public class AuthenticatedUser extends AbstractAuthenticatedUser { + /** + * Logger for this class. + */ + private static final Logger LOGGER = LoggerFactory.getLogger(AuthenticatedUser.class); + /** * Reference to the authentication provider associated with this * authenticated user. */ @Inject private AuthenticationProvider authProvider; + + /** + * Reference to the configuration service associated with this + * authentication provider. + */ + @Inject + private ConfigurationService confService; /** * The credentials provided when this user was authenticated. @@ -66,6 +82,19 @@ public class AuthenticatedUser extends AbstractAuthenticatedUser { this.userData = userData; setIdentifier(userData.getUsername()); } + + @Override + public boolean isCaseSensitive() { + try { + return confService.getCaseSensitiveUsernames(); + } + catch (GuacamoleException e) { + LOGGER.error("Error when attempting to get the JSON configuration: {}. " + + "Username comparisons will be case-sensitive.", e.getMessage()); + LOGGER.debug("Exception caught while retrieving JSON configuration.", e); + return true; + } + } @Override public AuthenticationProvider getAuthenticationProvider() { diff --git a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/ConnectedLDAPConfiguration.java b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/ConnectedLDAPConfiguration.java index c41114c02..3f555ce5d 100644 --- a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/ConnectedLDAPConfiguration.java +++ b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/ConnectedLDAPConfiguration.java @@ -223,5 +223,10 @@ public class ConnectedLDAPConfiguration implements LDAPConfiguration, AutoClosea public MemberAttributeType getMemberAttributeType() throws GuacamoleException { return config.getMemberAttributeType(); } + + @Override + public boolean getCaseSensitiveUsernames() throws GuacamoleException { + return config.getCaseSensitiveUsernames(); + } } diff --git a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/DefaultLDAPConfiguration.java b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/DefaultLDAPConfiguration.java index cb2db9b6b..63cd64ab1 100644 --- a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/DefaultLDAPConfiguration.java +++ b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/DefaultLDAPConfiguration.java @@ -19,6 +19,7 @@ package org.apache.guacamole.auth.ldap.conf; +import com.google.inject.Inject; import java.util.Collections; import java.util.List; import org.apache.directory.api.ldap.model.filter.ExprNode; @@ -27,6 +28,7 @@ import org.apache.directory.api.ldap.model.message.AliasDerefMode; import org.apache.directory.api.ldap.model.name.Dn; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleServerException; +import org.apache.guacamole.environment.Environment; /** * LDAPConfiguration implementation that returns the default values for all @@ -35,6 +37,12 @@ import org.apache.guacamole.GuacamoleServerException; */ public class DefaultLDAPConfiguration implements LDAPConfiguration { + /** + * The environment in which Guacamole is running. + */ + @Inject + private Environment environment; + @Override public String appliesTo(String username) { return null; @@ -150,5 +158,10 @@ public class DefaultLDAPConfiguration implements LDAPConfiguration { throws GuacamoleException { return MemberAttributeType.DN; } + + @Override + public boolean getCaseSensitiveUsernames() throws GuacamoleException { + return environment.getCaseSensitiveUsernames(); + } } diff --git a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/EnvironmentLDAPConfiguration.java b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/EnvironmentLDAPConfiguration.java index 5ffeb203b..44a946417 100644 --- a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/EnvironmentLDAPConfiguration.java +++ b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/EnvironmentLDAPConfiguration.java @@ -233,5 +233,19 @@ public class EnvironmentLDAPConfiguration implements LDAPConfiguration { DEFAULT.getMemberAttributeType() ); } + + @Override + public boolean getCaseSensitiveUsernames() throws GuacamoleException { + + // Most LDAP directories do not factor in case when comparing usernames, + // however, in order to avoid surprising anyone who may rely on this + // behavior in Guacamole, this is currently defaulted the overall + // Guacamole configuration (default of true), but can be over-ridden + // for the LDAP extension specifically, if desired. + return environment.getProperty( + LDAPGuacamoleProperties.LDAP_CASE_SENSITIVE_USERNAMES, + environment.getCaseSensitiveUsernames() + ); + } } diff --git a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/JacksonLDAPConfiguration.java b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/JacksonLDAPConfiguration.java index bddccd871..bb9b474ee 100644 --- a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/JacksonLDAPConfiguration.java +++ b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/JacksonLDAPConfiguration.java @@ -203,6 +203,13 @@ public class JacksonLDAPConfiguration implements LDAPConfiguration { */ @JsonProperty("member-attribute-type") private String memberAttributeType; + + /** + * The raw YAML value of {@link LDAPGuacamoleProperties#LDAP_USERNAMES_CASE_SENSITIVE}. + * If not set within the YAML, this will currently default to true. + */ + @JsonProperty("case-sensitive-usernames") + private String caseSensitiveUsernames; /** * The default configuration options for all parameters. @@ -439,5 +446,11 @@ public class JacksonLDAPConfiguration implements LDAPConfiguration { return withDefault(LDAPGuacamoleProperties.LDAP_MEMBER_ATTRIBUTE_TYPE, memberAttributeType, defaultConfig::getMemberAttributeType); } + + @Override + public boolean getCaseSensitiveUsernames() throws GuacamoleException { + return withDefault(LDAPGuacamoleProperties.LDAP_CASE_SENSITIVE_USERNAMES, + caseSensitiveUsernames, defaultConfig::getCaseSensitiveUsernames); + } } diff --git a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/LDAPConfiguration.java b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/LDAPConfiguration.java index e57049b26..31a847566 100644 --- a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/LDAPConfiguration.java +++ b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/LDAPConfiguration.java @@ -20,7 +20,6 @@ package org.apache.guacamole.auth.ldap.conf; import java.util.Collection; -import java.util.List; import org.apache.directory.api.ldap.model.filter.ExprNode; import org.apache.directory.api.ldap.model.message.AliasDerefMode; import org.apache.directory.api.ldap.model.name.Dn; @@ -334,5 +333,21 @@ public interface LDAPConfiguration { * retrieved. */ MemberAttributeType getMemberAttributeType() throws GuacamoleException; + + /** + * Returns true if the usernames provided to the LDAP authentication + * module should be treated as case-sensitive, or false if usernames + * should be treated as case-insensitive. The default is true, usernames + * will be case-sensitive in keeping with the past behavior of Guacamole + * prior to the addition of this option. + * + * @return + * true if usernames should be treated as case-sensitive, otherwise + * false. + * + * @throws GuacamoleException + * If guacamole.properties cannot be parsed. + */ + boolean getCaseSensitiveUsernames() throws GuacamoleException; } diff --git a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/LDAPGuacamoleProperties.java b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/LDAPGuacamoleProperties.java index 7349356b9..072128ecf 100644 --- a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/LDAPGuacamoleProperties.java +++ b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/LDAPGuacamoleProperties.java @@ -306,5 +306,17 @@ public class LDAPGuacamoleProperties { public String getName() { return "ldap-member-attribute-type"; } }; + + /** + * A property used to configure whether or not usernames within the LDAP + * module should be treated as case-sensitive. + */ + public static final BooleanGuacamoleProperty LDAP_CASE_SENSITIVE_USERNAMES = + new BooleanGuacamoleProperty() { + + @Override + public String getName() { return "ldap-case-sensitive-usernames"; } + + }; } diff --git a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/user/LDAPAuthenticatedUser.java b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/user/LDAPAuthenticatedUser.java index 2abe4aef0..f934afd57 100644 --- a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/user/LDAPAuthenticatedUser.java +++ b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/user/LDAPAuthenticatedUser.java @@ -24,10 +24,13 @@ import java.util.Collections; import java.util.Map; import java.util.Set; import org.apache.directory.api.ldap.model.name.Dn; +import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.auth.ldap.ConnectedLDAPConfiguration; import org.apache.guacamole.net.auth.AbstractAuthenticatedUser; import org.apache.guacamole.net.auth.AuthenticationProvider; import org.apache.guacamole.net.auth.Credentials; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * An LDAP-specific implementation of AuthenticatedUser, associating a @@ -35,6 +38,11 @@ import org.apache.guacamole.net.auth.Credentials; */ public class LDAPAuthenticatedUser extends AbstractAuthenticatedUser { + /** + * The logger for this class. + */ + private static final Logger LOGGER = LoggerFactory.getLogger(LDAPAuthenticatedUser.class); + /** * Reference to the authentication provider associated with this * authenticated user. @@ -135,6 +143,23 @@ public class LDAPAuthenticatedUser extends AbstractAuthenticatedUser { return config; } + @Override + public boolean isCaseSensitive() { + try { + return config.getCaseSensitiveUsernames(); + } + catch (GuacamoleException e) { + // LDAP authentication is almost universally case-insensitive, + // however, we're maintaining case-sensitivity within Guacamole + // at the moment in order to avoid surprising anyone with this change. + // Case-sensitivity can be disabled as a configuration option. + LOGGER.error("Error retrieving configuration for username case-sensitivity: {}. " + + "Username comparisons will be done case-sensitively.", e.getMessage()); + LOGGER.debug("Caught exception when retrieving case-sensitivity configuration.", e); + return true; + } + } + @Override public AuthenticationProvider getAuthenticationProvider() { return authProvider; diff --git a/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/conf/ConfigurationService.java b/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/conf/ConfigurationService.java index 78738ed53..614464142 100644 --- a/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/conf/ConfigurationService.java +++ b/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/conf/ConfigurationService.java @@ -362,5 +362,26 @@ public class ConfigurationService { throw new GuacamoleServerException("Unknown host specified for NAS IP.", e); } } + + /** + * Returns true if the usernames provided to the RADIUS authentication + * module should be treated as case-sensitive, or false if usernames + * should be treated as case-insensitive. The default value is read from + * Guacamole's global configuration, which defaults to true, but can be + * overridden for the RADIUS extension, if desired. + * + * @return + * true if usernames should be treated as case-sensitive, otherwise + * false. + * + * @throws GuacamoleException + * If guacamole.properties cannot be parsed. + */ + public boolean getCaseSensitiveUsernames() throws GuacamoleException { + return environment.getProperty( + RadiusGuacamoleProperties.RADIUS_CASE_SENSITIVE_USERNAMES, + environment.getCaseSensitiveUsernames() + ); + } } diff --git a/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/conf/RadiusGuacamoleProperties.java b/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/conf/RadiusGuacamoleProperties.java index 06e186f23..459dc5859 100644 --- a/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/conf/RadiusGuacamoleProperties.java +++ b/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/conf/RadiusGuacamoleProperties.java @@ -204,6 +204,18 @@ public class RadiusGuacamoleProperties { public String getName() { return "radius-nas-ip"; } }; + + /** + * A property used to configure whether or not usernames within the RADIUS + * module should be treated as case-sensitive. + */ + public static final BooleanGuacamoleProperty RADIUS_CASE_SENSITIVE_USERNAMES = + new BooleanGuacamoleProperty() { + + @Override + public String getName() { return "radius-case-sensitive-usernames"; } + + }; } diff --git a/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/user/AuthenticatedUser.java b/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/user/AuthenticatedUser.java index a42925357..ddedb860d 100644 --- a/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/user/AuthenticatedUser.java +++ b/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/user/AuthenticatedUser.java @@ -23,6 +23,8 @@ import com.google.inject.Inject; import org.apache.guacamole.net.auth.AbstractAuthenticatedUser; import org.apache.guacamole.net.auth.AuthenticationProvider; import org.apache.guacamole.net.auth.Credentials; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * An RADIUS-specific implementation of AuthenticatedUser, associating a @@ -30,12 +32,23 @@ import org.apache.guacamole.net.auth.Credentials; */ public class AuthenticatedUser extends AbstractAuthenticatedUser { + /** + * Logger for this class. + */ + private static final Logger LOGGER = LoggerFactory.getLogger(AuthenticatedUser.class); + /** * Reference to the authentication provider associated with this * authenticated user. */ @Inject private AuthenticationProvider authProvider; + + /** + * A reference to the configuration service associated with this module. + */ + @Inject + private ConfigurationService confService; /** * The credentials provided when this user was authenticated. @@ -62,5 +75,18 @@ public class AuthenticatedUser extends AbstractAuthenticatedUser { public Credentials getCredentials() { return credentials; } + + @Override + public boolean isCaseSensitive() { + try { + return confService.getCaseSensitiveUsernames(); + } + catch (GuacamoleException e) { + LOGGER.error("Error retrieving configuration for username case sensiivity. " + + "Usernames will be processed as case-sensitive."); + LOGGER.debug("Exception caught while retrieving RADIUS configuration.", e); + return true; + } + } } diff --git a/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-base/src/main/java/org/apache/guacamole/auth/sso/conf/SSOEnvironment.java b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-base/src/main/java/org/apache/guacamole/auth/sso/conf/SSOEnvironment.java new file mode 100644 index 000000000..b9fe0258d --- /dev/null +++ b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-base/src/main/java/org/apache/guacamole/auth/sso/conf/SSOEnvironment.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.guacamole.auth.sso.conf; + +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.environment.DelegatingEnvironment; +import org.apache.guacamole.environment.LocalEnvironment; + +/** + * An SSO-specific environment that defines generic properties that can be used + * with any of the implemented SSO providers. + */ +public abstract class SSOEnvironment extends DelegatingEnvironment { + + /** + * Create a new instance of the SSOEnvironment using the underlying + * LocalEnvironment to read configured properties. + */ + public SSOEnvironment() { + super(LocalEnvironment.getInstance()); + } + + /** + * Returns true if the usernames provided to the SSO authentication + * module should be treated as case-sensitive, or false if usernames + * should be treated as case-insensitive. The default is true, usernames + * will be case-sensitive in keeping with the past behavior of Guacamole + * prior to the addition of this option. + * + * @return + * true if usernames should be treated as case-sensitive, otherwise + * false. + * + * @throws GuacamoleException + * If guacamole.properties cannot be parsed. + */ + public boolean getCaseSensitiveUsernames() throws GuacamoleException { + + // While most SSO systems do not use case to differentiate between + // usernames, this currently defaults to true to avoid suddenly + // breaking any extensions that rely on case-sensitivity. + return true; + } + +} diff --git a/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-base/src/main/java/org/apache/guacamole/auth/sso/user/SSOAuthenticatedUser.java b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-base/src/main/java/org/apache/guacamole/auth/sso/user/SSOAuthenticatedUser.java index 1e46f6d25..5f3e5d07a 100644 --- a/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-base/src/main/java/org/apache/guacamole/auth/sso/user/SSOAuthenticatedUser.java +++ b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-base/src/main/java/org/apache/guacamole/auth/sso/user/SSOAuthenticatedUser.java @@ -23,9 +23,13 @@ import com.google.inject.Inject; import java.util.Collections; import java.util.Map; import java.util.Set; +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.environment.Environment; import org.apache.guacamole.net.auth.AbstractAuthenticatedUser; import org.apache.guacamole.net.auth.AuthenticationProvider; import org.apache.guacamole.net.auth.Credentials; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * An AuthenticatedUser whose identity has been supplied by an arbitrary SSO @@ -35,12 +39,23 @@ import org.apache.guacamole.net.auth.Credentials; */ public class SSOAuthenticatedUser extends AbstractAuthenticatedUser { + /** + * Logger for this class. + */ + private static final Logger LOGGER = LoggerFactory.getLogger(SSOAuthenticatedUser.class); + /** * Reference to the authentication provider associated with this * authenticated user. */ @Inject private AuthenticationProvider authProvider; + + /** + * The environment in which this instance of Guacamole is running. + */ + @Inject + private Environment environment; /** * The credentials provided when this user was authenticated. @@ -112,5 +127,22 @@ public class SSOAuthenticatedUser extends AbstractAuthenticatedUser { public Set getEffectiveUserGroups() { return effectiveGroups; } + + @Override + public boolean isCaseSensitive() { + try { + return environment.getCaseSensitiveUsernames(); + } + catch (GuacamoleException e) { + // Most SSO systems do not consider usernames to be case-sensitive; + // however, in order to avoid any surprises created by the introduction + // of case-sensitivity, we've opted to continue to evaluate these + // usernames in a case-sensitive manner by default. + LOGGER.error("Error occurred when trying to retrieve case-sensitivity configuration: {}. " + + "Usernames comparisons will be done in a case-sensitive manner.", e.getMessage()); + LOGGER.debug("Exception caught when trying to access the case-sensitivity property.", e); + return true; + } + } } diff --git a/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-cas/src/main/java/org/apache/guacamole/auth/cas/CASAuthenticationProviderModule.java b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-cas/src/main/java/org/apache/guacamole/auth/cas/CASAuthenticationProviderModule.java index 0cfeacd42..a9568b6bb 100644 --- a/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-cas/src/main/java/org/apache/guacamole/auth/cas/CASAuthenticationProviderModule.java +++ b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-cas/src/main/java/org/apache/guacamole/auth/cas/CASAuthenticationProviderModule.java @@ -20,18 +20,28 @@ package org.apache.guacamole.auth.cas; import com.google.inject.AbstractModule; +import org.apache.guacamole.auth.cas.conf.CASEnvironment; import org.apache.guacamole.auth.cas.conf.ConfigurationService; import org.apache.guacamole.auth.cas.ticket.TicketValidationService; +import org.apache.guacamole.environment.Environment; /** * Guice module which configures CAS-specific injections. */ public class CASAuthenticationProviderModule extends AbstractModule { + /** + * The configuration environment for this server and extension. + */ + private final Environment environment = new CASEnvironment(); + @Override protected void configure() { bind(ConfigurationService.class); bind(TicketValidationService.class); + + bind(Environment.class).toInstance(environment); + } } diff --git a/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-cas/src/main/java/org/apache/guacamole/auth/cas/conf/CASEnvironment.java b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-cas/src/main/java/org/apache/guacamole/auth/cas/conf/CASEnvironment.java new file mode 100644 index 000000000..45973f3fc --- /dev/null +++ b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-cas/src/main/java/org/apache/guacamole/auth/cas/conf/CASEnvironment.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.guacamole.auth.cas.conf; + +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.environment.DelegatingEnvironment; +import org.apache.guacamole.environment.LocalEnvironment; + +/** + * An environment for retrieving CAS-related properties from the Guacamole + * configuration. + */ +public class CASEnvironment extends DelegatingEnvironment { + + /** + * Create a new instance of the configuration environment for the + * CAS SSO module, pulling the default instance of the LocalEnvironment. + */ + public CASEnvironment() { + super(LocalEnvironment.getInstance()); + } + + @Override + public boolean getCaseSensitiveUsernames() throws GuacamoleException { + + // While most SSO systems do not consider usernames case-sensitive, + // this defaults to the global Guacamole configuration, which defaults + // to true, in order to avoid surprising or breaking environments that + // may rely on this behavior. This can be overridden for the entire + // Guacamole instance or for this extension. + return getProperty(CASGuacamoleProperties.CAS_CASE_SENSITIVE_USERNAMES, + super.getCaseSensitiveUsernames()); + + } + +} diff --git a/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-cas/src/main/java/org/apache/guacamole/auth/cas/conf/CASGuacamoleProperties.java b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-cas/src/main/java/org/apache/guacamole/auth/cas/conf/CASGuacamoleProperties.java index 7bb363f9c..cb40b9740 100644 --- a/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-cas/src/main/java/org/apache/guacamole/auth/cas/conf/CASGuacamoleProperties.java +++ b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-cas/src/main/java/org/apache/guacamole/auth/cas/conf/CASGuacamoleProperties.java @@ -20,6 +20,7 @@ package org.apache.guacamole.auth.cas.conf; import org.apache.guacamole.auth.cas.group.GroupFormat; +import org.apache.guacamole.properties.BooleanGuacamoleProperty; import org.apache.guacamole.properties.EnumGuacamoleProperty; import org.apache.guacamole.properties.URIGuacamoleProperty; import org.apache.guacamole.properties.StringGuacamoleProperty; @@ -117,5 +118,17 @@ public class CASGuacamoleProperties { public String getName() { return "cas-group-ldap-attribute"; } }; + + /** + * A property used to configure whether or not usernames within the CAS SSO + * module should be treated as case-sensitive. + */ + public static final BooleanGuacamoleProperty CAS_CASE_SENSITIVE_USERNAMES = + new BooleanGuacamoleProperty() { + + @Override + public String getName() { return "cas-case-sensitive-usernames"; } + + }; } diff --git a/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-openid/src/main/java/org/apache/guacamole/auth/openid/OpenIDAuthenticationProviderModule.java b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-openid/src/main/java/org/apache/guacamole/auth/openid/OpenIDAuthenticationProviderModule.java index 2fce2a719..6dc45f7f9 100644 --- a/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-openid/src/main/java/org/apache/guacamole/auth/openid/OpenIDAuthenticationProviderModule.java +++ b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-openid/src/main/java/org/apache/guacamole/auth/openid/OpenIDAuthenticationProviderModule.java @@ -22,19 +22,28 @@ package org.apache.guacamole.auth.openid; import com.google.inject.AbstractModule; import com.google.inject.Scopes; import org.apache.guacamole.auth.openid.conf.ConfigurationService; +import org.apache.guacamole.auth.openid.conf.OpenIDEnvironment; import org.apache.guacamole.auth.sso.NonceService; import org.apache.guacamole.auth.openid.token.TokenValidationService; +import org.apache.guacamole.environment.Environment; /** * Guice module which configures OpenID-specific injections. */ public class OpenIDAuthenticationProviderModule extends AbstractModule { + /** + * The configuration environment for this server and extension. + */ + private final Environment environment = new OpenIDEnvironment(); + @Override protected void configure() { bind(ConfigurationService.class); bind(NonceService.class).in(Scopes.SINGLETON); bind(TokenValidationService.class); + + bind(Environment.class).toInstance(environment); } } diff --git a/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-openid/src/main/java/org/apache/guacamole/auth/openid/conf/ConfigurationService.java b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-openid/src/main/java/org/apache/guacamole/auth/openid/conf/ConfigurationService.java index 444bd7706..58204277a 100644 --- a/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-openid/src/main/java/org/apache/guacamole/auth/openid/conf/ConfigurationService.java +++ b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-openid/src/main/java/org/apache/guacamole/auth/openid/conf/ConfigurationService.java @@ -26,6 +26,7 @@ import java.util.Collections; import java.util.List; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.environment.Environment; +import org.apache.guacamole.properties.BooleanGuacamoleProperty; import org.apache.guacamole.properties.IntegerGuacamoleProperty; import org.apache.guacamole.properties.StringGuacamoleProperty; import org.apache.guacamole.properties.URIGuacamoleProperty; @@ -217,7 +218,19 @@ public class ConfigurationService { @Override public String getName() { return "openid-redirect-uri"; } - + + }; + + /** + * A property used to configure whether or not usernames within the OpenID + * SSO module should be treated as case-sensitive. + */ + public static final BooleanGuacamoleProperty OPENID_CASE_SENSITIVE_USERNAMES = + new BooleanGuacamoleProperty() { + + @Override + public String getName() { return "openid-case-sensitive-usernames"; } + }; /** diff --git a/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-openid/src/main/java/org/apache/guacamole/auth/openid/conf/OpenIDEnvironment.java b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-openid/src/main/java/org/apache/guacamole/auth/openid/conf/OpenIDEnvironment.java new file mode 100644 index 000000000..a8ea4d081 --- /dev/null +++ b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-openid/src/main/java/org/apache/guacamole/auth/openid/conf/OpenIDEnvironment.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.guacamole.auth.openid.conf; + +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.environment.DelegatingEnvironment; +import org.apache.guacamole.environment.LocalEnvironment; + +/** + * An environment for retrieving OpenID-related properties from the Guacamole + * configuration. + */ +public class OpenIDEnvironment extends DelegatingEnvironment { + + /** + * Create a new instance of the configuration environment for the + * OpenID SSO module, pulling the default instance of the LocalEnvironment. + */ + public OpenIDEnvironment() { + super(LocalEnvironment.getInstance()); + } + + @Override + public boolean getCaseSensitiveUsernames() throws GuacamoleException { + + // While most SSO systems do not consider usernames case-sensitive, + // this defaults to the global Guacamole configuration, which defaults + // to true, in order to avoid surprising or breaking environments that + // may rely on this behavior. This can be overridden for the entire + // Guacamole instance or for this extension. + return getProperty(ConfigurationService.OPENID_CASE_SENSITIVE_USERNAMES, + super.getCaseSensitiveUsernames()); + + } + +} diff --git a/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-saml/src/main/java/org/apache/guacamole/auth/saml/SAMLAuthenticationProviderModule.java b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-saml/src/main/java/org/apache/guacamole/auth/saml/SAMLAuthenticationProviderModule.java index e01880a62..a223392fa 100644 --- a/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-saml/src/main/java/org/apache/guacamole/auth/saml/SAMLAuthenticationProviderModule.java +++ b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-saml/src/main/java/org/apache/guacamole/auth/saml/SAMLAuthenticationProviderModule.java @@ -24,12 +24,19 @@ import org.apache.guacamole.auth.saml.conf.ConfigurationService; import org.apache.guacamole.auth.saml.acs.AssertionConsumerServiceResource; import org.apache.guacamole.auth.saml.acs.SAMLAuthenticationSessionManager; import org.apache.guacamole.auth.saml.acs.SAMLService; +import org.apache.guacamole.auth.saml.conf.SAMLEnvironment; +import org.apache.guacamole.environment.Environment; /** * Guice module which configures SAML-specific injections. */ public class SAMLAuthenticationProviderModule extends AbstractModule { + /** + * The environment for this server and extension. + */ + private final Environment environment = new SAMLEnvironment(); + @Override protected void configure() { bind(AssertionConsumerServiceResource.class); @@ -37,6 +44,8 @@ public class SAMLAuthenticationProviderModule extends AbstractModule { bind(SAMLAuthenticationSessionManager.class); bind(SAMLService.class); + bind(Environment.class).toInstance(environment); + requestStaticInjection(SAMLAuthenticationEventListener.class); } diff --git a/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-saml/src/main/java/org/apache/guacamole/auth/saml/conf/ConfigurationService.java b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-saml/src/main/java/org/apache/guacamole/auth/saml/conf/ConfigurationService.java index 47ead8820..8419658ec 100644 --- a/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-saml/src/main/java/org/apache/guacamole/auth/saml/conf/ConfigurationService.java +++ b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-saml/src/main/java/org/apache/guacamole/auth/saml/conf/ConfigurationService.java @@ -189,6 +189,18 @@ public class ConfigurationService { public String getName() { return "saml-private-key-path"; } }; + + /** + * A property used to configure whether or not usernames within the SAML SSO + * module should be treated as case-sensitive. + */ + public static final BooleanGuacamoleProperty SAML_CASE_SENSITIVE_USERNAMES = + new BooleanGuacamoleProperty() { + + @Override + public String getName() { return "saml-case-sensitive-usernames"; } + + }; /** * The Guacamole server environment. diff --git a/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-saml/src/main/java/org/apache/guacamole/auth/saml/conf/SAMLEnvironment.java b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-saml/src/main/java/org/apache/guacamole/auth/saml/conf/SAMLEnvironment.java new file mode 100644 index 000000000..b294db528 --- /dev/null +++ b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-saml/src/main/java/org/apache/guacamole/auth/saml/conf/SAMLEnvironment.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.guacamole.auth.saml.conf; + +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.environment.DelegatingEnvironment; +import org.apache.guacamole.environment.LocalEnvironment; + +/** + * An environment for retrieving SAML-related properties from the Guacamole + * configuration. + */ +public class SAMLEnvironment extends DelegatingEnvironment { + + /** + * Create a new instance of the configuration environment for the + * SAML SSO module, pulling the default instance of the LocalEnvironment. + */ + public SAMLEnvironment() { + super(LocalEnvironment.getInstance()); + } + + @Override + public boolean getCaseSensitiveUsernames() throws GuacamoleException { + + // While most SSO systems do not consider usernames case-sensitive, + // this defaults to the global Guacamole configuration, which defaults + // to true, in order to avoid surprising or breaking environments that + // may rely on this behavior. This can be overridden for the entire + // Guacamole instance or for this extension. + return getProperty(ConfigurationService.SAML_CASE_SENSITIVE_USERNAMES, + super.getCaseSensitiveUsernames()); + + } + +} diff --git a/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-ssl/src/main/java/org/apache/guacamole/auth/ssl/SSLAuthenticationProviderModule.java b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-ssl/src/main/java/org/apache/guacamole/auth/ssl/SSLAuthenticationProviderModule.java index 9f7b15aff..bcf7368fb 100644 --- a/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-ssl/src/main/java/org/apache/guacamole/auth/ssl/SSLAuthenticationProviderModule.java +++ b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-ssl/src/main/java/org/apache/guacamole/auth/ssl/SSLAuthenticationProviderModule.java @@ -22,7 +22,9 @@ package org.apache.guacamole.auth.ssl; import com.google.inject.AbstractModule; import com.google.inject.Scopes; import org.apache.guacamole.auth.ssl.conf.ConfigurationService; +import org.apache.guacamole.auth.ssl.conf.SSLEnvironment; import org.apache.guacamole.auth.sso.NonceService; +import org.apache.guacamole.environment.Environment; /** * Guice module which configures injections specific to SSO using SSL/TLS @@ -30,12 +32,19 @@ import org.apache.guacamole.auth.sso.NonceService; */ public class SSLAuthenticationProviderModule extends AbstractModule { + /** + * The configuration environment of this server and extension. + */ + private final Environment environment = new SSLEnvironment(); + @Override protected void configure() { bind(ConfigurationService.class); bind(NonceService.class).in(Scopes.SINGLETON); bind(SSLAuthenticationSessionManager.class); + bind(Environment.class).toInstance(environment); + requestStaticInjection(SSLAuthenticationEventListener.class); } diff --git a/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-ssl/src/main/java/org/apache/guacamole/auth/ssl/conf/ConfigurationService.java b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-ssl/src/main/java/org/apache/guacamole/auth/ssl/conf/ConfigurationService.java index 7931c0dad..9a165cbcc 100644 --- a/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-ssl/src/main/java/org/apache/guacamole/auth/ssl/conf/ConfigurationService.java +++ b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-ssl/src/main/java/org/apache/guacamole/auth/ssl/conf/ConfigurationService.java @@ -28,6 +28,7 @@ import javax.ws.rs.core.UriBuilder; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleServerException; import org.apache.guacamole.environment.Environment; +import org.apache.guacamole.properties.BooleanGuacamoleProperty; import org.apache.guacamole.properties.IntegerGuacamoleProperty; import org.apache.guacamole.properties.StringGuacamoleProperty; import org.apache.guacamole.properties.URIGuacamoleProperty; @@ -186,6 +187,18 @@ public class ConfigurationService { public String getName() { return "ssl-max-domain-validity"; } }; + + /** + * A property used to configure whether or not usernames within the SSL SSO + * module should be treated as case-sensitive. + */ + public static final BooleanGuacamoleProperty SSL_CASE_SENSITIVE_USERNAMES = + new BooleanGuacamoleProperty() { + + @Override + public String getName() { return "ssl-case-sensitive-usernames"; } + + }; /** * The Guacamole server environment. diff --git a/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-ssl/src/main/java/org/apache/guacamole/auth/ssl/conf/SSLEnvironment.java b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-ssl/src/main/java/org/apache/guacamole/auth/ssl/conf/SSLEnvironment.java new file mode 100644 index 000000000..bf544dd0d --- /dev/null +++ b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-ssl/src/main/java/org/apache/guacamole/auth/ssl/conf/SSLEnvironment.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.guacamole.auth.ssl.conf; + +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.environment.DelegatingEnvironment; +import org.apache.guacamole.environment.LocalEnvironment; + +/** + * An environment for retrieving SSL-related properties from the Guacamole + * configuration. + */ +public class SSLEnvironment extends DelegatingEnvironment { + + /** + * Create a new instance of the configuration environment for the + * SSL SSO module, pulling the default instance of the LocalEnvironment. + */ + public SSLEnvironment() { + super(LocalEnvironment.getInstance()); + } + + @Override + public boolean getCaseSensitiveUsernames() throws GuacamoleException { + + // While most SSO systems do not consider usernames case-sensitive, + // this defaults to the global Guacamole configuration, which defaults + // to true, in order to avoid surprising or breaking environments that + // may rely on this behavior. This can be overridden for the entire + // Guacamole instance or for this extension. + return getProperty(ConfigurationService.SSL_CASE_SENSITIVE_USERNAMES, + super.getCaseSensitiveUsernames()); + + } + +} diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/environment/DelegatingEnvironment.java b/guacamole-ext/src/main/java/org/apache/guacamole/environment/DelegatingEnvironment.java index 936612a21..5dc3266c4 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/environment/DelegatingEnvironment.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/environment/DelegatingEnvironment.java @@ -113,5 +113,10 @@ public class DelegatingEnvironment implements Environment { public void addGuacamoleProperties(GuacamoleProperties properties) throws GuacamoleException { environment.addGuacamoleProperties(properties); } + + @Override + public boolean getCaseSensitiveUsernames() throws GuacamoleException { + return environment.getCaseSensitiveUsernames(); + } } diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/environment/Environment.java b/guacamole-ext/src/main/java/org/apache/guacamole/environment/Environment.java index 22b95149b..e6154a17c 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/environment/Environment.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/environment/Environment.java @@ -69,6 +69,18 @@ public interface Environment { public String getName() { return "guacd-ssl"; } }; + + /** + * A property that configures whether or not Guacamole will take case + * into account when comparing and processing usernames. + */ + public static final BooleanGuacamoleProperty CASE_SENSITIVE_USERNAMES = + new BooleanGuacamoleProperty() { + + @Override + public String getName() { return "case-sensitive-usernames"; } + + }; /** * Returns the Guacamole home directory as determined when this Environment @@ -367,5 +379,23 @@ public interface Environment { + "support dynamic definition of Guacamole properties.", getClass())); } + + /** + * Returns true if Guacamole should consider case when comparing and + * processing usernames (case-sensitive), or false if case should not be + * considered (case-insensitive). Because the past behavior of Guacamole, + * prior to the introduction of this option, was case-sensitive, the default + * value is true. + * + * @return + * true if Guacamole should consider usernames case-sensitive, otherwise + * false. + * + * @throws GuacamoleException + * If guacamole.properties cannot be parsed. + */ + public default boolean getCaseSensitiveUsernames() throws GuacamoleException { + return getProperty(CASE_SENSITIVE_USERNAMES, true); + } } diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AbstractIdentifiable.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AbstractIdentifiable.java index 3401719d4..f0cd2ed1a 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AbstractIdentifiable.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AbstractIdentifiable.java @@ -19,7 +19,6 @@ package org.apache.guacamole.net.auth; - /** * Abstract implementation of Identifiable which provides equals() and * hashCode() implementations which use the identifier to determine equality. @@ -34,12 +33,18 @@ public abstract class AbstractIdentifiable implements Identifiable { @Override public String getIdentifier() { - return identifier; + if (identifier == null || isCaseSensitive()) + return identifier; + + return identifier.toLowerCase(); } @Override public void setIdentifier(String identifier) { - this.identifier = identifier; + if (isCaseSensitive() || identifier == null) + this.identifier = identifier; + else + this.identifier = identifier.toLowerCase(); } @Override @@ -48,7 +53,10 @@ public abstract class AbstractIdentifiable implements Identifiable { if (identifier == null) return 0; - return identifier.hashCode(); + if (isCaseSensitive()) + return identifier.hashCode(); + + return identifier.toLowerCase().hashCode(); } @Override @@ -65,8 +73,12 @@ public abstract class AbstractIdentifiable implements Identifiable { if (otherIdentifier == null) return identifier == null; - // Otherwise, equal only if strings are identical - return otherIdentifier.equals(identifier); + // If this identifier is case-sensitive, evaluate with case-sensitivity. + if (isCaseSensitive()) + return otherIdentifier.equals(identifier); + + // The identifier should not be evaluated in a case-sensitive manner. + return otherIdentifier.equalsIgnoreCase(identifier); } diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AbstractUser.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AbstractUser.java index f4e85ae6f..472a3325c 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AbstractUser.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AbstractUser.java @@ -49,6 +49,14 @@ public abstract class AbstractUser extends AbstractIdentifiable public void setPassword(String password) { this.password = password; } + + @Override + public boolean isCaseSensitive() { + + // In order to avoid causing incompatibility with other extensions, + // this class maintains case-sensitive comparisons. + return true; + } /** * {@inheritDoc} diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/Identifiable.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/Identifiable.java index d32fec063..b5e0136c2 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/Identifiable.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/Identifiable.java @@ -43,5 +43,18 @@ public interface Identifiable { * The identifier to assign. */ public void setIdentifier(String identifier); + + /** + * Whether or not this identifier should be evaluated in a case-sensitive + * manner or not. By default this returns true and the identifier will + * be evaluated in a case-sensitive manner. + * + * @return + * True if the comparisons of this identifier should be case-sensitive, + * otherwise false. + */ + default public boolean isCaseSensitive() { + return true; + } }