This change also overrides User#find_by_login to give priority to exact matches in the login. Contributed by Greg Mefford git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3807 e93f8b46-1217-0410-a6f0-8f06a7374b81tags/1.0.0
@@ -53,7 +53,7 @@ class User < Principal | |||
attr_protected :login, :admin, :password, :password_confirmation, :hashed_password, :group_ids | |||
validates_presence_of :login, :firstname, :lastname, :mail, :if => Proc.new { |user| !user.is_a?(AnonymousUser) } | |||
validates_uniqueness_of :login, :if => Proc.new { |user| !user.login.blank? } | |||
validates_uniqueness_of :login, :if => Proc.new { |user| !user.login.blank? }, :case_sensitive => false | |||
validates_uniqueness_of :mail, :if => Proc.new { |user| !user.mail.blank? }, :case_sensitive => false | |||
# Login must contain lettres, numbers, underscores only | |||
validates_format_of :login, :with => /^[a-z0-9_\-@\.]*$/i | |||
@@ -96,7 +96,7 @@ class User < Principal | |||
def self.try_to_login(login, password) | |||
# Make sure no one can sign in with an empty password | |||
return nil if password.to_s.empty? | |||
user = find(:first, :conditions => ["login=?", login]) | |||
user = find_by_login(login) | |||
if user | |||
# user is already in local database | |||
return nil if !user.active? | |||
@@ -222,6 +222,15 @@ class User < Principal | |||
notified_projects_ids | |||
end | |||
# case-insensitive fall-over | |||
def self.find_by_login(login) | |||
# First look for an exact match | |||
user = find(:first, :conditions => ["login = ?", login]) | |||
# Fail over to case-insensitive if none was found | |||
user = find(:first, :conditions => ["LOWER(login) = ?", login.to_s.downcase]) if user.nil? | |||
return user | |||
end | |||
def self.find_by_rss_key(key) | |||
token = Token.find_by_value(key) | |||
token && token.user.active? ? token.user : nil |
@@ -55,6 +55,21 @@ class UserTest < ActiveSupport::TestCase | |||
assert user.save | |||
end | |||
context "User.login" do | |||
should "be case-insensitive." do | |||
u = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo") | |||
u.login = 'newuser' | |||
u.password, u.password_confirmation = "password", "password" | |||
assert u.save | |||
u = User.new(:firstname => "Similar", :lastname => "User", :mail => "similaruser@somenet.foo") | |||
u.login = 'NewUser' | |||
u.password, u.password_confirmation = "password", "password" | |||
assert !u.save | |||
assert_equal I18n.translate('activerecord.errors.messages.taken'), u.errors.on(:login) | |||
end | |||
end | |||
def test_mail_uniqueness_should_not_be_case_sensitive | |||
u = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo") | |||
u.login = 'newuser1' | |||
@@ -88,6 +103,25 @@ class UserTest < ActiveSupport::TestCase | |||
assert_equal 1, @admin.errors.count | |||
end | |||
context "User#try_to_login" do | |||
should "fall-back to case-insensitive if user login is not found as-typed." do | |||
user = User.try_to_login("AdMin", "admin") | |||
assert_kind_of User, user | |||
assert_equal "admin", user.login | |||
end | |||
should "select the exact matching user first" do | |||
case_sensitive_user = User.generate_with_protected!(:login => 'changed', :password => 'admin', :password_confirmation => 'admin') | |||
# bypass validations to make it appear like existing data | |||
case_sensitive_user.update_attribute(:login, 'ADMIN') | |||
user = User.try_to_login("ADMIN", "admin") | |||
assert_kind_of User, user | |||
assert_equal "ADMIN", user.login | |||
end | |||
end | |||
def test_password | |||
user = User.try_to_login("admin", "admin") | |||
assert_kind_of User, user |