2012-03-27 12 views
1

Mein Kunde will, dass alle Benutzerdaten verschlüsselt, also habe ich eine before_save und after_find Rückruf erstellt, die bestimmte Eigenschaften mit Hilfe Gibberish verschlüsseln: Erste Anzeichen up mit Devise, das Modell siehtDevise Modelle laufen vor_Speichern mehrmals?

# 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 

Nun, wenn der Benutzer ungefähr wie es sollte. Aber sobald der Benutzer bestätigt, wenn ich den Benutzer inspiziere, scheinen die first_name und last_name Eigenschaften mehrfach verschlüsselt worden zu sein. Also setze ich einen Breakpoint in die before_save Methode und klicke auf den Bestätigungslink, und ich sehe, dass es dreimal hintereinander ausgeführt wird. Das Ergebnis ist, dass der verschlüsselte Wert erneut verschlüsselt wird und dann erneut, so dass beim nächsten Abruf des Datensatzes und jedes Mal danach ein zweifach verschlüsselter Wert erhalten wird.

Nun, warum zum Teufel ist das passiert? Es tritt nicht bei anderen Nicht-Modell-Modellen auf, die dieselbe Logik ausführen. Hat Devise die current_user im Cache an ein paar verschiedenen Orten, und es speichert den Benutzer an jedem Ort? Wie sonst könnte ein Callback before_save 3 Mal aufgerufen werden, bevor das nächste before_find ausgeführt wird?

Und noch wichtiger, wie kann ich meine Benutzerdaten erfolgreich verschlüsseln, wenn ich Devise verwende? Ich habe auch Probleme mit attr_encrypted und devise_aes_encryptable, also wenn ich viele dieser Vorschläge dann habe, schätze ich, habe ich noch einige Fragen zu posten :-)

Antwort

2

Ich löste mein Problem mit Hilfe eines Mitarbeiters.

Für die Verschlüsselung des Vor- und Nachnamens war es ausreichend, dem Modell ein Flag hinzuzufügen, das anzeigt, ob es verschlüsselt wurde oder nicht. Auf diese Weise, wenn mehrere auftreten speichern, weiß das Modell ist es bereits verschlüsselt und diesen Schritt überspringen:

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 

Für die E-Mail-Adresse, die nicht ausreichend war. Devise hat mit dem Zurücksetzen von zwischengespeicherten Werten wirklich seltsame Sachen gemacht, so dass die E-Mail-Adresse immer noch doppelt verschlüsselt wurde. Anstatt also in die Rückrufe von Einhaken des E-Mail-Adresse zu verschlüsseln, overrode wir einige Methoden auf dem Benutzermodell:

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 

Dies hat uns die meisten der Weg dorthin. Meine Devise-Modelle sind jedoch wiederbestätigbar. Als ich also die E-Mail-Adresse eines Benutzers änderte und versuchte zu speichern, stieß das wiederherstellbare Modul auf etwas Unkonventionelles, der Datensatz wurde etwa hundert Mal gespeichert, und dann kam ein Stapelüberlauf und ein Rollback. Wir fanden, dass wir noch eine Methode auf dem Benutzermodell außer Kraft zu setzen benötigt, um den Trick zu tun:

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

Nun sind alle unsere persönlichen Informationen verschlüsselt werden! Yay!

Verwandte Themen