1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

Allow passing hash on secure password validations

I was running into a case where I didn't want to just disabled the
validations and add my own. In fact, I would very much like to keep the
default validation but just de-activate it on some scenario:
e.g. Inviting a user without having to set a password for them yet so
they can add it themselves later when they receive an email invitation
to finish setting up their account.

My understanding of the validations flag originally intended was to
just disabled them and if you needed something more custom, you could
run your own validations instead.

This would be an acceptable solution, but it would add more code to my
controller. Instead validations can receive a `Hash` wich is then use to
apply validations rules to `validate`.

This is just a suggestion, I am not sure if there is a need, and I am
aware this PR is probably far from perfect. Any feedback welcome.

EDIT: implemented changes as per feedback.
This commit is contained in:
Kevin Jacoby 2022-06-29 04:02:26 +01:00 committed by Jonathan Hefner
parent 894686b8ef
commit 8804128ad4
3 changed files with 28 additions and 3 deletions

View file

@ -1,3 +1,13 @@
* Support passing `Hash` with keys `:if / :unless / :on` to SecurePassword.
This provides more flexibility when you want to trigger or skip validations on
specific conditions
```ruby
secure_password validations: {if: :requires_password?}`
```
*Kevin Jacoby*
* `has_secure_password` now supports password challenges via a * `has_secure_password` now supports password challenges via a
`password_challenge` accessor and validation. `password_challenge` accessor and validation.

View file

@ -35,8 +35,11 @@ module ActiveModel
# ActiveModel::Dirty; if dirty tracking methods are not defined, this # ActiveModel::Dirty; if dirty tracking methods are not defined, this
# validation will fail. # validation will fail.
# #
# All of the above validations can be omitted by passing # The password presence validation can be conditionally enforced by
# <tt>validations: false</tt> as an argument. This allows complete # passing an options hash to +:validations+ with the standard +:if+ /
# +:unless+ / +:on+ keys. (See ActiveModel::Validations::ClassMethods#validates
# for more information.) Alternatively, all of the above validations can
# be omitted by passing <tt>validations: false</tt>. This allows complete
# customizability of validation behavior. # customizability of validation behavior.
# #
# To use +has_secure_password+, add bcrypt (~> 3.1.7) to your Gemfile: # To use +has_secure_password+, add bcrypt (~> 3.1.7) to your Gemfile:
@ -93,11 +96,13 @@ module ActiveModel
if validations if validations
include ActiveModel::Validations include ActiveModel::Validations
validation_options = validations.is_a?(Hash) ? validations : {}
# This ensures the model has a password by checking whether the password_digest # This ensures the model has a password by checking whether the password_digest
# is present, so that this works with both new and existing records. However, # is present, so that this works with both new and existing records. However,
# when there is an error, the message is added to the password attribute instead # when there is an error, the message is added to the password attribute instead
# so that the error message will make sense to the end-user. # so that the error message will make sense to the end-user.
validate do |record| validate(validation_options) do |record|
record.errors.add(attribute, :blank) unless record.public_send("#{attribute}_digest").present? record.errors.add(attribute, :blank) unless record.public_send("#{attribute}_digest").present?
end end

View file

@ -31,6 +31,16 @@ class SecurePasswordTest < ActiveModel::TestCase
assert_not_respond_to @visitor, :valid? assert_not_respond_to @visitor, :valid?
end end
test "support conditional validation" do
user = Struct.new(:requires_password, :password_digest) do
include ActiveModel::SecurePassword
has_secure_password validations: { if: :requires_password }
end
assert_predicate user.new(false), :valid?
assert_predicate user.new(true), :invalid?
end
test "create a new user with validations and valid password/confirmation" do test "create a new user with validations and valid password/confirmation" do
@user.password = "password" @user.password = "password"
@user.password_confirmation = "password" @user.password_confirmation = "password"