/lib/galaxy/web/security/__init__.py

https://bitbucket.org/cistrome/cistrome-harvard/ · Python · 100 lines · 82 code · 11 blank · 7 comment · 5 complexity · e2578ecb05272c3f283bd7e734a880fe MD5 · raw file

  1. import collections
  2. import os
  3. import os.path
  4. import logging
  5. import pkg_resources
  6. pkg_resources.require( "pycrypto" )
  7. from Crypto.Cipher import Blowfish
  8. from Crypto.Util.randpool import RandomPool
  9. from Crypto.Util import number
  10. log = logging.getLogger( __name__ )
  11. if os.path.exists( "/dev/urandom" ):
  12. # We have urandom, use it as the source of random data
  13. random_fd = os.open( "/dev/urandom", os.O_RDONLY )
  14. def get_random_bytes( nbytes ):
  15. value = os.read( random_fd, nbytes )
  16. # Normally we should get as much as we need
  17. if len( value ) == nbytes:
  18. return value.encode( "hex" )
  19. # If we don't, keep reading (this is slow and should never happen)
  20. while len( value ) < nbytes:
  21. value += os.read( random_fd, nbytes - len( value ) )
  22. return value.encode( "hex" )
  23. else:
  24. def get_random_bytes( nbytes ):
  25. nbits = nbytes * 8
  26. random_pool = RandomPool( 1064 )
  27. while random_pool.entropy < nbits:
  28. random_pool.add_event()
  29. random_pool.stir()
  30. return str( number.getRandomNumber( nbits, random_pool.get_bytes ) )
  31. class SecurityHelper( object ):
  32. def __init__( self, **config ):
  33. self.id_secret = config['id_secret']
  34. self.id_cipher = Blowfish.new( self.id_secret )
  35. per_kind_id_secret_base = config.get( 'per_kind_id_secret_base', self.id_secret )
  36. self.id_ciphers_for_kind = _cipher_cache( per_kind_id_secret_base )
  37. def encode_id( self, obj_id, kind=None ):
  38. id_cipher = self.__id_cipher( kind )
  39. # Convert to string
  40. s = str( obj_id )
  41. # Pad to a multiple of 8 with leading "!"
  42. s = ( "!" * ( 8 - len(s) % 8 ) ) + s
  43. # Encrypt
  44. return id_cipher.encrypt( s ).encode( 'hex' )
  45. def encode_dict_ids( self, a_dict, kind=None ):
  46. """
  47. Encode all ids in dictionary. Ids are identified by (a) an 'id' key or
  48. (b) a key that ends with '_id'
  49. """
  50. for key, val in a_dict.items():
  51. if key == 'id' or key.endswith('_id'):
  52. a_dict[ key ] = self.encode_id( val, kind=kind )
  53. return a_dict
  54. def decode_id( self, obj_id, kind=None ):
  55. id_cipher = self.__id_cipher( kind )
  56. return int( id_cipher.decrypt( obj_id.decode( 'hex' ) ).lstrip( "!" ) )
  57. def encode_guid( self, session_key ):
  58. # Session keys are strings
  59. # Pad to a multiple of 8 with leading "!"
  60. s = ( "!" * ( 8 - len( session_key ) % 8 ) ) + session_key
  61. # Encrypt
  62. return self.id_cipher.encrypt( s ).encode( 'hex' )
  63. def decode_guid( self, session_key ):
  64. # Session keys are strings
  65. return self.id_cipher.decrypt( session_key.decode( 'hex' ) ).lstrip( "!" )
  66. def get_new_guid( self ):
  67. # Generate a unique, high entropy 128 bit random number
  68. return get_random_bytes( 16 )
  69. def __id_cipher( self, kind ):
  70. if not kind:
  71. id_cipher = self.id_cipher
  72. else:
  73. id_cipher = self.id_ciphers_for_kind[ kind ]
  74. return id_cipher
  75. class _cipher_cache( collections.defaultdict ):
  76. def __init__( self, secret_base ):
  77. self.secret_base = secret_base
  78. def __missing__( self, key ):
  79. return Blowfish.new( self.secret_base + "__" + key )