root / elixir / trunk / elixir / ext / encrypted.py @ 267

Revision 267, 3.1 kB (checked in by ged, 6 years ago)
  • cleanup class attributes (in the attributes-based syntax) after the
    property is attached to its entity, so that SQLAlchemy is not confused.
    Only caused problem in the case of single inheritance and when omitting
    some values. See SA ticket #866.
  • some PEP8 fixes
Line 
1'''
2An encryption plugin for Elixir utilizing the excellent PyCrypto library, which
3can be downloaded here: http://www.amk.ca/python/code/crypto
4
5Values for columns that are specified to be encrypted will be transparently
6encrypted and safely encoded for storage in a unicode column using the powerful
7and secure Blowfish Cipher using a specified "secret" which can be passed into
8the plugin at class declaration time.
9
10Example usage:
11
12.. sourcecode:: python
13
14    from elixir import *
15    from elixir.ext.encrypted import acts_as_encrypted
16   
17    class Person(Entity):
18        name = Field(Unicode)
19        password = Field(Unicode)
20        ssn = Field(Unicode)
21        acts_as_encrypted(for_columns=['password', 'ssn'],
22                          with_secret='secret')
23
24The above Person entity will automatically encrypt and decrypt the password and
25ssn columns on save, update, and load.  Different secrets can be specified on
26an entity by entity basis, for added security.
27'''
28
29from Crypto.Cipher          import Blowfish 
30from elixir.statements      import Statement
31from sqlalchemy.orm         import MapperExtension, EXT_PASS
32
33__all__ = ['acts_as_encrypted']
34__doc_all__ = []
35
36
37#
38# encryption and decryption functions
39#
40
41def encrypt_value(value, secret):
42    return Blowfish.new(secret, Blowfish.MODE_CFB) \
43                   .encrypt(value).encode('string_escape')
44
45def decrypt_value(value, secret):
46    return Blowfish.new(secret, Blowfish.MODE_CFB) \
47                   .decrypt(value.decode('string_escape'))
48
49
50#
51# acts_as_encrypted statement
52#
53
54class ActsAsEncrypted(object):   
55
56    def __init__(self, entity, for_fields=[], with_secret='abcdef'):
57       
58        def perform_encryption(instance, decrypt=False):
59            for column_name in for_fields:
60                current_value = getattr(instance, column_name)
61                if current_value:
62                    if decrypt:
63                        new_value = decrypt_value(current_value, with_secret)
64                    else:
65                        new_value = encrypt_value(current_value, with_secret)
66                    setattr(instance, column_name, new_value)
67       
68        def perform_decryption(instance):
69            perform_encryption(instance, decrypt=True)
70       
71        class EncryptedMapperExtension(MapperExtension):           
72           
73            def before_insert(self, mapper, connection, instance):
74                perform_encryption(instance)
75                return EXT_PASS
76           
77            def before_update(self, mapper, connection, instance):       
78                perform_encryption(instance)
79                return EXT_PASS
80           
81            def populate_instance(self, mapper, selectcontext, row, instance, 
82                                  *args, **kwargs):
83                mapper.populate_instance(selectcontext, instance, row, 
84                                         *args, **kwargs)
85                perform_decryption(instance)
86                return True
87       
88        # make sure that the entity's mapper has our mapper extension
89        entity._descriptor.add_mapper_extension(EncryptedMapperExtension())
90
91
92acts_as_encrypted = Statement(ActsAsEncrypted)
Note: See TracBrowser for help on using the browser.