重现 Sequel 的 validates_min_length 验证
Reproduce Sequel's validates_min_length validation
我希望重现 Sequel 的 validates_min_length
Bcrypt 加密密码错误。我无法使用验证,因为它将测试密码哈希,而不是未加密的文本。
我很难让 password=
方法产生所需的错误。
未加密密码逻辑
require 'sequel'
DB = Sequel.sqlite
DB.create_table(:users) do
primary_key :id
String :name, null: false, unique: true
String :password, null: false
end
class User < Sequel::Model
plugin :validation_helpers
def validate
super
validates_presence [:name,:password]
validates_unique [:name]
validates_min_length 8, :password
end
end
IRB:
irb(main):001:0> u=User.new(name: 'foobar', password: 'Pa55w0rd')
=> #<User @values={:name=>"foobar", :password=>"Pa55w0rd"}>
irb(main):002:0> u.valid?
=> true
irb(main):003:0> u.password=nil
=> nil
irb(main):004:0> u
=> #<User @values={:name=>"foobar", :password=>nil}>
irb(main):005:0> u.valid?
=> false
irb(main):007:0> u.errors
=> {:password=>["is not present", "is shorter than 8 characters"]}
irb(main):008:0> u.password='foo'
=> "foo"
irb(main):009:0> u
=> #<User @values={:name=>"foobar", :password=>"foo"}>
irb(main):010:0> u.valid?
=> false
irb(main):011:0> u.errors
=> {:password=>["is shorter than 8 characters"]}
加密密码逻辑
require 'sequel'
require 'bcrypt'
DB = Sequel.sqlite
DB.create_table(:users) do
primary_key :id
String :name, null: false, unique: true
String :password_hash, null: false
end
class User < Sequel::Model
plugin :validation_helpers
include BCrypt
def validate
super
validates_presence [:name,:password]
validates_unique [:name]
end
def password
# check for :password_hash existence to ensure that validates_presence :password works correctly
@password ||= Password.new(password_hash) if password_hash
end
def password=(new_password)
# add validation errors
errors.add(:password, 'is shorter than 8 characters') if new_password==nil || new_password.length < 8
if new_password == nil
@password = nil
else
@password = Password.create(new_password)
end
self.password_hash = @password
end
end
IRB:
irb(main):001:0> u=User.new(name: 'foobar', password: 'Pa55w0rd')
=> #<User @values={:name=>"foobar", :password_hash=>"a$K3UALPYz/bb5bdrGmbq22eRM31A6rU3kqkbzcU4.6J5APQVSqxQo6"}>
irb(main):002:0> u.valid?
=> true
irb(main):003:0> u.password=nil
=> nil
irb(main):004:0> u
=> #<User @values={:name=>"foobar", :password_hash=>nil}>
irb(main):005:0> u.valid?
=> false
irb(main):006:0> u.errors
=> {:password=>["is not present"]}
irb(main):007:0> u.password='foo'
=> "foo"
irb(main):009:0> u
=> #<User @values={:name=>"foobar", :password_hash=>"a$lA6fsKXSvl5cd.Zl53qEqOzxk1LPehvGujWaXwcf1//IUc82CmowC"}>
irb(main):008:0> u.valid?
=> true
两个无效密码 (nil
,'foo') 都缺少 is shorter than 8 characters
错误。
我错过了什么?
版本:
$ sequel --version
sequel 5.7.1
@password
实际上并不短于 8 个字符。 BCrypt::Password 是字符串的子类,它的长度与密码哈希值相同。如果您想使用验证来确保密码的大小,则必须在 password=
中设置 @password
。
我不是 100% 确定功能,但这应该适合你。
def validate
super
validates_presence [:name,:password]
validates_unique [:name]
validates_min_length 8, :password
end
def password=(new_password)
return unless @password = new_password and @password.to_s.length >= 8
self.password_hash = Password.create(password)
end
我添加了实际长度验证来验证 valid?
将 return 为假。此外,我稍微清理了 password=
方法。
所以现在 password=
有一个保护子句,如果密码无效(如 "less than 8 characters" 定义的那样),它只是 returns。
否则我们通过 Password.create
创建一个新的 password_hash 并将其分配给 @password_hash
.
请注意: 我建议的代码和您的代码之间存在功能性变化,即当 "new_password" 无效时 @password_hash
不会被覆盖。这对我来说似乎违反直觉,无效的密码可能会覆盖现有的和有效的 password_hash,因此我在实施中发生了变化。
话虽这么说,您最好的选择实际上是这样做
class User < Sequel::Model
plugin :validation_helpers
include BCrypt
attr_accessor :password
def validate
super
validates_presence [:name,:password]
validates_unique [:name]
validates_min_length 8, :password
end
def before_save
self.password_hash = Password.create(password)
end
end
这避免了所有操作,并且只会在模型通过验证检查时创建 password_hash
(这是您实际需要的)
这按预期工作:
require 'sequel'
require 'bcrypt'
DB = Sequel.sqlite
DB.create_table(:users) do
primary_key :id
String :name, null: false, unique: true
String :password_hash, null: false
end
class User < Sequel::Model
plugin :validation_helpers
include BCrypt
def validate
super
validates_presence [:name,:password]
validates_unique [:name]
errors.add(:password, 'is shorter than 8 characters') if @password==nil || @password.length < 8
end
def password
Password.new(password_hash) if password_hash
end
def password=(new_password)
# uncomment to prevent bad passwords from changing a good password; probably needs to include a non-terminating error message
# return if new_password==nil || new_password.length < 8
# store clear-text password for validation
@password = new_password
#modify password hash accordingly
self.password_hash = if new_password then Password.create(new_password) else nil end
end
end
我希望重现 Sequel 的 validates_min_length
Bcrypt 加密密码错误。我无法使用验证,因为它将测试密码哈希,而不是未加密的文本。
我很难让 password=
方法产生所需的错误。
未加密密码逻辑
require 'sequel'
DB = Sequel.sqlite
DB.create_table(:users) do
primary_key :id
String :name, null: false, unique: true
String :password, null: false
end
class User < Sequel::Model
plugin :validation_helpers
def validate
super
validates_presence [:name,:password]
validates_unique [:name]
validates_min_length 8, :password
end
end
IRB:
irb(main):001:0> u=User.new(name: 'foobar', password: 'Pa55w0rd')
=> #<User @values={:name=>"foobar", :password=>"Pa55w0rd"}>
irb(main):002:0> u.valid?
=> true
irb(main):003:0> u.password=nil
=> nil
irb(main):004:0> u
=> #<User @values={:name=>"foobar", :password=>nil}>
irb(main):005:0> u.valid?
=> false
irb(main):007:0> u.errors
=> {:password=>["is not present", "is shorter than 8 characters"]}
irb(main):008:0> u.password='foo'
=> "foo"
irb(main):009:0> u
=> #<User @values={:name=>"foobar", :password=>"foo"}>
irb(main):010:0> u.valid?
=> false
irb(main):011:0> u.errors
=> {:password=>["is shorter than 8 characters"]}
加密密码逻辑
require 'sequel'
require 'bcrypt'
DB = Sequel.sqlite
DB.create_table(:users) do
primary_key :id
String :name, null: false, unique: true
String :password_hash, null: false
end
class User < Sequel::Model
plugin :validation_helpers
include BCrypt
def validate
super
validates_presence [:name,:password]
validates_unique [:name]
end
def password
# check for :password_hash existence to ensure that validates_presence :password works correctly
@password ||= Password.new(password_hash) if password_hash
end
def password=(new_password)
# add validation errors
errors.add(:password, 'is shorter than 8 characters') if new_password==nil || new_password.length < 8
if new_password == nil
@password = nil
else
@password = Password.create(new_password)
end
self.password_hash = @password
end
end
IRB:
irb(main):001:0> u=User.new(name: 'foobar', password: 'Pa55w0rd')
=> #<User @values={:name=>"foobar", :password_hash=>"a$K3UALPYz/bb5bdrGmbq22eRM31A6rU3kqkbzcU4.6J5APQVSqxQo6"}>
irb(main):002:0> u.valid?
=> true
irb(main):003:0> u.password=nil
=> nil
irb(main):004:0> u
=> #<User @values={:name=>"foobar", :password_hash=>nil}>
irb(main):005:0> u.valid?
=> false
irb(main):006:0> u.errors
=> {:password=>["is not present"]}
irb(main):007:0> u.password='foo'
=> "foo"
irb(main):009:0> u
=> #<User @values={:name=>"foobar", :password_hash=>"a$lA6fsKXSvl5cd.Zl53qEqOzxk1LPehvGujWaXwcf1//IUc82CmowC"}>
irb(main):008:0> u.valid?
=> true
两个无效密码 (nil
,'foo') 都缺少 is shorter than 8 characters
错误。
我错过了什么?
版本:
$ sequel --version
sequel 5.7.1
@password
实际上并不短于 8 个字符。 BCrypt::Password 是字符串的子类,它的长度与密码哈希值相同。如果您想使用验证来确保密码的大小,则必须在 password=
中设置 @password
。
我不是 100% 确定功能,但这应该适合你。
def validate
super
validates_presence [:name,:password]
validates_unique [:name]
validates_min_length 8, :password
end
def password=(new_password)
return unless @password = new_password and @password.to_s.length >= 8
self.password_hash = Password.create(password)
end
我添加了实际长度验证来验证 valid?
将 return 为假。此外,我稍微清理了 password=
方法。
所以现在 password=
有一个保护子句,如果密码无效(如 "less than 8 characters" 定义的那样),它只是 returns。
否则我们通过 Password.create
创建一个新的 password_hash 并将其分配给 @password_hash
.
请注意: 我建议的代码和您的代码之间存在功能性变化,即当 "new_password" 无效时 @password_hash
不会被覆盖。这对我来说似乎违反直觉,无效的密码可能会覆盖现有的和有效的 password_hash,因此我在实施中发生了变化。
话虽这么说,您最好的选择实际上是这样做
class User < Sequel::Model
plugin :validation_helpers
include BCrypt
attr_accessor :password
def validate
super
validates_presence [:name,:password]
validates_unique [:name]
validates_min_length 8, :password
end
def before_save
self.password_hash = Password.create(password)
end
end
这避免了所有操作,并且只会在模型通过验证检查时创建 password_hash
(这是您实际需要的)
这按预期工作:
require 'sequel'
require 'bcrypt'
DB = Sequel.sqlite
DB.create_table(:users) do
primary_key :id
String :name, null: false, unique: true
String :password_hash, null: false
end
class User < Sequel::Model
plugin :validation_helpers
include BCrypt
def validate
super
validates_presence [:name,:password]
validates_unique [:name]
errors.add(:password, 'is shorter than 8 characters') if @password==nil || @password.length < 8
end
def password
Password.new(password_hash) if password_hash
end
def password=(new_password)
# uncomment to prevent bad passwords from changing a good password; probably needs to include a non-terminating error message
# return if new_password==nil || new_password.length < 8
# store clear-text password for validation
@password = new_password
#modify password hash accordingly
self.password_hash = if new_password then Password.create(new_password) else nil end
end
end