Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 0 additions & 11 deletions app/controllers/sessions_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,6 @@ def new

sig { void }
def create
begin
clear_expired_sessions
rescue StandardError
# Cleanup is best effort; it must not block logging in.
end

user, status = User.authenticate_with_status(params[:session][:login], params[:session][:password])

case status
Expand Down Expand Up @@ -68,9 +62,4 @@ def try_to_redirect_incorrect_login(redirect_params = {})
redirect_to login_path, redirect_params
end
end

sig { void }
def clear_expired_sessions
Session.delete_expired
end
end
4 changes: 0 additions & 4 deletions app/models/session.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,6 @@

class Session < ActiveRecord::SessionStore::Session
extend T::Sig
sig { returns(Integer) }
def self.delete_expired
where(updated_at: ...1.month.ago).delete_all
end

sig { params(user: User).returns(T::Boolean) }
def belongs_to?(user)
Expand Down
3 changes: 2 additions & 1 deletion app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,9 @@ class User < ApplicationRecord
has_many :organization_memberships, dependent: :destroy
has_many :memberships, through: :organization_memberships, source: :organization

# Case-sensitive so it uses index_users_on_login; a case-insensitive check scans every user on each signup.
validates :login, presence: true,
uniqueness: { case_sensitive: false },
uniqueness: true,
length: { within: 2..50 }

validates :email, presence: true,
Expand Down
11 changes: 11 additions & 0 deletions spec/controllers/sessions_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,17 @@ def post_create
end
end

describe 'with stale sessions in the store' do
it 'does not delete them while logging in' do
stale = Session.create!(session_id: 'stale', data: {})
stale.update_column(:updated_at, 2.months.ago)

post_create

expect(Session.exists?(stale.id)).to be(true)
end
end

describe 'when authentication fails' do
before :each do
allow(User).to receive(:authenticate_with_status).and_return([nil, :rejected])
Expand Down
10 changes: 10 additions & 0 deletions spec/models/user_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,16 @@
expect(user.errors[:login].size).to eq(1)
end

it 'checks login uniqueness without scanning LOWER(login)' do
user = User.new(@params)
queries = []
callback = ->(*, payload) { queries << payload[:sql] }
ActiveSupport::Notifications.subscribed(callback, 'sql.active_record') { user.valid? }

expect(queries.grep(/"users"\."login"/)).not_to be_empty
expect(queries.grep(/LOWER\("users"\."login"\)/i)).to be_empty
end

it 'should fail without email' do
@params.delete(:email)

Expand Down
Loading