Example #1
0
def check_password_PBKDF2( guess, hashed ):
    # Split the database representation to extract cost_factor and salt
    name, hash_function, cost_factor, salt, encoded_original = hashed.split( '$', 5 )
    # Hash the guess using the same parameters
    hashed_guess = pbkdf2_bin( bytes( guess ), salt, int( cost_factor ), KEY_LENGTH, getattr( hashlib, hash_function ) )
    encoded_guess = b64encode( hashed_guess )
    return safe_str_cmp( encoded_original, encoded_guess )
def check_password_PBKDF2(guess, hashed):
    # Split the database representation to extract cost_factor and salt
    name, hash_function, cost_factor, salt, encoded_original = hashed.split('$', 5)
    # Hash the guess using the same parameters
    hashed_guess = pbkdf2_bin(bytes(guess), salt, int(cost_factor), KEY_LENGTH, getattr(hashlib, hash_function))
    encoded_guess = b64encode(hashed_guess)
    return safe_str_cmp(encoded_original, encoded_guess)
Example #3
0
 def _check_master_api_key(self, api_key):
     master_api_key = getattr(self.app.config, 'master_api_key', None)
     if not master_api_key:
         return False
     # Hash keys to make them the same size, so we can do safe comparison.
     master_hash = hashlib.sha256(master_api_key).hexdigest()
     provided_hash = hashlib.sha256(api_key).hexdigest()
     return safe_str_cmp(master_hash, provided_hash)
Example #4
0
 def _check_master_api_key( self, api_key ):
     master_api_key = getattr( self.app.config, 'master_api_key', None )
     if not master_api_key:
         return False
     # Hash keys to make them the same size, so we can do safe comparison.
     master_hash = hashlib.sha256( master_api_key ).hexdigest()
     provided_hash = hashlib.sha256( api_key ).hexdigest()
     return safe_str_cmp( master_hash, provided_hash )
def check_password(guess, hashed):
    """
    Check a hashed password. Supports either PBKDF2 if the hash is
    prefixed with that string, or sha1 otherwise.
    """
    if hashed.startswith("PBKDF2"):
        if check_password_PBKDF2(guess, hashed):
            return True
    else:
        # Passwords were originally encoded with sha1 and hexed
        if safe_str_cmp(hashlib.sha1(guess).hexdigest(), hashed):
            return True
    # Password does not match
    return False
Example #6
0
def check_password(guess, hashed):
    """
    Check a hashed password. Supports either PBKDF2 if the hash is
    prefixed with that string, or sha1 otherwise.
    """
    if hashed.startswith("PBKDF2"):
        if check_password_PBKDF2(guess, hashed):
            return True
    else:
        # Passwords were originally encoded with sha1 and hexed
        if safe_str_cmp(hashlib.sha1(smart_str(guess)).hexdigest(), hashed):
            return True
    # Password does not match
    return False
Example #7
0
def check_password_PBKDF2(guess, hashed):
    # Split the database representation to extract cost_factor and salt
    name, hash_function, cost_factor, salt, encoded_original = hashed.split('$', 5)
    if six.PY3:
        guess = bytes(guess, 'utf-8')
        salt = bytes(salt, 'utf-8')
    else:
        guess = smart_str(guess)
    hashed_guess = pbkdf2_bin(guess, salt, int(cost_factor), KEY_LENGTH, getattr(hashlib, hash_function))
    # Hash the guess using the same parameters
    hashed_guess = pbkdf2_bin(smart_str(guess), salt, int(cost_factor), KEY_LENGTH, getattr(hashlib, hash_function))
    encoded_guess = b64encode(hashed_guess)
    if six.PY3:
        encoded_guess = encoded_guess.decode('utf-8')
    return safe_str_cmp(encoded_original, encoded_guess)
Example #8
0
    def __authorize_job_access(self, trans, encoded_job_id, **kwargs):
        for key in [ "path", "job_key" ]:
            if key not in kwargs:
                error_message = "Job files action requires a valid '%s'." % key
                raise exceptions.ObjectAttributeMissingException( error_message )

        job_id = trans.security.decode_id( encoded_job_id )
        job_key = trans.security.encode_id( job_id, kind="jobs_files" )
        if not util.safe_str_cmp( kwargs[ "job_key" ], job_key ):
            raise exceptions.ItemAccessibilityException("Invalid job_key supplied.")

        # Verify job is active. Don't update the contents of complete jobs.
        job = trans.sa_session.query( model.Job ).get( job_id )
        if job.finished:
            error_message = "Attempting to read or modify the files of a job that has already completed."
            raise exceptions.ItemAccessibilityException( error_message )
        return job
Example #9
0
    def __authorize_job_access(self, trans, encoded_job_id, **kwargs):
        for key in ["path", "job_key"]:
            if key not in kwargs:
                error_message = f"Job files action requires a valid '{key}'."
                raise exceptions.ObjectAttributeMissingException(error_message)

        job_id = trans.security.decode_id(encoded_job_id)
        job_key = trans.security.encode_id(job_id, kind="jobs_files")
        if not util.safe_str_cmp(kwargs["job_key"], job_key):
            raise exceptions.ItemAccessibilityException("Invalid job_key supplied.")

        # Verify job is active. Don't update the contents of complete jobs.
        job = trans.sa_session.query(model.Job).get(job_id)
        if job.finished:
            error_message = "Attempting to read or modify the files of a job that has already completed."
            raise exceptions.ItemAccessibilityException(error_message)
        return job
Example #10
0
    def __authorize_job_access(self, encoded_job_id, **kwargs):
        key = "job_key"
        if key not in kwargs:
            error_message = "Job files action requires a valid '%s'." % key
            raise exceptions.ObjectAttributeMissingException(error_message)

        job_id = self._security.decode_id(encoded_job_id)
        job_key = self._security.encode_id(job_id, kind="jobs_files")
        if not util.safe_str_cmp(kwargs["job_key"], job_key):
            raise exceptions.ItemAccessibilityException("Invalid job_key supplied.")

        # Verify job is active. Don't update the contents of complete jobs.
        sa_session = self._app.model.context.current
        job = sa_session.query(model.Job).get(job_id)
        if not job.running:
            error_message = "Attempting to read or modify the files of a job that has already completed."
            raise exceptions.ItemAccessibilityException(error_message)
        return job
Example #11
0
    def __call__(self, environ, start_response):
        # Allow display servers
        if self.display_servers and 'REMOTE_ADDR' in environ:
            try:
                host = socket.gethostbyaddr(environ['REMOTE_ADDR'])[0]
            except (socket.error, socket.herror, socket.gaierror,
                    socket.timeout):
                # in the event of a lookup failure, deny access
                host = None
            if host in self.display_servers:
                environ[
                    self.remote_user_header] = 'remote_display_server@%s' % (
                        self.maildomain or 'example.org')
                return self.app(environ, start_response)
        # Apache sets REMOTE_USER to the string '(null)' when using the
        # Rewrite* method for passing REMOTE_USER and a user is
        # un-authenticated.  Any other possible values need to go here as well.
        path_info = environ.get('PATH_INFO', '')

        # If the secret header is enabled, we expect upstream to send along some key
        # in HTTP_GX_SECRET, so we'll need to compare that here to the correct value
        #
        # This is not an ideal location for this function.  The reason being
        # that because this check is done BEFORE the REMOTE_USER check,  it is
        # possible to attack the GX_SECRET key without having correct
        # credentials. However, that's why it's not "ideal", but it is "good
        # enough". The only users able to exploit this are ones with access to
        # the local system (unless Galaxy is listening on 0.0.0.0....). It
        # seems improbable that an attacker with access to the server hosting
        # Galaxy would not have access to Galaxy itself, and be attempting to
        # attack the system
        if self.config_secret_header is not None:
            if environ.get('HTTP_GX_SECRET') is None:
                title = "Access to Galaxy is denied"
                message = """
                Galaxy is configured to authenticate users via an external
                method (such as HTTP authentication in Apache), but
                no shared secret key was provided by the
                upstream (proxy) server.</p>
                <p>Please contact your local Galaxy administrator.  The
                variable <code>remote_user_secret</code> and
                <code>GX_SECRET</code> header must be set before you may
                access Galaxy.
                """
                return self.error(start_response, title, message)

            if not safe_str_cmp(environ.get('HTTP_GX_SECRET'),
                                self.config_secret_header):
                title = "Access to Galaxy is denied"
                message = """
                Galaxy is configured to authenticate users via an external
                method (such as HTTP authentication in Apache), but an
                incorrect shared secret key was provided by the
                upstream (proxy) server.</p>
                <p>Please contact your local Galaxy administrator.  The
                variable <code>remote_user_secret</code> and
                <code>GX_SECRET</code> header must be set before you may
                access Galaxy.
                """
                return self.error(start_response, title, message)

        if not environ.get(self.remote_user_header,
                           '(null)').startswith('(null)'):
            if not environ[self.remote_user_header].count('@'):
                if self.maildomain is not None:
                    environ[self.remote_user_header] += '@' + self.maildomain
                else:
                    title = "Access to Galaxy is denied"
                    message = """
                        Galaxy is configured to authenticate users via an external
                        method (such as HTTP authentication in Apache), but only a
                        username (not an email address) was provided by the
                        upstream (proxy) server.  Since Galaxy usernames are email
                        addresses, a default mail domain must be set.</p>
                        <p>Please contact your local Galaxy administrator.  The
                        variable <code>remote_user_maildomain</code> must be set
                        before you may access Galaxy.
                    """
                    return self.error(start_response, title, message)

            user_accessible_paths = (
                '/user/api_keys',
                '/user/edit_username',
                '/user/dbkeys',
                '/user/toolbox_filters',
                '/user/set_default_permissions',
            )

            admin_accessible_paths = (
                '/user/create',
                '/user/logout',
                '/user/manage_user_info',
                '/user/edit_info',
                '/userskeys/all_users',
            )

            if not path_info.startswith('/user'):
                # shortcut the following whitelist for non-user-controller
                # requests.
                pass
            elif environ[self.remote_user_header] in self.admin_users and \
                    any([path_info.startswith(prefix) for prefix in admin_accessible_paths]):
                # If the user is an admin user, and any of the admin accessible paths match..., allow them to execute that action.
                pass
            elif any([
                    path_info.startswith(prefix)
                    for prefix in user_accessible_paths
            ]):
                # If the user is allowed to access the path, pass
                pass
            elif path_info == '/user' or path_info == '/user/':
                pass  # We do allow access to the root user preferences page.
            elif path_info.startswith('/user'):
                # Any other endpoint in the user controller is off limits
                title = "Access to Galaxy user controls is disabled"
                message = """
                    User controls are disabled when Galaxy is configured
                    for external authentication.
                """
                return self.error(start_response, title, message)
            return self.app(environ, start_response)
        elif path_info.startswith('/api/'):
            # The API handles its own authentication via keys
            return self.app(environ, start_response)
        else:
            log.debug("Unable to identify user.  %s not found" %
                      self.remote_user_header)
            for k, v in environ.iteritems():
                log.debug("%s = %s", k, v)

            title = "Access to Galaxy is denied"
            message = """
                Galaxy is configured to authenticate users via an external
                method (such as HTTP authentication in Apache), but a username
                was not provided by the upstream (proxy) server.  This is
                generally due to a misconfiguration in the upstream server.</p>
                <p>Please contact your local Galaxy administrator.
            """
            return self.error(start_response, title, message)
Example #12
0
    def __call__( self, environ, start_response ):
        # Allow display servers
        if self.display_servers and 'REMOTE_ADDR' in environ:
            try:
                host = socket.gethostbyaddr( environ[ 'REMOTE_ADDR' ] )[0]
            except( socket.error, socket.herror, socket.gaierror, socket.timeout ):
                # in the event of a lookup failure, deny access
                host = None
            if host in self.display_servers:
                environ[ self.remote_user_header ] = 'remote_display_server@%s' % ( self.maildomain or 'example.org' )
                return self.app( environ, start_response )
        # Apache sets REMOTE_USER to the string '(null)' when using the
        # Rewrite* method for passing REMOTE_USER and a user is
        # un-authenticated.  Any other possible values need to go here as well.
        path_info = environ.get('PATH_INFO', '')

        # If the secret header is enabled, we expect upstream to send along some key
        # in HTTP_GX_SECRET, so we'll need to compare that here to the correct value
        #
        # This is not an ideal location for this function.  The reason being
        # that because this check is done BEFORE the REMOTE_USER check,  it is
        # possible to attack the GX_SECRET key without having correct
        # credentials. However, that's why it's not "ideal", but it is "good
        # enough". The only users able to exploit this are ones with access to
        # the local system (unless Galaxy is listening on 0.0.0.0....). It
        # seems improbable that an attacker with access to the server hosting
        # Galaxy would not have access to Galaxy itself, and be attempting to
        # attack the system
        if path_info.startswith( '/api/' ):
            # The API handles its own authentication via keys
            # Check for API key before checking for header
            return self.app( environ, start_response )

        elif self.config_secret_header is not None:
            if environ.get('HTTP_GX_SECRET') is None:
                title = "Access to Galaxy is denied"
                message = """
                Galaxy is configured to authenticate users via an external
                method (such as HTTP authentication in Apache), but
                no shared secret key was provided by the
                upstream (proxy) server.</p>
                <p>Please contact your local Galaxy administrator.  The
                variable <code>remote_user_secret</code> and
                <code>GX_SECRET</code> header must be set before you may
                access Galaxy.
                """
                return self.error( start_response, title, message )

            if not safe_str_cmp(environ.get('HTTP_GX_SECRET', ''), self.config_secret_header):
                title = "Access to Galaxy is denied"
                message = """
                Galaxy is configured to authenticate users via an external
                method (such as HTTP authentication in Apache), but an
                incorrect shared secret key was provided by the
                upstream (proxy) server.</p>
                <p>Please contact your local Galaxy administrator.  The
                variable <code>remote_user_secret</code> and
                <code>GX_SECRET</code> header must be set before you may
                access Galaxy.
                """
                return self.error( start_response, title, message )

        if not environ.get(self.remote_user_header, '(null)').startswith('(null)'):
            if not environ[ self.remote_user_header ].count( '@' ):
                if self.maildomain is not None:
                    environ[ self.remote_user_header ] += '@' + self.maildomain
                else:
                    title = "Access to Galaxy is denied"
                    message = """
                        Galaxy is configured to authenticate users via an external
                        method (such as HTTP authentication in Apache), but only a
                        username (not an email address) was provided by the
                        upstream (proxy) server.  Since Galaxy usernames are email
                        addresses, a default mail domain must be set.</p>
                        <p>Please contact your local Galaxy administrator.  The
                        variable <code>remote_user_maildomain</code> must be set
                        before you may access Galaxy.
                    """
                    return self.error( start_response, title, message )

            user_accessible_paths = (
                '/user/api_keys',
                '/user/edit_username',
                '/user/dbkeys',
                '/user/toolbox_filters',
                '/user/set_default_permissions',
            )

            admin_accessible_paths = (
                '/user/create',
                '/user/logout',
                '/user/manage_user_info',
                '/user/edit_info',
                '/userskeys/all_users',
            )

            if not path_info.startswith('/user'):
                # shortcut the following whitelist for non-user-controller
                # requests.
                pass
            elif environ[self.remote_user_header] in self.admin_users and \
                    any([path_info.startswith(prefix) for prefix in admin_accessible_paths]):
                # If the user is an admin user, and any of the admin accessible paths match..., allow them to execute that action.
                pass
            elif any([path_info.startswith(prefix) for prefix in user_accessible_paths]):
                # If the user is allowed to access the path, pass
                pass
            elif path_info == '/user' or path_info == '/user/':
                pass  # We do allow access to the root user preferences page.
            elif path_info.startswith( '/user' ):
                # Any other endpoint in the user controller is off limits
                title = "Access to Galaxy user controls is disabled"
                message = """
                    User controls are disabled when Galaxy is configured
                    for external authentication.
                """
                return self.error( start_response, title, message )
            return self.app( environ, start_response )
        else:
            log.debug("Unable to identify user.  %s not found" % self.remote_user_header)
            for k, v in environ.iteritems():
                log.debug("%s = %s" , k, v)

            title = "Access to Galaxy is denied"
            message = """
                Galaxy is configured to authenticate users via an external
                method (such as HTTP authentication in Apache), but a username
                was not provided by the upstream (proxy) server.  This is
                generally due to a misconfiguration in the upstream server.</p>
                <p>Please contact your local Galaxy administrator.
            """
            return self.error( start_response, title, message )
Example #13
0
    def __call__(self, environ, start_response):
        environ['webapp'] = 'tool_shed'
        # Allow display servers
        if self.display_servers and 'REMOTE_ADDR' in environ:
            try:
                host = socket.gethostbyaddr(environ['REMOTE_ADDR'])[0]
            except (socket.error, socket.herror, socket.gaierror,
                    socket.timeout):
                # in the event of a lookup failure, deny access
                host = None
            if host in self.display_servers:
                environ['HTTP_REMOTE_USER'] = '******' % (
                    self.maildomain or 'example.org')
                return self.app(environ, start_response)

        # If the secret header is enabled, we expect upstream to send along some key
        # in HTTP_GX_SECRET, so we'll need to compare that here to the correct value
        #
        # This is not an ideal location for this function.  The reason being
        # that because this check is done BEFORE the REMOTE_USER check,  it is
        # possible to attack the GX_SECRET key without having correct
        # credentials. However, that's why it's not "ideal", but it is "good
        # enough". The only users able to exploit this are ones with access to
        # the local system (unless Galaxy is listening on 0.0.0.0....). It
        # seems improbable that an attacker with access to the server hosting
        # Galaxy would not have access to Galaxy itself, and be attempting to
        # attack the system
        if self.config_secret_header is not None:
            if not safe_str_cmp(environ.get('HTTP_GX_SECRET'),
                                self.config_secret_header):
                title = "Access to Galaxy is denied"
                message = """
                Galaxy is configured to authenticate users via an external
                method (such as HTTP authentication in Apache), but an
                incorrect shared secret key was provided by the
                upstream (proxy) server.</p>
                <p>Please contact your local Galaxy administrator.  The
                variable <code>remote_user_secret</code> and
                <code>GX_SECRET</code> header must be set before you may
                access Galaxy.
                """
                return self.error(start_response, title, message)

        # Apache sets REMOTE_USER to the string '(null)' when using the Rewrite* method for passing REMOTE_USER and a user is
        # un-authenticated.  Any other possible values need to go here as well.
        path_info = environ.get('PATH_INFO', '')
        if 'HTTP_REMOTE_USER' in environ and environ[
                'HTTP_REMOTE_USER'] != '(null)':
            if not environ['HTTP_REMOTE_USER'].count('@'):
                if self.maildomain is not None:
                    environ['HTTP_REMOTE_USER'] += '@' + self.maildomain
                else:
                    title = "Access to this Galaxy tool shed is denied"
                    message = """
                        This Galaxy tool shed is configured to authenticate users via an external
                        method (such as HTTP authentication in Apache), but only a username (not
                        an email address) was provided by the upstream (proxy) server.  Since tool
                        shed usernames are email addresses, a default mail domain must be set.</[>
                        <p>The variable <code>remote_user_maildomain</code> must be set before you
                        can access this tool shed.  Contact your local tool shed administrator.
                    """
                    return self.error(start_response, title, message)
            return self.app(environ, start_response)
        elif path_info.startswith('/api/'):
            # The API handles its own authentication via keys
            return self.app(environ, start_response)
        elif path_info.startswith('/user/api_keys'):
            # api_keys can be managed when remote_user is in use.
            pass
        else:
            title = "Access to this Galaxy tool shed is denied"
            message = """
                This Galaxy tool shed is configured to authenticate users via an external
                method (such as HTTP authentication in Apache), but a username was not
                provided by the upstream (proxy) server.  This is generally due to a
                misconfiguration in the upstream server.</p>
                <p>Contact your local Galaxy tool shed administrator.
            """
            return self.error(start_response, title, message)
Example #14
0
    def __call__( self, environ, start_response ):
        environ[ 'webapp' ] = 'tool_shed'
        # Allow display servers
        if self.display_servers and environ.has_key( 'REMOTE_ADDR' ):
            try:
                host = socket.gethostbyaddr( environ[ 'REMOTE_ADDR' ] )[0]
            except( socket.error, socket.herror, socket.gaierror, socket.timeout ):
                # in the event of a lookup failure, deny access
                host = None
            if host in self.display_servers:
                environ[ 'HTTP_REMOTE_USER' ] = 'remote_display_server@%s' % ( self.maildomain or 'example.org' )
                return self.app( environ, start_response )

        # If the secret header is enabled, we expect upstream to send along some key
        # in HTTP_GX_SECRET, so we'll need to compare that here to the correct value
        # 
        # This is not an ideal location for this function.  The reason being
        # that because this check is done BEFORE the REMOTE_USER check,  it is
        # possible to attack the GX_SECRET key without having correct
        # credentials. However, that's why it's not "ideal", but it is "good
        # enough". The only users able to exploit this are ones with access to
        # the local system (unless Galaxy is listening on 0.0.0.0....). It
        # seems improbable that an attacker with access to the server hosting
        # Galaxy would not have access to Galaxy itself, and be attempting to
        # attack the system
        if self.config_secret_header is not None:
            if not safe_str_cmp(environ.get('HTTP_GX_SECRET'), self.config_secret_header):
                title = "Access to Galaxy is denied"
                message = """
                Galaxy is configured to authenticate users via an external
                method (such as HTTP authentication in Apache), but an 
                incorrect shared secret key was provided by the 
                upstream (proxy) server.</p>
                <p>Please contact your local Galaxy administrator.  The
                variable <code>remote_user_secret</code> and
                <code>GX_SECRET</code> header must be set before you may
                access Galaxy.
                """
                return self.error( start_response, title, message )

        # Apache sets REMOTE_USER to the string '(null)' when using the Rewrite* method for passing REMOTE_USER and a user is
        # un-authenticated.  Any other possible values need to go here as well.
        path_info = environ.get('PATH_INFO', '')
        if environ.has_key( 'HTTP_REMOTE_USER' ) and environ[ 'HTTP_REMOTE_USER' ] != '(null)':
            if not environ[ 'HTTP_REMOTE_USER' ].count( '@' ):
                if self.maildomain is not None:
                    environ[ 'HTTP_REMOTE_USER' ] += '@' + self.maildomain
                else:
                    title = "Access to this Galaxy tool shed is denied"
                    message = """
                        This Galaxy tool shed is configured to authenticate users via an external
                        method (such as HTTP authentication in Apache), but only a username (not
                        an email address) was provided by the upstream (proxy) server.  Since tool
                        shed usernames are email addresses, a default mail domain must be set.</[>
                        <p>The variable <code>remote_user_maildomain</code> must be set before you
                        can access this tool shed.  Contact your local tool shed administrator.
                    """
                    return self.error( start_response, title, message )
            return self.app( environ, start_response )
        elif path_info.startswith( '/api/' ):
            # The API handles its own authentication via keys
            return self.app( environ, start_response )
        elif path_info.startswith( '/user/api_keys' ):
            # api_keys can be managed when remote_user is in use.
            pass
        else:
            title = "Access to this Galaxy tool shed is denied"
            message = """
                This Galaxy tool shed is configured to authenticate users via an external
                method (such as HTTP authentication in Apache), but a username was not
                provided by the upstream (proxy) server.  This is generally due to a
                misconfiguration in the upstream server.</p>
                <p>Contact your local Galaxy tool shed administrator.
            """
            return self.error( start_response, title, message )