You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
124 lines
4.2 KiB
124 lines
4.2 KiB
'''
|
|
An encryption plugin for Elixir utilizing the excellent PyCrypto library, which
|
|
can be downloaded here: http://www.amk.ca/python/code/crypto
|
|
|
|
Values for columns that are specified to be encrypted will be transparently
|
|
encrypted and safely encoded for storage in a unicode column using the powerful
|
|
and secure Blowfish Cipher using a specified "secret" which can be passed into
|
|
the plugin at class declaration time.
|
|
|
|
Example usage:
|
|
|
|
.. sourcecode:: python
|
|
|
|
from elixir import *
|
|
from elixir.ext.encrypted import acts_as_encrypted
|
|
|
|
class Person(Entity):
|
|
name = Field(Unicode)
|
|
password = Field(Unicode)
|
|
ssn = Field(Unicode)
|
|
acts_as_encrypted(for_fields=['password', 'ssn'],
|
|
with_secret='secret')
|
|
|
|
The above Person entity will automatically encrypt and decrypt the password and
|
|
ssn columns on save, update, and load. Different secrets can be specified on
|
|
an entity by entity basis, for added security.
|
|
|
|
**Important note**: instance attributes are encrypted in-place. This means that
|
|
if one of the encrypted attributes of an instance is accessed after the
|
|
instance has been flushed to the database (and thus encrypted), the value for
|
|
that attribute will be crypted in the in-memory object in addition to the
|
|
database row.
|
|
'''
|
|
|
|
from Crypto.Cipher import Blowfish
|
|
from elixir.statements import Statement
|
|
from sqlalchemy.orm import MapperExtension, EXT_CONTINUE, EXT_STOP
|
|
|
|
try:
|
|
from sqlalchemy.orm import EXT_PASS
|
|
SA05orlater = False
|
|
except ImportError:
|
|
SA05orlater = True
|
|
|
|
__all__ = ['acts_as_encrypted']
|
|
__doc_all__ = []
|
|
|
|
|
|
#
|
|
# encryption and decryption functions
|
|
#
|
|
|
|
def encrypt_value(value, secret):
|
|
return Blowfish.new(secret, Blowfish.MODE_CFB) \
|
|
.encrypt(value).encode('string_escape')
|
|
|
|
def decrypt_value(value, secret):
|
|
return Blowfish.new(secret, Blowfish.MODE_CFB) \
|
|
.decrypt(value.decode('string_escape'))
|
|
|
|
|
|
#
|
|
# acts_as_encrypted statement
|
|
#
|
|
|
|
class ActsAsEncrypted(object):
|
|
|
|
def __init__(self, entity, for_fields=[], with_secret='abcdef'):
|
|
|
|
def perform_encryption(instance, encrypt=True):
|
|
encrypted = getattr(instance, '_elixir_encrypted', None)
|
|
if encrypted is encrypt:
|
|
# skipping encryption or decryption, as it is already done
|
|
return
|
|
else:
|
|
# marking instance as already encrypted/decrypted
|
|
instance._elixir_encrypted = encrypt
|
|
|
|
if encrypt:
|
|
func = encrypt_value
|
|
else:
|
|
func = decrypt_value
|
|
|
|
for column_name in for_fields:
|
|
current_value = getattr(instance, column_name)
|
|
if current_value:
|
|
setattr(instance, column_name,
|
|
func(current_value, with_secret))
|
|
|
|
def perform_decryption(instance):
|
|
perform_encryption(instance, encrypt=False)
|
|
|
|
class EncryptedMapperExtension(MapperExtension):
|
|
|
|
def before_insert(self, mapper, connection, instance):
|
|
perform_encryption(instance)
|
|
return EXT_CONTINUE
|
|
|
|
def before_update(self, mapper, connection, instance):
|
|
perform_encryption(instance)
|
|
return EXT_CONTINUE
|
|
|
|
if SA05orlater:
|
|
def reconstruct_instance(self, mapper, instance):
|
|
perform_decryption(instance)
|
|
# no special return value is required for
|
|
# reconstruct_instance, but you never know...
|
|
return EXT_CONTINUE
|
|
else:
|
|
def populate_instance(self, mapper, selectcontext, row,
|
|
instance, *args, **kwargs):
|
|
mapper.populate_instance(selectcontext, instance, row,
|
|
*args, **kwargs)
|
|
perform_decryption(instance)
|
|
# EXT_STOP because we already did populate the instance and
|
|
# the normal processing should not happen
|
|
return EXT_STOP
|
|
|
|
# make sure that the entity's mapper has our mapper extension
|
|
entity._descriptor.add_mapper_extension(EncryptedMapperExtension())
|
|
|
|
|
|
acts_as_encrypted = Statement(ActsAsEncrypted)
|
|
|
|
|