PageRenderTime 61ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 1ms

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

https://bitbucket.org/galaxy/galaxy-central/
Python | 130 lines | 112 code | 11 blank | 7 comment | 6 complexity | a7995c765ef8552d8124a73dfdf67e92 MD5 | raw file
Possible License(s): CC-BY-3.0
  1. import collections
  2. import os
  3. import os.path
  4. import logging
  5. import galaxy.exceptions
  6. from Crypto.Cipher import Blowfish
  7. from Crypto.Util.randpool import RandomPool
  8. from Crypto.Util import number
  9. log = logging.getLogger( __name__ )
  10. if os.path.exists( "/dev/urandom" ):
  11. # We have urandom, use it as the source of random data
  12. random_fd = os.open( "/dev/urandom", os.O_RDONLY )
  13. def get_random_bytes( nbytes ):
  14. value = os.read( random_fd, nbytes )
  15. # Normally we should get as much as we need
  16. if len( value ) == nbytes:
  17. return value.encode( "hex" )
  18. # If we don't, keep reading (this is slow and should never happen)
  19. while len( value ) < nbytes:
  20. value += os.read( random_fd, nbytes - len( value ) )
  21. return value.encode( "hex" )
  22. else:
  23. def get_random_bytes( nbytes ):
  24. nbits = nbytes * 8
  25. random_pool = RandomPool( 1064 )
  26. while random_pool.entropy < nbits:
  27. random_pool.add_event()
  28. random_pool.stir()
  29. return str( number.getRandomNumber( nbits, random_pool.get_bytes ) )
  30. class SecurityHelper( object ):
  31. def __init__( self, **config ):
  32. self.id_secret = config['id_secret']
  33. self.id_cipher = Blowfish.new( self.id_secret )
  34. per_kind_id_secret_base = config.get( 'per_kind_id_secret_base', self.id_secret )
  35. self.id_ciphers_for_kind = _cipher_cache( per_kind_id_secret_base )
  36. def encode_id( self, obj_id, kind=None ):
  37. if obj_id is None:
  38. raise galaxy.exceptions.MalformedId("Attempted to encode None id")
  39. id_cipher = self.__id_cipher( kind )
  40. # Convert to string
  41. s = str( obj_id )
  42. # Pad to a multiple of 8 with leading "!"
  43. s = ( "!" * ( 8 - len(s) % 8 ) ) + s
  44. # Encrypt
  45. return id_cipher.encrypt( s ).encode( 'hex' )
  46. def encode_dict_ids( self, a_dict, kind=None ):
  47. """
  48. Encode all ids in dictionary. Ids are identified by (a) an 'id' key or
  49. (b) a key that ends with '_id'
  50. """
  51. for key, val in a_dict.items():
  52. if key == 'id' or key.endswith('_id'):
  53. a_dict[ key ] = self.encode_id( val, kind=kind )
  54. return a_dict
  55. def encode_all_ids( self, rval, recursive=False ):
  56. """
  57. Encodes all integer values in the dict rval whose keys are 'id' or end
  58. with '_id' excluding `tool_id` which are consumed and produced as is
  59. via the API.
  60. """
  61. if not isinstance( rval, dict ):
  62. return rval
  63. for k, v in rval.items():
  64. if ( k == 'id' or k.endswith( '_id' ) ) and v is not None and k not in [ 'tool_id', 'external_id' ]:
  65. try:
  66. rval[ k ] = self.encode_id( v )
  67. except Exception:
  68. pass # probably already encoded
  69. if ( k.endswith( "_ids" ) and isinstance( v, list ) ):
  70. try:
  71. o = []
  72. for i in v:
  73. o.append( self.encode_id( i ) )
  74. rval[ k ] = o
  75. except Exception:
  76. pass
  77. else:
  78. if recursive and isinstance( v, dict ):
  79. rval[ k ] = self.encode_all_ids( v, recursive )
  80. elif recursive and isinstance( v, list ):
  81. rval[ k ] = map( lambda el: self.encode_all_ids( el, True), v )
  82. return rval
  83. def decode_id( self, obj_id, kind=None ):
  84. id_cipher = self.__id_cipher( kind )
  85. return int( id_cipher.decrypt( obj_id.decode( 'hex' ) ).lstrip( "!" ) )
  86. def encode_guid( self, session_key ):
  87. # Session keys are strings
  88. # Pad to a multiple of 8 with leading "!"
  89. s = ( "!" * ( 8 - len( session_key ) % 8 ) ) + session_key
  90. # Encrypt
  91. return self.id_cipher.encrypt( s ).encode( 'hex' )
  92. def decode_guid( self, session_key ):
  93. # Session keys are strings
  94. return self.id_cipher.decrypt( session_key.decode( 'hex' ) ).lstrip( "!" )
  95. def get_new_guid( self ):
  96. # Generate a unique, high entropy 128 bit random number
  97. return get_random_bytes( 16 )
  98. def __id_cipher( self, kind ):
  99. if not kind:
  100. id_cipher = self.id_cipher
  101. else:
  102. id_cipher = self.id_ciphers_for_kind[ kind ]
  103. return id_cipher
  104. class _cipher_cache( collections.defaultdict ):
  105. def __init__( self, secret_base ):
  106. self.secret_base = secret_base
  107. def __missing__( self, key ):
  108. return Blowfish.new( self.secret_base + "__" + key )