PageRenderTime 71ms CodeModel.GetById 44ms RepoModel.GetById 1ms app.codeStats 0ms

/skink/lib/elixir/ext/encrypted.py

https://bitbucket.org/vitormazzi/skink
Python | 106 lines | 100 code | 0 blank | 6 comment | 0 complexity | ab93a77f58490c39cd8dda8211a40b89 MD5 | raw file
  1. '''
  2. An encryption plugin for Elixir utilizing the excellent PyCrypto library, which
  3. can be downloaded here: http://www.amk.ca/python/code/crypto
  4. Values for columns that are specified to be encrypted will be transparently
  5. encrypted and safely encoded for storage in a unicode column using the powerful
  6. and secure Blowfish Cipher using a specified "secret" which can be passed into
  7. the plugin at class declaration time.
  8. Example usage:
  9. .. sourcecode:: python
  10. from elixir import *
  11. from elixir.ext.encrypted import acts_as_encrypted
  12. class Person(Entity):
  13. name = Field(Unicode)
  14. password = Field(Unicode)
  15. ssn = Field(Unicode)
  16. acts_as_encrypted(for_columns=['password', 'ssn'],
  17. with_secret='secret')
  18. The above Person entity will automatically encrypt and decrypt the password and
  19. ssn columns on save, update, and load. Different secrets can be specified on
  20. an entity by entity basis, for added security.
  21. '''
  22. from Crypto.Cipher import Blowfish
  23. from elixir.statements import Statement
  24. from sqlalchemy.orm import MapperExtension, EXT_CONTINUE
  25. __all__ = ['acts_as_encrypted']
  26. __doc_all__ = []
  27. #
  28. # encryption and decryption functions
  29. #
  30. def encrypt_value(value, secret):
  31. return Blowfish.new(secret, Blowfish.MODE_CFB) \
  32. .encrypt(value).encode('string_escape')
  33. def decrypt_value(value, secret):
  34. return Blowfish.new(secret, Blowfish.MODE_CFB) \
  35. .decrypt(value.decode('string_escape'))
  36. try:
  37. from sqlalchemy.orm import EXT_PASS
  38. SA05orlater = False
  39. except ImportError:
  40. SA05orlater = True
  41. #
  42. # acts_as_encrypted statement
  43. #
  44. class ActsAsEncrypted(object):
  45. def __init__(self, entity, for_fields=[], with_secret='abcdef'):
  46. def perform_encryption(instance, decrypt=False):
  47. for column_name in for_fields:
  48. current_value = getattr(instance, column_name)
  49. if current_value:
  50. if decrypt:
  51. new_value = decrypt_value(current_value, with_secret)
  52. else:
  53. new_value = encrypt_value(current_value, with_secret)
  54. setattr(instance, column_name, new_value)
  55. def perform_decryption(instance):
  56. perform_encryption(instance, decrypt=True)
  57. class EncryptedMapperExtension(MapperExtension):
  58. def before_insert(self, mapper, connection, instance):
  59. perform_encryption(instance)
  60. return EXT_CONTINUE
  61. def before_update(self, mapper, connection, instance):
  62. perform_encryption(instance)
  63. return EXT_CONTINUE
  64. if SA05orlater:
  65. def reconstruct_instance(self, mapper, instance):
  66. perform_decryption(instance)
  67. return True
  68. EncryptedMapperExtension.reconstruct_instance = reconstruct_instance
  69. else:
  70. def populate_instance(self, mapper, selectcontext, row, instance,
  71. *args, **kwargs):
  72. mapper.populate_instance(selectcontext, instance, row,
  73. *args, **kwargs)
  74. perform_decryption(instance)
  75. return True
  76. EncryptedMapperExtension.populate_instance = populate_instance
  77. # make sure that the entity's mapper has our mapper extension
  78. entity._descriptor.add_mapper_extension(EncryptedMapperExtension())
  79. acts_as_encrypted = Statement(ActsAsEncrypted)