mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Convert attributes to Hash in authenticate_by
Follow-up to #43765. This ensures that `authenticate_by` supports controller params, e.g.: ```ruby User.authenticate_by(params.permit(:email, :password)) ``` Note that `ActionController::Parameters#to_h` will raise an error when there are unpermitted params. This guards against unsafe usage such as: ```ruby User.authenticate_by(params) ```
This commit is contained in:
parent
5aeb7d1c40
commit
c5bbc81014
2 changed files with 18 additions and 6 deletions
|
@ -1,7 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "active_support/core_ext/hash/except"
|
||||
|
||||
module ActiveRecord
|
||||
module SecurePassword
|
||||
extend ActiveSupport::Concern
|
||||
|
@ -37,15 +35,17 @@ module ActiveRecord
|
|||
# User.authenticate_by(email: "jdoe@example.com") # => ArgumentError
|
||||
# User.authenticate_by(password: "abc123") # => ArgumentError
|
||||
def authenticate_by(attributes)
|
||||
passwords = attributes.select { |name, value| !has_attribute?(name) && has_attribute?("#{name}_digest") }
|
||||
passwords, identifiers = attributes.to_h.partition do |name, value|
|
||||
!has_attribute?(name) && has_attribute?("#{name}_digest")
|
||||
end.map(&:to_h)
|
||||
|
||||
raise ArgumentError, "One or more password arguments are required" if passwords.empty?
|
||||
raise ArgumentError, "One or more finder arguments are required" if passwords.size == attributes.size
|
||||
raise ArgumentError, "One or more finder arguments are required" if identifiers.empty?
|
||||
|
||||
if record = find_by(attributes.except(*passwords.keys))
|
||||
if record = find_by(identifiers)
|
||||
record if passwords.count { |name, value| record.public_send(:"authenticate_#{name}", value) } == passwords.size
|
||||
else
|
||||
self.new(passwords)
|
||||
new(passwords)
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
|
|
@ -64,4 +64,16 @@ class SecurePasswordTest < ActiveRecord::TestCase
|
|||
User.authenticate_by(password: @user.password)
|
||||
end
|
||||
end
|
||||
|
||||
test "authenticate_by accepts any object that implements to_h" do
|
||||
params = Enumerator.new { raise "must access via to_h" }
|
||||
|
||||
assert_called_with(params, :to_h, [[]], returns: { token: @user.token, password: @user.password }) do
|
||||
assert_equal @user, User.authenticate_by(params)
|
||||
end
|
||||
|
||||
assert_called_with(params, :to_h, [[]], returns: { token: "wrong", password: @user.password }) do
|
||||
assert_nil User.authenticate_by(params)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue