重现Sequel的validates_min_length验证

时间:2018-04-19 13:54:31

标签: ruby sequel

我希望为Bcrypt加密的密码重现Sequel的validates_min_length错误。我无法使用验证,因为它将测试密码哈希,而不是未加密的文本。

我很难通过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=>"$2a$10$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=>"$2a$10$lA6fsKXSvl5cd.Zl53qEqOzxk1LPehvGujWaXwcf1//IUc82CmowC"}>
irb(main):008:0> u.valid?
=> true

两个无效密码(nil,'foo')都缺少is shorter than 8 characters错误。

我错过了什么?

版本:

$ sequel --version
sequel 5.7.1

3 个答案:

答案 0 :(得分:3)

@password实际上不会短于8个字符。 BCrypt :: Password是字符串的子类,它与密码哈希的长度相同。如果您想使用验证来确保密码的大小,则必须在@password中设置password=

答案 1 :(得分:0)

我不是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?将返回false。另外,我稍微清理了password=方法。 所以现在password=有一个保护条款,如果密码无效,只需返回&#34;少于8个字符&#34;。 否则,我们会通过Password.create创建一个新的password_hash,并将其分配给@password_hash

请注意:我建议的代码与您的代码之间存在功能变化,即{&#34; new_password&#34;}时@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(这是您实际需要的所有操作)

答案 2 :(得分:0)

这可以按照需要运行:

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