Devise模型运行多次? [英] Devise models run before_save multiple times?

查看:175
本文介绍了Devise模型运行多次?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的客户端希望所有用户数据加密,所以我创建了一个 before_save after_find 使用 Gibberish



b before_save UserEncryptor.new
after_find UserEncryptor.new

#user_encryptor.rb
class UserEncryptor
def initialize
@cipher = Gibberish :: AES.new (password)
end

def before_save(user)
user.first_name = encrypt(user.first_name)
user.last_name = encrypt(user.last_name )
user.email = encrypt(user.email)除非没有user.confirmed?或user.unconfirmed_email
end

def after_find(user)
user.first_name = decrypt(user.first_name)
user.last_name = decrypt(user.last_name)
user.email = decrypt(user.email)除非没有user.confirmed?或user.unconfirmed_email
end

private
def encrypt(value)
@ cipher.enc(value)
end

def decrypt(value)
@ cipher.dec(value)
end
end

嗯,当用户首先使用 Devise 注册时,模型看起来就像是应该的。但是一旦用户确认,如果我检查用户, first_name last_name 属性看起来已被加密多个倍。所以我在 before_save 方法中放置一个断点,然后点击确认链接,我看到它连续执行了三次。结果是加密值再次被加密,然后再次被加密,所以下次我们检索记录,并且每次之后,我们得到两倍加密的值。



现在,为什么会发生这种情况?对于执行相同逻辑的其他非设计模型,不会发生这种情况。 Devise 将$ code> current_user 缓存在几个不同的地方,并将用户保存在每个位置?在执行下一个 before_find 之前,还可以调用 before_save 回调3次?



更重要的是,当我使用 Devise 时,如何成功加密我的用户数据?我也有 attr_encrypted devise_aes_encryptable 的问题,所以如果我得到很多这些建议,那我猜我有一些更多的问题要发布: - )

解决方案

我在同事的帮助下解决了我的问题。
$ b

对于加密名字和名字,只需向模型添加一个标志,表示是否已加密。这样,如果发生多个保存,则该模型知道它已经被加密,并且可以跳过该步骤:

  def before_update(user)
除非user.encrypted
user.first_name = encrypt(user.first_name)
user.last_name = encrypt(user.last_name)
user.encrypted = true
end
end

def after_find(user)
如果user.encrypted
user.first_name = decrypt(user.first_name)
user.last_name = decrypt(user .last_name)
user.encrypted = false
end
end

对于电子邮件地址,这还不够。 Devise正在做一些非常奇怪的事情,重置缓存的值,所以电子邮件地址仍然被加密。因此,我们在使用者模型时忽略了回调来加密电子邮件地址:

  def email_before_type_cast 
super.present? ? AES.decrypt(super,KEY):
end

def电子邮件
返回除非self [:email]
@email || = AES。 $($)

def email =(provided_email)
self [:email] = encrypted_email ($)

def self.find_for_authentication(conditions = {})
条件[:email] = encrypted_email(条件[:email])
super
end

def self.find_or_initialize_with_errors(required_attributes,attributes,error =:invalid)
属性[:email] = encrypted_email(attributes [:email])if属性[:email]
super
end

def self.encrypted_email decryptpted_email
AES.encrypt(decryptpted_email,KEY,{:iv => IV})
end

这让我们大部分的方式。然而,我的设计模型是可以重见的,所以当我改变用户的电子邮件地址并尝试保存时,可重复的模块遇到了一些时髦的事情,记录被保存了一百次左右,然后我得到了一个堆栈溢出和一个回滚。我们发现我们需要在用户模型上重写一个方法来做这个窍门:

  def email_was 
super 。当下? ? AES.decrypt(super,KEY):
end

现在我们所有的个人可识别信息被加密! Yay!


My client wants all user data encrypted, so I've created a before_save and after_find call back that will encrypt certain properties using Gibberish:

  # user.rb
  before_save UserEncryptor.new 
  after_find UserEncryptor.new

# user_encryptor.rb
class UserEncryptor
  def initialize
    @cipher = Gibberish::AES.new("password")
  end

  def before_save(user)
    user.first_name = encrypt(user.first_name)
    user.last_name = encrypt(user.last_name)
    user.email = encrypt(user.email) unless not user.confirmed? or user.unconfirmed_email
  end

  def after_find(user)
    user.first_name = decrypt(user.first_name)
    user.last_name = decrypt(user.last_name)
    user.email = decrypt(user.email) unless not user.confirmed? or user.unconfirmed_email
  end

  private
    def encrypt(value)
      @cipher.enc(value)
    end

    def decrypt(value)
      @cipher.dec(value)
    end
end

Well, when the user first signs up using Devise, the model looks about like it should. But then once the user confirms, if I inspect the user, the first_name and last_name properties look to have been encrypted multiple times. So I put a breakpoint in the before_save method and click the confirmation link, and I see that it's getting executed three times in a row. The result is that the encrypted value gets encrypted again, and then again, so next time we retrieve the record, and every time thereafter, we get a twice encrypted value.

Now, why the heck is this happening? It's not occurring for other non-devise models that are executing the same logic. Does Devise have the current_user cached in a few different places, and it saves the user in each location? How else could a before_save callback be called 3 times before the next before_find is executed?

And, more importantly, how can I successfully encrypt my user data when I'm using Devise? I've also had problems with attr_encrypted and devise_aes_encryptable so if I get a lot of those suggestions then I guess I have some more questions to post :-)

解决方案

I solved my problem with the help of a coworker.

For encrypting the first and last name, it was sufficient to add a flag to the model indicating whether or not it's been encrypted. That way, if multiple saves occur, the model knows it's already encrypted and can skip that step:

  def before_update(user)
    unless user.encrypted
      user.first_name = encrypt(user.first_name)
      user.last_name = encrypt(user.last_name)
      user.encrypted = true
    end
  end 

  def after_find(user) 
    if user.encrypted
      user.first_name = decrypt(user.first_name)
      user.last_name = decrypt(user.last_name)
      user.encrypted = false
    end 
  end

For the email address, this was not sufficient. Devise was doing some really weird stuff with resetting cached values, so the email address was still getting double encrypted. So instead of hooking into the callbacks to encrypt the email address, we overrode some methods on the user model:

  def email_before_type_cast
    super.present? ? AES.decrypt(super, KEY) : ""
  end 

  def email
    return "" unless self[:email]
    @email ||= AES.decrypt(self[:email], KEY)
  end

  def email=(provided_email)
    self[:email] = encrypted_email(provided_email)
    @email = provided_email
  end

  def self.find_for_authentication(conditions={})
    conditions[:email] = encrypted_email(conditions[:email])
    super
  end

  def self.find_or_initialize_with_errors(required_attributes, attributes, error=:invalid)
    attributes[:email] = encrypted_email(attributes[:email]) if attributes[:email]
    super
  end

  def self.encrypted_email decrypted_email
    AES.encrypt(decrypted_email, KEY, {:iv => IV})
  end

This got us most of the way there. However, my Devise models are reconfirmable, so when I changed a user's email address and tried to save, the reconfirmable module encountered something funky, the record got saved like a hundred times or so, and then I got a stack overflow and a rollback. We found that we needed to override one more method on the user model to do the trick:

  def email_was
    super.present? ? AES.decrypt(super, KEY) : ""
  end

Now all of our personally identifiable information is encrypted! Yay!

这篇关于Devise模型运行多次?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆