root / elixir / tags / 0.6.0 / elixir / ext / encrypted.py

Revision 340, 3.6 kB (checked in by ged, 7 years ago)

fix encrypted extension on SA0.5

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_CONTINUE
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
50try:
51    from sqlalchemy.orm import EXT_PASS
52    SA05orlater = False
53except ImportError:
54    SA05orlater = True
55
56#
57# acts_as_encrypted statement
58#
59
60class ActsAsEncrypted(object):   
61
62    def __init__(self, entity, for_fields=[], with_secret='abcdef'):
63       
64        def perform_encryption(instance, decrypt=False):
65            for column_name in for_fields:
66                current_value = getattr(instance, column_name)
67                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)
73       
74        def perform_decryption(instance):
75            perform_encryption(instance, decrypt=True)
76       
77        class EncryptedMapperExtension(MapperExtension):           
78           
79            def before_insert(self, mapper, connection, instance):
80                perform_encryption(instance)
81                return EXT_CONTINUE
82           
83            def before_update(self, mapper, connection, instance):       
84                perform_encryption(instance)
85                return EXT_CONTINUE
86       
87        if SA05orlater:
88            def on_reconstitute(self, mapper, instance):
89                perform_decryption(instance)
90                return True
91            EncryptedMapperExtension.on_reconstitute = on_reconstitute
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
100       
101        # make sure that the entity's mapper has our mapper extension
102        entity._descriptor.add_mapper_extension(EncryptedMapperExtension())
103
104
105acts_as_encrypted = Statement(ActsAsEncrypted)
Note: See TracBrowser for help on using the browser.