Warning
This document is for an in-development version of Galaxy. You can alternatively view this page in the latest release if it exists or view the top of the latest release's documentation.
Source code for galaxy.webapps.tool_shed.framework.middleware.remoteuser
"""
Middleware for handling $REMOTE_USER if use_remote_user is enabled.
"""
import socket
from galaxy.util import safe_str_cmp
errorpage = """
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html lang="en">
    <head>
        <title>Galaxy</title>
        <style type="text/css">
        body {
            min-width: 500px;
            text-align: center;
        }
        .errormessage {
            font: 75%% verdana, "Bitstream Vera Sans", geneva, arial, helvetica, helve, sans-serif;
            padding: 10px;
            margin: 100px auto;
            min-height: 32px;
            max-width: 500px;
            border: 1px solid #AA6666;
            background-color: #FFCCCC;
            text-align: left;
        }
        </style>
    </head>
    <body>
        <div class="errormessage">
            <h4>%s</h4>
            <p>%s</p>
        </div>
    </body>
</html>
"""
[docs]class RemoteUser(object):
[docs]    def __init__(self, app, maildomain=None, display_servers=None, admin_users=None, remote_user_secret_header=None):
        self.app = app
        self.maildomain = maildomain
        self.display_servers = display_servers or []
        self.admin_users = admin_users or []
        self.config_secret_header = remote_user_secret_header
    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'] = '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 '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)
[docs]    def error(self, start_response, title="Access denied", message="Contact your local Galaxy tool shed administrator."):
        start_response('403 Forbidden', [('Content-type', 'text/html')])
        return [errorpage % (title, message)]