PageRenderTime 107ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 1ms

/libs/elixir/ext/encrypted.py

http://github.com/RuudBurger/CouchPotatoServer
Python | 124 lines | 118 code | 0 blank | 6 comment | 0 complexity | 1e632651ccfce419dfe3682d4536006a MD5 | raw file
Possible License(s): GPL-3.0
  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_fields=['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. **Important note**: instance attributes are encrypted in-place. This means that
  22. if one of the encrypted attributes of an instance is accessed after the
  23. instance has been flushed to the database (and thus encrypted), the value for
  24. that attribute will be crypted in the in-memory object in addition to the
  25. database row.
  26. '''
  27. from Crypto.Cipher import Blowfish
  28. from elixir.statements import Statement
  29. from sqlalchemy.orm import MapperExtension, EXT_CONTINUE, EXT_STOP
  30. try:
  31. from sqlalchemy.orm import EXT_PASS
  32. SA05orlater = False
  33. except ImportError:
  34. SA05orlater = True
  35. __all__ = ['acts_as_encrypted']
  36. __doc_all__ = []
  37. #
  38. # encryption and decryption functions
  39. #
  40. def encrypt_value(value, secret):
  41. return Blowfish.new(secret, Blowfish.MODE_CFB) \
  42. .encrypt(value).encode('string_escape')
  43. def decrypt_value(value, secret):
  44. return Blowfish.new(secret, Blowfish.MODE_CFB) \
  45. .decrypt(value.decode('string_escape'))
  46. #
  47. # acts_as_encrypted statement
  48. #
  49. class ActsAsEncrypted(object):
  50. def __init__(self, entity, for_fields=[], with_secret='abcdef'):
  51. def perform_encryption(instance, encrypt=True):
  52. encrypted = getattr(instance, '_elixir_encrypted', None)
  53. if encrypted is encrypt:
  54. # skipping encryption or decryption, as it is already done
  55. return
  56. else:
  57. # marking instance as already encrypted/decrypted
  58. instance._elixir_encrypted = encrypt
  59. if encrypt:
  60. func = encrypt_value
  61. else:
  62. func = decrypt_value
  63. for column_name in for_fields:
  64. current_value = getattr(instance, column_name)
  65. if current_value:
  66. setattr(instance, column_name,
  67. func(current_value, with_secret))
  68. def perform_decryption(instance):
  69. perform_encryption(instance, encrypt=False)
  70. class EncryptedMapperExtension(MapperExtension):
  71. def before_insert(self, mapper, connection, instance):
  72. perform_encryption(instance)
  73. return EXT_CONTINUE
  74. def before_update(self, mapper, connection, instance):
  75. perform_encryption(instance)
  76. return EXT_CONTINUE
  77. if SA05orlater:
  78. def reconstruct_instance(self, mapper, instance):
  79. perform_decryption(instance)
  80. # no special return value is required for
  81. # reconstruct_instance, but you never know...
  82. return EXT_CONTINUE
  83. else:
  84. def populate_instance(self, mapper, selectcontext, row,
  85. instance, *args, **kwargs):
  86. mapper.populate_instance(selectcontext, instance, row,
  87. *args, **kwargs)
  88. perform_decryption(instance)
  89. # EXT_STOP because we already did populate the instance and
  90. # the normal processing should not happen
  91. return EXT_STOP
  92. # make sure that the entity's mapper has our mapper extension
  93. entity._descriptor.add_mapper_extension(EncryptedMapperExtension())
  94. acts_as_encrypted = Statement(ActsAsEncrypted)