Files
prism/examples/blog/app/controllers/password_resets_controller.rb

63 lines
2.4 KiB
Ruby

class PasswordResetsController < ApplicationController
def new
render(:new)
end
# Always shows the SAME generic "check your email" notice whether or not
# the address belongs to an account - branching the response on
# User.find_by(email:) here would turn this form into an email-enumeration
# oracle one guess at a time, the same concern app.rb's signups/ip
# throttle exists for one step earlier in the account lifecycle (and this
# action gets its own password_resets/ip throttle below for the same
# reason: even a constant-response endpoint still leaks timing/rate
# information if it can be hit without limit).
def create
email = params.require('password_reset').permit(:email).to_h['email']
user = User.find_by(email: email)
if user
token = user.generate_token_for(:password_reset)
NotificationMailer.password_reset(user.id, token).deliver_later
end
flash[:notice] = t('password_resets.instructions_sent_notice')
redirect url_for(:new_session)
end
def edit
user = User.find_by_token_for(:password_reset, params['token'])
if user
render(:edit, locals: { token: params['token'], errors: [] })
else
flash[:notice] = t('password_resets.invalid_token_notice')
redirect url_for(:new_password_reset)
end
end
# find_by_token_for's fingerprint block (see User#generates_token_for)
# snapshots password_digest at #new/#create time - so the very save
# below that sets the new password also produces a new digest, which
# means the SAME token this action just accepted can never verify again
# afterwards. A stolen-but-unused link still dies once its owner resets
# through it; there is no separate "used" flag or token table to keep
# that guarantee true.
def update
user = User.find_by_token_for(:password_reset, params['token'])
unless user
flash[:notice] = t('password_resets.invalid_token_notice')
redirect url_for(:new_password_reset)
return
end
attrs = params.require('password_reset').permit(:password, :password_confirmation).to_h
user.password = attrs['password']
user.password_confirmation = attrs['password_confirmation']
if user.save
flash[:notice] = t('password_resets.reset_notice')
redirect url_for(:new_session)
else
status(422)
render(:edit, locals: { token: params['token'], errors: user.errors.full_messages })
end
end
end