PageRenderTime 31ms CodeModel.GetById 16ms app.highlight 11ms RepoModel.GetById 1ms app.codeStats 0ms

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

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