Show
Ignore:
Timestamp:
02/02/09 17:25:51 (3 years ago)
Author:
ged
Message:

- Fixed encrypted extension to not encrypt several times an instance attributes

when that instance is flushed several times before being expunged from the
session.

- added note about how the encrypted extension works

Files:
1 modified

Legend:

Unmodified
Added
Removed
  • elixir/trunk/elixir/ext/encrypted.py

    r440 r446  
    2525ssn columns on save, update, and load.  Different secrets can be specified on 
    2626an entity by entity basis, for added security. 
     27 
     28**Important note**: instance attributes are encrypted in-place. This means that 
     29if one of the encrypted attributes of an instance is accessed after the 
     30instance has been flushed to the database (and thus encrypted), the value for 
     31that attribute will be crypted in the in-memory object in addition to the 
     32database row. 
    2733''' 
    2834 
    29 from Crypto.Cipher          import Blowfish 
    30 from elixir.statements      import Statement 
    31 from sqlalchemy.orm         import MapperExtension, EXT_CONTINUE 
     35from Crypto.Cipher import Blowfish 
     36from elixir.statements import Statement 
     37from sqlalchemy.orm import MapperExtension, EXT_CONTINUE, EXT_STOP 
    3238 
    3339try: 
     
    6268    def __init__(self, entity, for_fields=[], with_secret='abcdef'): 
    6369 
    64         def perform_encryption(instance, decrypt=False): 
     70        def perform_encryption(instance, encrypt=True): 
     71            encrypted = getattr(instance, '_elixir_encrypted', None) 
     72            if encrypted is encrypt: 
     73                # skipping encryption or decryption, as it is already done 
     74                return 
     75            else: 
     76                # marking instance as already encrypted/decrypted 
     77                instance._elixir_encrypted = encrypt 
     78 
     79            if encrypt: 
     80                func = encrypt_value 
     81            else: 
     82                func = decrypt_value 
     83 
    6584            for column_name in for_fields: 
    6685                current_value = getattr(instance, column_name) 
    6786                if current_value: 
    68                     if decrypt: 
    69                         new_value = decrypt_value(current_value, with_secret) 
    70                     else: 
    71                         new_value = encrypt_value(current_value, with_secret) 
    72                     setattr(instance, column_name, new_value) 
     87                    setattr(instance, column_name, 
     88                            func(current_value, with_secret)) 
    7389 
    7490        def perform_decryption(instance): 
    75             perform_encryption(instance, decrypt=True) 
     91            perform_encryption(instance, encrypt=False) 
    7692 
    7793        class EncryptedMapperExtension(MapperExtension): 
     
    85101                return EXT_CONTINUE 
    86102 
    87         if SA05orlater: 
    88             def reconstruct_instance(self, mapper, instance): 
    89                 perform_decryption(instance) 
    90                 return True 
    91             EncryptedMapperExtension.reconstruct_instance = reconstruct_instance 
    92         else: 
    93             def populate_instance(self, mapper, selectcontext, row, instance, 
    94                                   *args, **kwargs): 
    95                 mapper.populate_instance(selectcontext, instance, row, 
    96                                          *args, **kwargs) 
    97                 perform_decryption(instance) 
    98                 return True 
    99             EncryptedMapperExtension.populate_instance = populate_instance 
     103            if SA05orlater: 
     104                def reconstruct_instance(self, mapper, instance): 
     105                    perform_decryption(instance) 
     106                    # no special return value is required for 
     107                    # reconstruct_instance, but you never know... 
     108                    return EXT_CONTINUE 
     109            else: 
     110                def populate_instance(self, mapper, selectcontext, row, 
     111                                      instance, *args, **kwargs): 
     112                    mapper.populate_instance(selectcontext, instance, row, 
     113                                             *args, **kwargs) 
     114                    perform_decryption(instance) 
     115                    # EXT_STOP because we already did populate the instance and 
     116                    # the normal processing should not happen 
     117                    return EXT_STOP 
    100118 
    101119        # make sure that the entity's mapper has our mapper extension