GUACAMOLE-1239: Make identifier comparison case-insensitive.

This commit is contained in:
Virtually Nick
2023-07-18 17:26:40 -04:00
parent 073d1d476e
commit 4d5101574a
43 changed files with 853 additions and 12 deletions

View File

@@ -54,4 +54,25 @@ public class ConfigurationService {
); );
} }
/**
* 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()
);
}
} }

View File

@@ -19,7 +19,7 @@
package org.apache.guacamole.auth.header; package org.apache.guacamole.auth.header;
import org.apache.guacamole.properties.IntegerGuacamoleProperty; import org.apache.guacamole.properties.BooleanGuacamoleProperty;
import org.apache.guacamole.properties.StringGuacamoleProperty; import org.apache.guacamole.properties.StringGuacamoleProperty;
@@ -36,7 +36,7 @@ public class HTTPHeaderGuacamoleProperties {
private 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() { public static final StringGuacamoleProperty HTTP_AUTH_HEADER = new StringGuacamoleProperty() {
@@ -45,4 +45,16 @@ public class HTTPHeaderGuacamoleProperties {
}; };
/**
* 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"; }
};
} }

View File

@@ -20,9 +20,13 @@
package org.apache.guacamole.auth.header.user; package org.apache.guacamole.auth.header.user;
import com.google.inject.Inject; 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.AbstractAuthenticatedUser;
import org.apache.guacamole.net.auth.AuthenticationProvider; import org.apache.guacamole.net.auth.AuthenticationProvider;
import org.apache.guacamole.net.auth.Credentials; import org.apache.guacamole.net.auth.Credentials;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** /**
* An HTTP header implementation of AuthenticatedUser, associating a * An HTTP header implementation of AuthenticatedUser, associating a
@@ -31,6 +35,11 @@ import org.apache.guacamole.net.auth.Credentials;
*/ */
public class AuthenticatedUser extends AbstractAuthenticatedUser { 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 * Reference to the authentication provider associated with this
* authenticated user. * authenticated user.
@@ -38,6 +47,12 @@ public class AuthenticatedUser extends AbstractAuthenticatedUser {
@Inject @Inject
private AuthenticationProvider authProvider; private AuthenticationProvider authProvider;
/**
* Service for retrieving header configuration information.
*/
@Inject
private ConfigurationService confService;
/** /**
* The credentials provided when this user was authenticated. * The credentials provided when this user was authenticated.
*/ */
@@ -58,6 +73,19 @@ public class AuthenticatedUser extends AbstractAuthenticatedUser {
setIdentifier(username.toLowerCase()); 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 @Override
public AuthenticationProvider getAuthenticationProvider() { public AuthenticationProvider getAuthenticationProvider() {
return authProvider; return authProvider;

View File

@@ -272,4 +272,17 @@ public abstract class JDBCEnvironment extends DelegatingEnvironment {
} }
/**
* 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;
} }

View File

@@ -195,4 +195,9 @@ public class ModeledAuthenticatedUser extends RemoteAuthenticatedUser {
return getUser().isPrivileged(); return getUser().isPrivileged();
} }
@Override
public boolean isCaseSensitive() {
return user.isCaseSensitive();
}
} }

View File

@@ -36,6 +36,7 @@ import java.util.TimeZone;
import org.apache.guacamole.auth.jdbc.security.PasswordEncryptionService; import org.apache.guacamole.auth.jdbc.security.PasswordEncryptionService;
import org.apache.guacamole.auth.jdbc.security.SaltService; import org.apache.guacamole.auth.jdbc.security.SaltService;
import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.jdbc.JDBCEnvironment;
import org.apache.guacamole.auth.jdbc.base.ModeledPermissions; import org.apache.guacamole.auth.jdbc.base.ModeledPermissions;
import org.apache.guacamole.form.BooleanField; import org.apache.guacamole.form.BooleanField;
import org.apache.guacamole.form.DateField; import org.apache.guacamole.form.DateField;
@@ -181,6 +182,13 @@ public class ModeledUser extends ModeledPermissions<UserModel> implements User {
@Inject @Inject
private Provider<UserRecordSet> userRecordSetProvider; private Provider<UserRecordSet> 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 * Whether attributes which control access restrictions should be exposed
* via getAttributes() or allowed to be set via setAttributes(). * via getAttributes() or allowed to be set via setAttributes().
@@ -781,4 +789,14 @@ public class ModeledUser extends ModeledPermissions<UserModel> implements User {
return (getModel().getEntityID() == null); return (getModel().getEntityID() == null);
} }
@Override
public boolean isCaseSensitive() {
try {
return environment.getCaseSensitiveUsernames();
}
catch (GuacamoleException e) {
return true;
}
}
} }

View File

@@ -439,7 +439,18 @@ public class MySQLEnvironment extends JDBCEnvironment {
// Enforce access window restrictions for active sessions unless explicitly disabled // Enforce access window restrictions for active sessions unless explicitly disabled
return getProperty( return getProperty(
MySQLGuacamoleProperties.MYSQL_ENFORCE_ACCESS_WINDOWS_FOR_ACTIVE_SESSIONS, MySQLGuacamoleProperties.MYSQL_ENFORCE_ACCESS_WINDOWS_FOR_ACTIVE_SESSIONS,
true); true
);
}
@Override
public boolean getCaseSensitiveUsernames() throws GuacamoleException {
return getProperty(
MySQLGuacamoleProperties.MYSQL_CASE_SENSITIVE_USERNAMES,
false
);
} }
} }

View File

@@ -303,4 +303,12 @@ public class MySQLGuacamoleProperties {
}; };
public static final BooleanGuacamoleProperty MYSQL_CASE_SENSITIVE_USERNAMES =
new BooleanGuacamoleProperty() {
@Override
public String getName() { return "mysql-case-sensitive-usernames"; }
};
} }

View File

@@ -399,4 +399,16 @@ public class PostgreSQLEnvironment extends JDBCEnvironment {
true); 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
);
}
} }

View File

@@ -315,4 +315,16 @@ public class PostgreSQLGuacamoleProperties {
}; };
/**
* 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"; }
};
} }

View File

@@ -329,4 +329,16 @@ public class SQLServerEnvironment extends JDBCEnvironment {
false); 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
);
}
} }

View File

@@ -258,4 +258,12 @@ public class SQLServerGuacamoleProperties {
}; };
public static final BooleanGuacamoleProperty SQLSERVER_CASE_SENSITIVE_USERNAMES =
new BooleanGuacamoleProperty() {
@Override
public String getName() { return "sqlserver-case-sensitive-usernames" ; }
};
} }

View File

@@ -24,6 +24,7 @@ import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.environment.Environment; import org.apache.guacamole.environment.Environment;
import org.apache.guacamole.properties.BooleanGuacamoleProperty;
import org.apache.guacamole.properties.ByteArrayProperty; import org.apache.guacamole.properties.ByteArrayProperty;
import org.apache.guacamole.properties.StringGuacamoleProperty; import org.apache.guacamole.properties.StringGuacamoleProperty;
@@ -39,6 +40,20 @@ public class ConfigurationService {
@Inject @Inject
private Environment environment; 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. * The encryption key to use for all decryption and signature verification.
*/ */
@@ -65,6 +80,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 * Returns the symmetric key which will be used to encrypt and sign all
* JSON data and should be used to decrypt and verify any received JSON * JSON data and should be used to decrypt and verify any received JSON

View File

@@ -20,9 +20,13 @@
package org.apache.guacamole.auth.json.user; package org.apache.guacamole.auth.json.user;
import com.google.inject.Inject; 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.AbstractAuthenticatedUser;
import org.apache.guacamole.net.auth.AuthenticationProvider; import org.apache.guacamole.net.auth.AuthenticationProvider;
import org.apache.guacamole.net.auth.Credentials; import org.apache.guacamole.net.auth.Credentials;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** /**
* An implementation of AuthenticatedUser specific to the * An implementation of AuthenticatedUser specific to the
@@ -31,6 +35,11 @@ import org.apache.guacamole.net.auth.Credentials;
*/ */
public class AuthenticatedUser extends AbstractAuthenticatedUser { 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 * Reference to the authentication provider associated with this
* authenticated user. * authenticated user.
@@ -38,6 +47,13 @@ public class AuthenticatedUser extends AbstractAuthenticatedUser {
@Inject @Inject
private AuthenticationProvider authProvider; 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. * The credentials provided when this user was authenticated.
*/ */
@@ -67,6 +83,19 @@ public class AuthenticatedUser extends AbstractAuthenticatedUser {
setIdentifier(userData.getUsername()); 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 @Override
public AuthenticationProvider getAuthenticationProvider() { public AuthenticationProvider getAuthenticationProvider() {
return authProvider; return authProvider;

View File

@@ -224,4 +224,9 @@ public class ConnectedLDAPConfiguration implements LDAPConfiguration, AutoClosea
return config.getMemberAttributeType(); return config.getMemberAttributeType();
} }
@Override
public boolean getCaseSensitiveUsernames() throws GuacamoleException {
return config.getCaseSensitiveUsernames();
}
} }

View File

@@ -19,6 +19,7 @@
package org.apache.guacamole.auth.ldap.conf; package org.apache.guacamole.auth.ldap.conf;
import com.google.inject.Inject;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import org.apache.directory.api.ldap.model.filter.ExprNode; 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.directory.api.ldap.model.name.Dn;
import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.GuacamoleServerException; import org.apache.guacamole.GuacamoleServerException;
import org.apache.guacamole.environment.Environment;
/** /**
* LDAPConfiguration implementation that returns the default values for all * LDAPConfiguration implementation that returns the default values for all
@@ -35,6 +37,12 @@ import org.apache.guacamole.GuacamoleServerException;
*/ */
public class DefaultLDAPConfiguration implements LDAPConfiguration { public class DefaultLDAPConfiguration implements LDAPConfiguration {
/**
* The environment in which Guacamole is running.
*/
@Inject
private Environment environment;
@Override @Override
public String appliesTo(String username) { public String appliesTo(String username) {
return null; return null;
@@ -151,4 +159,9 @@ public class DefaultLDAPConfiguration implements LDAPConfiguration {
return MemberAttributeType.DN; return MemberAttributeType.DN;
} }
@Override
public boolean getCaseSensitiveUsernames() throws GuacamoleException {
return environment.getCaseSensitiveUsernames();
}
} }

View File

@@ -234,4 +234,18 @@ public class EnvironmentLDAPConfiguration implements LDAPConfiguration {
); );
} }
@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()
);
}
} }

View File

@@ -204,6 +204,13 @@ public class JacksonLDAPConfiguration implements LDAPConfiguration {
@JsonProperty("member-attribute-type") @JsonProperty("member-attribute-type")
private String memberAttributeType; 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. * The default configuration options for all parameters.
*/ */
@@ -440,4 +447,10 @@ public class JacksonLDAPConfiguration implements LDAPConfiguration {
memberAttributeType, defaultConfig::getMemberAttributeType); memberAttributeType, defaultConfig::getMemberAttributeType);
} }
@Override
public boolean getCaseSensitiveUsernames() throws GuacamoleException {
return withDefault(LDAPGuacamoleProperties.LDAP_CASE_SENSITIVE_USERNAMES,
caseSensitiveUsernames, defaultConfig::getCaseSensitiveUsernames);
}
} }

View File

@@ -20,7 +20,6 @@
package org.apache.guacamole.auth.ldap.conf; package org.apache.guacamole.auth.ldap.conf;
import java.util.Collection; import java.util.Collection;
import java.util.List;
import org.apache.directory.api.ldap.model.filter.ExprNode; 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.message.AliasDerefMode;
import org.apache.directory.api.ldap.model.name.Dn; import org.apache.directory.api.ldap.model.name.Dn;
@@ -335,4 +334,20 @@ public interface LDAPConfiguration {
*/ */
MemberAttributeType getMemberAttributeType() throws GuacamoleException; 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;
} }

View File

@@ -307,4 +307,16 @@ public class LDAPGuacamoleProperties {
}; };
/**
* 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"; }
};
} }

View File

@@ -24,10 +24,13 @@ import java.util.Collections;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import org.apache.directory.api.ldap.model.name.Dn; 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.auth.ldap.ConnectedLDAPConfiguration;
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;
import org.apache.guacamole.net.auth.Credentials; import org.apache.guacamole.net.auth.Credentials;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** /**
* An LDAP-specific implementation of AuthenticatedUser, associating a * An LDAP-specific implementation of AuthenticatedUser, associating a
@@ -35,6 +38,11 @@ import org.apache.guacamole.net.auth.Credentials;
*/ */
public class LDAPAuthenticatedUser extends AbstractAuthenticatedUser { 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 * Reference to the authentication provider associated with this
* authenticated user. * authenticated user.
@@ -135,6 +143,23 @@ public class LDAPAuthenticatedUser extends AbstractAuthenticatedUser {
return config; 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 @Override
public AuthenticationProvider getAuthenticationProvider() { public AuthenticationProvider getAuthenticationProvider() {
return authProvider; return authProvider;

View File

@@ -363,4 +363,25 @@ public class ConfigurationService {
} }
} }
/**
* 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()
);
}
} }

View File

@@ -205,5 +205,17 @@ public class RadiusGuacamoleProperties {
}; };
/**
* 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"; }
};
} }

View File

@@ -23,6 +23,8 @@ import com.google.inject.Inject;
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;
import org.apache.guacamole.net.auth.Credentials; import org.apache.guacamole.net.auth.Credentials;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** /**
* An RADIUS-specific implementation of AuthenticatedUser, associating a * An RADIUS-specific implementation of AuthenticatedUser, associating a
@@ -30,6 +32,11 @@ import org.apache.guacamole.net.auth.Credentials;
*/ */
public class AuthenticatedUser extends AbstractAuthenticatedUser { 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 * Reference to the authentication provider associated with this
* authenticated user. * authenticated user.
@@ -37,6 +44,12 @@ public class AuthenticatedUser extends AbstractAuthenticatedUser {
@Inject @Inject
private AuthenticationProvider authProvider; 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. * The credentials provided when this user was authenticated.
*/ */
@@ -63,4 +76,17 @@ public class AuthenticatedUser extends AbstractAuthenticatedUser {
return credentials; 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;
}
}
} }

View File

@@ -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;
}
}

View File

@@ -23,9 +23,13 @@ import com.google.inject.Inject;
import java.util.Collections; import java.util.Collections;
import java.util.Map; import java.util.Map;
import java.util.Set; 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.AbstractAuthenticatedUser;
import org.apache.guacamole.net.auth.AuthenticationProvider; import org.apache.guacamole.net.auth.AuthenticationProvider;
import org.apache.guacamole.net.auth.Credentials; 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 * An AuthenticatedUser whose identity has been supplied by an arbitrary SSO
@@ -35,6 +39,11 @@ import org.apache.guacamole.net.auth.Credentials;
*/ */
public class SSOAuthenticatedUser extends AbstractAuthenticatedUser { 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 * Reference to the authentication provider associated with this
* authenticated user. * authenticated user.
@@ -42,6 +51,12 @@ public class SSOAuthenticatedUser extends AbstractAuthenticatedUser {
@Inject @Inject
private AuthenticationProvider authProvider; 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. * The credentials provided when this user was authenticated.
*/ */
@@ -113,4 +128,21 @@ public class SSOAuthenticatedUser extends AbstractAuthenticatedUser {
return effectiveGroups; 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;
}
}
} }

View File

@@ -20,18 +20,28 @@
package org.apache.guacamole.auth.cas; package org.apache.guacamole.auth.cas;
import com.google.inject.AbstractModule; 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.conf.ConfigurationService;
import org.apache.guacamole.auth.cas.ticket.TicketValidationService; import org.apache.guacamole.auth.cas.ticket.TicketValidationService;
import org.apache.guacamole.environment.Environment;
/** /**
* Guice module which configures CAS-specific injections. * Guice module which configures CAS-specific injections.
*/ */
public class CASAuthenticationProviderModule extends AbstractModule { public class CASAuthenticationProviderModule extends AbstractModule {
/**
* The configuration environment for this server and extension.
*/
private final Environment environment = new CASEnvironment();
@Override @Override
protected void configure() { protected void configure() {
bind(ConfigurationService.class); bind(ConfigurationService.class);
bind(TicketValidationService.class); bind(TicketValidationService.class);
bind(Environment.class).toInstance(environment);
} }
} }

View File

@@ -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());
}
}

View File

@@ -20,6 +20,7 @@
package org.apache.guacamole.auth.cas.conf; package org.apache.guacamole.auth.cas.conf;
import org.apache.guacamole.auth.cas.group.GroupFormat; 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.EnumGuacamoleProperty;
import org.apache.guacamole.properties.URIGuacamoleProperty; import org.apache.guacamole.properties.URIGuacamoleProperty;
import org.apache.guacamole.properties.StringGuacamoleProperty; import org.apache.guacamole.properties.StringGuacamoleProperty;
@@ -118,4 +119,16 @@ public class CASGuacamoleProperties {
}; };
/**
* 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"; }
};
} }

View File

@@ -22,19 +22,28 @@ package org.apache.guacamole.auth.openid;
import com.google.inject.AbstractModule; import com.google.inject.AbstractModule;
import com.google.inject.Scopes; import com.google.inject.Scopes;
import org.apache.guacamole.auth.openid.conf.ConfigurationService; 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.sso.NonceService;
import org.apache.guacamole.auth.openid.token.TokenValidationService; import org.apache.guacamole.auth.openid.token.TokenValidationService;
import org.apache.guacamole.environment.Environment;
/** /**
* Guice module which configures OpenID-specific injections. * Guice module which configures OpenID-specific injections.
*/ */
public class OpenIDAuthenticationProviderModule extends AbstractModule { public class OpenIDAuthenticationProviderModule extends AbstractModule {
/**
* The configuration environment for this server and extension.
*/
private final Environment environment = new OpenIDEnvironment();
@Override @Override
protected void configure() { protected void configure() {
bind(ConfigurationService.class); bind(ConfigurationService.class);
bind(NonceService.class).in(Scopes.SINGLETON); bind(NonceService.class).in(Scopes.SINGLETON);
bind(TokenValidationService.class); bind(TokenValidationService.class);
bind(Environment.class).toInstance(environment);
} }
} }

View File

@@ -26,6 +26,7 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.environment.Environment; import org.apache.guacamole.environment.Environment;
import org.apache.guacamole.properties.BooleanGuacamoleProperty;
import org.apache.guacamole.properties.IntegerGuacamoleProperty; import org.apache.guacamole.properties.IntegerGuacamoleProperty;
import org.apache.guacamole.properties.StringGuacamoleProperty; import org.apache.guacamole.properties.StringGuacamoleProperty;
import org.apache.guacamole.properties.URIGuacamoleProperty; import org.apache.guacamole.properties.URIGuacamoleProperty;
@@ -220,6 +221,18 @@ public class ConfigurationService {
}; };
/**
* 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"; }
};
/** /**
* The Guacamole server environment. * The Guacamole server environment.
*/ */

View File

@@ -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());
}
}

View File

@@ -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.AssertionConsumerServiceResource;
import org.apache.guacamole.auth.saml.acs.SAMLAuthenticationSessionManager; import org.apache.guacamole.auth.saml.acs.SAMLAuthenticationSessionManager;
import org.apache.guacamole.auth.saml.acs.SAMLService; 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. * Guice module which configures SAML-specific injections.
*/ */
public class SAMLAuthenticationProviderModule extends AbstractModule { public class SAMLAuthenticationProviderModule extends AbstractModule {
/**
* The environment for this server and extension.
*/
private final Environment environment = new SAMLEnvironment();
@Override @Override
protected void configure() { protected void configure() {
bind(AssertionConsumerServiceResource.class); bind(AssertionConsumerServiceResource.class);
@@ -37,6 +44,8 @@ public class SAMLAuthenticationProviderModule extends AbstractModule {
bind(SAMLAuthenticationSessionManager.class); bind(SAMLAuthenticationSessionManager.class);
bind(SAMLService.class); bind(SAMLService.class);
bind(Environment.class).toInstance(environment);
requestStaticInjection(SAMLAuthenticationEventListener.class); requestStaticInjection(SAMLAuthenticationEventListener.class);
} }

View File

@@ -190,6 +190,18 @@ public class ConfigurationService {
}; };
/**
* 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. * The Guacamole server environment.
*/ */

View File

@@ -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());
}
}

View File

@@ -22,7 +22,9 @@ package org.apache.guacamole.auth.ssl;
import com.google.inject.AbstractModule; import com.google.inject.AbstractModule;
import com.google.inject.Scopes; import com.google.inject.Scopes;
import org.apache.guacamole.auth.ssl.conf.ConfigurationService; 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.auth.sso.NonceService;
import org.apache.guacamole.environment.Environment;
/** /**
* Guice module which configures injections specific to SSO using SSL/TLS * 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 { public class SSLAuthenticationProviderModule extends AbstractModule {
/**
* The configuration environment of this server and extension.
*/
private final Environment environment = new SSLEnvironment();
@Override @Override
protected void configure() { protected void configure() {
bind(ConfigurationService.class); bind(ConfigurationService.class);
bind(NonceService.class).in(Scopes.SINGLETON); bind(NonceService.class).in(Scopes.SINGLETON);
bind(SSLAuthenticationSessionManager.class); bind(SSLAuthenticationSessionManager.class);
bind(Environment.class).toInstance(environment);
requestStaticInjection(SSLAuthenticationEventListener.class); requestStaticInjection(SSLAuthenticationEventListener.class);
} }

View File

@@ -28,6 +28,7 @@ import javax.ws.rs.core.UriBuilder;
import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.GuacamoleServerException; import org.apache.guacamole.GuacamoleServerException;
import org.apache.guacamole.environment.Environment; import org.apache.guacamole.environment.Environment;
import org.apache.guacamole.properties.BooleanGuacamoleProperty;
import org.apache.guacamole.properties.IntegerGuacamoleProperty; import org.apache.guacamole.properties.IntegerGuacamoleProperty;
import org.apache.guacamole.properties.StringGuacamoleProperty; import org.apache.guacamole.properties.StringGuacamoleProperty;
import org.apache.guacamole.properties.URIGuacamoleProperty; import org.apache.guacamole.properties.URIGuacamoleProperty;
@@ -187,6 +188,18 @@ public class ConfigurationService {
}; };
/**
* 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. * The Guacamole server environment.
*/ */

View File

@@ -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());
}
}

View File

@@ -114,4 +114,9 @@ public class DelegatingEnvironment implements Environment {
environment.addGuacamoleProperties(properties); environment.addGuacamoleProperties(properties);
} }
@Override
public boolean getCaseSensitiveUsernames() throws GuacamoleException {
return environment.getCaseSensitiveUsernames();
}
} }

View File

@@ -70,6 +70,18 @@ public interface Environment {
}; };
/**
* 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 * Returns the Guacamole home directory as determined when this Environment
* object was created. The Guacamole home directory is found by checking, in * object was created. The Guacamole home directory is found by checking, in
@@ -368,4 +380,22 @@ public interface Environment {
getClass())); 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);
}
} }

View File

@@ -19,7 +19,6 @@
package org.apache.guacamole.net.auth; package org.apache.guacamole.net.auth;
/** /**
* Abstract implementation of Identifiable which provides equals() and * Abstract implementation of Identifiable which provides equals() and
* hashCode() implementations which use the identifier to determine equality. * hashCode() implementations which use the identifier to determine equality.
@@ -34,12 +33,18 @@ public abstract class AbstractIdentifiable implements Identifiable {
@Override @Override
public String getIdentifier() { public String getIdentifier() {
if (identifier == null || isCaseSensitive())
return identifier; return identifier;
return identifier.toLowerCase();
} }
@Override @Override
public void setIdentifier(String identifier) { public void setIdentifier(String identifier) {
if (isCaseSensitive() || identifier == null)
this.identifier = identifier; this.identifier = identifier;
else
this.identifier = identifier.toLowerCase();
} }
@Override @Override
@@ -48,7 +53,10 @@ public abstract class AbstractIdentifiable implements Identifiable {
if (identifier == null) if (identifier == null)
return 0; return 0;
if (isCaseSensitive())
return identifier.hashCode(); return identifier.hashCode();
return identifier.toLowerCase().hashCode();
} }
@Override @Override
@@ -65,9 +73,13 @@ public abstract class AbstractIdentifiable implements Identifiable {
if (otherIdentifier == null) if (otherIdentifier == null)
return identifier == null; return identifier == null;
// Otherwise, equal only if strings are identical // If this identifier is case-sensitive, evaluate with case-sensitivity.
if (isCaseSensitive())
return otherIdentifier.equals(identifier); return otherIdentifier.equals(identifier);
// The identifier should not be evaluated in a case-sensitive manner.
return otherIdentifier.equalsIgnoreCase(identifier);
} }
} }

View File

@@ -50,6 +50,14 @@ public abstract class AbstractUser extends AbstractIdentifiable
this.password = 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} * {@inheritDoc}
* *

View File

@@ -44,4 +44,17 @@ public interface Identifiable {
*/ */
public void setIdentifier(String identifier); 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;
}
} }