Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
* Stores the email using exact casing as input
* Index on lower(email)
* Enforce uniqueness on lower(email)
* Use Case insensitive email to login
  • Loading branch information
anuragaryan committed Nov 28, 2018
1 parent 5fdad3e commit 101df67
Show file tree
Hide file tree
Showing 5 changed files with 22 additions and 8 deletions.
8 changes: 6 additions & 2 deletions app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class User < ApplicationRecord
after_validation :set_unconfirmed_email, if: :email_changed?, on: :update
before_create :generate_api_key, :generate_confirmation_token

validates :email, length: { maximum: 254 }
validates :email, length: { maximum: 254 }, uniqueness: { case_sensitive: false }

validates :handle, uniqueness: true, allow_nil: true
validates :handle, format: {
Expand All @@ -49,7 +49,7 @@ class User < ApplicationRecord
enum mfa_level: { no_mfa: 0, ui_mfa_only: 1, ui_and_api_mfa: 2 }

def self.authenticate(who, password)
user = find_by(email: who.downcase) || find_by(handle: who)
user = find_by('lower(email) = ?', who.downcase) || find_by(handle: who)
user if user&.authenticated?(password)
end

Expand All @@ -61,6 +61,10 @@ def self.find_by_name(name)
find_by(email: name) || find_by(handle: name)
end

def self.normalize_email(email)
email.to_s.gsub(/\s+/, "")
end

def name
handle || email
end
Expand Down
9 changes: 9 additions & 0 deletions db/migrate/20181128191130_add_index_to_lowercase_email.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
class AddIndexToLowercaseEmail < ActiveRecord::Migration[5.2]
def up
add_index "users", "lower(email) varchar_pattern_ops", name: "index_users_on_lower_email"
end

def down
remove_index "users", name: "index_users_on_lower_email"
end
end
3 changes: 2 additions & 1 deletion db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema.define(version: 20181020173922) do
ActiveRecord::Schema.define(version: 20181128191130) do

# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
Expand Down Expand Up @@ -140,6 +140,7 @@
t.string "mfa_seed"
t.integer "mfa_level", default: 0
t.string "mfa_recovery_codes", default: [], array: true
t.index "lower((email)::text) varchar_pattern_ops", name: "index_users_on_lower_email"
t.index ["email"], name: "index_users_on_email", using: :btree
t.index ["handle"], name: "index_users_on_handle", using: :btree
t.index ["id", "confirmation_token"], name: "index_users_on_id_and_confirmation_token", using: :btree
Expand Down
8 changes: 4 additions & 4 deletions test/functional/users_controller_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ class UsersControllerTest < ActionController::TestCase
context "on POST to create" do
context "when email and password are given" do
should "create a user" do
post :create, params: { user: { email: 'foo@bar.com', password: 'secret12345' } }
assert User.find_by(email: 'foo@bar.com')
post :create, params: { user: { email: 'Foo@bar.com', password: 'secret12345' } }
assert User.find_by(email: 'Foo@bar.com')
end
end

Expand All @@ -39,7 +39,7 @@ class UsersControllerTest < ActionController::TestCase

context 'confirmation mail' do
setup do
post :create, params: { user: { email: 'foo@bar.com', password: 'secretpassword', handle: 'foo' } }
post :create, params: { user: { email: 'Foo@bar.com', password: 'secretpassword', handle: 'foo' } }
Delayed::Worker.new.work_off
end

Expand All @@ -51,7 +51,7 @@ class UsersControllerTest < ActionController::TestCase
should 'deliver confirmation mail' do
refute ActionMailer::Base.deliveries.empty?
email = ActionMailer::Base.deliveries.last
assert_equal ['foo@bar.com'], email.to
assert_equal ['Foo@bar.com'], email.to
assert_equal ['no-reply@mailer.rubygems.org'], email.from
assert_equal 'Please confirm your email address with RubyGems.org', email.subject
end
Expand Down
2 changes: 1 addition & 1 deletion test/integration/sign_in_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

class SignInTest < SystemTest
setup do
create(:user, email: "nick@example.com", password: "secret12345")
create(:user, email: "Nick@Example.com", password: "secret12345")
create(:user, email: "john@example.com", password: "secret12345",
mfa_level: :ui_mfa_only, mfa_seed: 'thisisonemfaseed',
mfa_recovery_codes: %w[0123456789ab ba9876543210])
Expand Down

0 comments on commit 101df67

Please sign in to comment.