Разработать модели, запущенные перед сохранением несколько раз? - PullRequest
1 голос
/ 27 марта 2012

Мой клиент хочет, чтобы все пользовательские данные были зашифрованы, поэтому я создал обратный вызов before_save и after_find, который будет шифровать определенные свойства с использованием 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

Хорошо, когда пользовательсначала регистрируется с помощью Devise, модель выглядит примерно так, как должна.Но затем, когда пользователь подтверждает, что если я проверяю пользователя, свойства first_name и last_name выглядят зашифрованными несколько раз.Поэтому я установил точку останова в методе before_save и щелкнул ссылку подтверждения, и я вижу, что она выполняется три раза подряд.В результате зашифрованное значение снова шифруется, а затем снова, так что в следующий раз, когда мы получим запись, и каждый раз после этого мы получаем дважды зашифрованное значение.

Теперь, какого черта это происходит?Это не происходит для других не разработанных моделей, которые выполняют ту же логику.Devise кэширует current_user в нескольких разных местах и ​​сохраняет пользователя в каждом месте?Как еще можно вызвать обратный вызов before_save 3 раза до выполнения следующего before_find?

И, что более важно, как я могу успешно зашифровать свои пользовательские данные, когда я использую Devise?У меня также были проблемы с attr_encrypted и devise_aes_encryptable, поэтому, если я получу много таких предложений, я думаю, у меня есть еще несколько вопросов: -)

1 Ответ

2 голосов
/ 04 апреля 2012

Я решил свою проблему с помощью коллеги.

Для шифрования имени и фамилии было достаточно добавить флаг к модели, указывающий, зашифрован ли он или нет.Таким образом, если происходит несколько сохранений, модель знает, что она уже зашифрована, и может пропустить этот шаг:

  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

Для адреса электронной почты этого было недостаточно.Devise делал некоторые действительно странные вещи со сбросом кэшированных значений, так что адрес электронной почты все еще получал двойное шифрование.Поэтому вместо того, чтобы подключаться к обратным вызовам для шифрования адреса электронной почты, мы отвергли некоторые методы в пользовательской модели:

  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

Это дало нам большую часть пути.Тем не менее, мои модели Devise могут быть повторно подтверждены, поэтому, когда я изменил адрес электронной почты пользователя и попытался сохранить, модуль с повторным подтверждением обнаружил нечто странное, запись была сохранена примерно сто раз или около того, а затем я получил переполнение стека и откат.Мы обнаружили, что нам нужно переопределить еще один метод в пользовательской модели, чтобы добиться цели:

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

Теперь вся наша личная информация зашифрована!Ура!

...