mirror of
https://github.com/basecamp/once-campfire.git
synced 2026-10-27 16:06:48 +09:00
A password change ends every session, but a sign-in or sign-in link checked against the old password just before the change could still save its session just after, and that session outlived the change. The session now checks, as it's saved, that the user's password is still the one the request loaded. The save's transaction holds SQLite's write lock from the start, so a change commits either before the check, which fails it, or after the insert, and ends this session with the others. Sessions start through the user's association so the check sees the user that was authenticated, and a refused sign-in is answered like a wrong password.
43 lines
1.1 KiB
Ruby
43 lines
1.1 KiB
Ruby
class SessionsController < ApplicationController
|
|
allow_unauthenticated_access only: %i[ new create ]
|
|
rate_limit to: 10, within: 3.minutes, only: :create, with: -> { render_rejection :too_many_requests }
|
|
|
|
before_action :ensure_user_exists, only: :new
|
|
|
|
def new
|
|
end
|
|
|
|
def create
|
|
if user = User.active.authenticate_by(email_address: params[:email_address], password: params[:password])
|
|
start_new_session_for user
|
|
redirect_to post_authenticating_url
|
|
else
|
|
render_rejection :unauthorized
|
|
end
|
|
rescue ActiveRecord::RecordInvalid
|
|
render_rejection :unauthorized
|
|
end
|
|
|
|
def destroy
|
|
remove_push_subscription
|
|
terminate_current_session
|
|
redirect_to root_url
|
|
end
|
|
|
|
private
|
|
def ensure_user_exists
|
|
redirect_to first_run_url if User.none?
|
|
end
|
|
|
|
def render_rejection(status)
|
|
flash.now[:alert] = "Too many requests or unauthorized."
|
|
render :new, status: status
|
|
end
|
|
|
|
def remove_push_subscription
|
|
if endpoint = params[:push_subscription_endpoint]
|
|
Push::Subscription.destroy_by(endpoint: endpoint, user_id: Current.user.id)
|
|
end
|
|
end
|
|
end
|