1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/21ec1290ad02/
changeset: 21ec1290ad02
user: inithello
date: 2012-06-27 15:47:32
summary: Added remote authentication to the toolshed.
affected #: 3 files
diff -r 9576fb8cd71e8ad16e9a6426c0c42dc6aba63716 -r 21ec1290ad02beb9c33516dcc4d5d4a732559414 lib/galaxy/web/framework/__init__.py
--- a/lib/galaxy/web/framework/__init__.py
+++ b/lib/galaxy/web/framework/__init__.py
@@ -498,8 +498,10 @@
# remote user authenticates, we'll look for this information, and if missing, create it.
if not self.app.security_agent.get_private_user_role( user ):
self.app.security_agent.create_private_user_role( user )
- if not user.default_permissions:
- self.app.security_agent.user_set_default_permissions( user, history=True, dataset=True )
+ if 'webapp' not in self.environ or self.environ['webapp'] != 'community':
+ if not user.default_permissions:
+ self.app.security_agent.user_set_default_permissions( user )
+ self.app.security_agent.user_set_default_permissions( user, history=True, dataset=True )
elif user is None:
username = remote_user_email.split( '@', 1 )[0].lower()
random.seed()
@@ -520,7 +522,8 @@
self.sa_session.flush()
self.app.security_agent.create_private_user_role( user )
# We set default user permissions, before we log in and set the default history permissions
- self.app.security_agent.user_set_default_permissions( user )
+ if 'webapp' not in self.environ or self.environ['webapp'] != 'community':
+ self.app.security_agent.user_set_default_permissions( user )
#self.log_event( "Automatically created account '%s'", user.email )
return user
def __update_session_cookie( self, name='galaxysession' ):
diff -r 9576fb8cd71e8ad16e9a6426c0c42dc6aba63716 -r 21ec1290ad02beb9c33516dcc4d5d4a732559414 lib/galaxy/webapps/community/buildapp.py
--- a/lib/galaxy/webapps/community/buildapp.py
+++ b/lib/galaxy/webapps/community/buildapp.py
@@ -17,6 +17,7 @@
import galaxy.webapps.community.model.mapping
import galaxy.web.framework
from galaxy.webapps.community.framework.middleware import hg
+from galaxy import util
log = logging.getLogger( __name__ )
@@ -102,6 +103,15 @@
# other middleware):
app = httpexceptions.make_middleware( app, conf )
log.debug( "Enabling 'httpexceptions' middleware" )
+ # If we're using remote_user authentication, add middleware that
+ # protects Galaxy from improperly configured authentication in the
+ # upstream server
+ if asbool(conf.get( 'use_remote_user', False )):
+ from galaxy.webapps.community.framework.middleware.remoteuser import RemoteUser
+ app = RemoteUser( app, maildomain = conf.get( 'remote_user_maildomain', None ),
+ display_servers = util.listify( conf.get( 'display_servers', '' ) ),
+ admin_users = conf.get( 'admin_users', '' ).split( ',' ) )
+ log.debug( "Enabling 'remote user' middleware" )
# The recursive middleware allows for including requests in other
# requests or forwarding of requests, all on the server side.
if asbool(conf.get('use_recursive', True)):
diff -r 9576fb8cd71e8ad16e9a6426c0c42dc6aba63716 -r 21ec1290ad02beb9c33516dcc4d5d4a732559414 lib/galaxy/webapps/community/framework/middleware/remoteuser.py
--- /dev/null
+++ b/lib/galaxy/webapps/community/framework/middleware/remoteuser.py
@@ -0,0 +1,93 @@
+"""
+Middleware for handling $REMOTE_USER if use_remote_user is enabled.
+"""
+
+import socket
+
+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>
+"""
+
+class RemoteUser( object ):
+ def __init__( self, app, maildomain=None, display_servers=None, admin_users=None ):
+ self.app = app
+ self.maildomain = maildomain
+ self.display_servers = display_servers or []
+ self.admin_users = admin_users or []
+ def __call__( self, environ, start_response ):
+ environ[ 'webapp' ] = 'community'
+ # 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 )
+ # 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 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 )
+ 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:
+ 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 )
+ def error( self, start_response, title="Access denied", message="Please contact your local Galaxy administrator." ):
+ start_response( '403 Forbidden', [('Content-type', 'text/html')] )
+ return [errorpage % (title, message)]
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/9576fb8cd71e/
changeset: 9576fb8cd71e
user: scot...(a)gatech.edu
date: 2012-06-26 23:31:30
summary: Generalized exit code and regex handling. TaskWrapper now uses the newly-generalized handling, too.
affected #: 2 files
diff -r fb67f73df9a24cb9eba278e7c62ba996fa11a6be -r 9576fb8cd71e8ad16e9a6426c0c42dc6aba63716 lib/galaxy/jobs/__init__.py
--- a/lib/galaxy/jobs/__init__.py
+++ b/lib/galaxy/jobs/__init__.py
@@ -306,78 +306,12 @@
#ERROR at this point means the job was deleted by an administrator.
return self.fail( job.info )
- err_msg = ""
- # Check exit codes and match regular expressions against stdout and
- # stderr if this tool was configured to do so.
- if ( len( self.tool.stdio_regexes ) > 0 or
- len( self.tool.stdio_exit_codes ) > 0 ):
- # We will check the exit code ranges in the order in which
- # they were specified. Each exit_code is a ToolStdioExitCode
- # that includes an applicable range. If the exit code was in
- # that range, then apply the error level and add in a message.
- # If we've reached a fatal error rule, then stop.
- max_error_level = galaxy.tools.StdioErrorLevel.NO_ERROR
- for stdio_exit_code in self.tool.stdio_exit_codes:
- if ( tool_exit_code >= stdio_exit_code.range_start and
- tool_exit_code <= stdio_exit_code.range_end ):
- if None != stdio_exit_code.desc:
- err_msg += stdio_exit_code.desc
- # TODO: Find somewhere to stick the err_msg - possibly to
- # the source (stderr/stdout), possibly in a new db column.
- max_error_level = max( max_error_level,
- stdio_exit_code.error_level )
- if max_error_level >= galaxy.tools.StdioErrorLevel.FATAL:
- break
- # If there is a regular expression for scanning stdout/stderr,
- # then we assume that the tool writer overwrote the default
- # behavior of just setting an error if there is *anything* on
- # stderr.
- if max_error_level < galaxy.tools.StdioErrorLevel.FATAL:
- # We'll examine every regex. Each regex specifies whether
- # it is to be run on stdout, stderr, or both. (It is
- # possible for neither stdout nor stderr to be scanned,
- # but those won't be scanned.) We record the highest
- # error level, which are currently "warning" and "fatal".
- # If fatal, then we set the job's state to ERROR.
- # If warning, then we still set the job's state to OK
- # but include a message. We'll do this if we haven't seen
- # a fatal error yet
- for regex in self.tool.stdio_regexes:
- # If ( this regex should be matched against stdout )
- # - Run the regex's match pattern against stdout
- # - If it matched, then determine the error level.
- # o If it was fatal, then we're done - break.
- # Repeat the stdout stuff for stderr.
- # TODO: Collapse this into a single function.
- if ( regex.stdout_match ):
- regex_match = re.search( regex.match, stdout )
- if ( regex_match ):
- err_msg += self.regex_err_msg( regex_match, regex )
- max_error_level = max( max_error_level, regex.error_level )
- if max_error_level >= galaxy.tools.StdioErrorLevel.FATAL:
- break
- if ( regex.stderr_match ):
- regex_match = re.search( regex.match, stderr )
- if ( regex_match ):
- err_msg += self.regex_err_msg( regex_match, regex )
- max_error_level = max( max_error_level,
- regex.error_level )
- if max_error_level >= galaxy.tools.StdioErrorLevel.FATAL:
- break
- # If we encountered a fatal error, then we'll need to set the
- # job state accordingly. Otherwise the job is ok:
- if max_error_level >= galaxy.tools.StdioErrorLevel.FATAL:
- job.state = job.states.ERROR
- else:
- job.state = job.states.OK
- # When there are no regular expressions and no exit codes to check,
- # default to the previous behavior: when there's anything on stderr
- # the job has an error, and the job is ok otherwise.
+ # Check the
+ if ( self.check_tool_output( stdout, stderr, tool_exit_code ) ):
+ job.state = job.states.OK
else:
- if stderr:
- job.state = job.states.ERROR
- else:
- job.state = job.states.OK
+ job.state = job.states.ERROR
+
if self.version_string_cmd:
version_filename = self.get_version_string_path()
if os.path.exists(version_filename):
@@ -566,6 +500,107 @@
if self.app.config.cleanup_job == 'always' or ( not stderr and self.app.config.cleanup_job == 'onsuccess' ):
self.cleanup()
+ def check_tool_output( self, stdout, stderr, tool_exit_code ):
+ """
+ Check the output of a tool - given the stdout, stderr, and the tool's
+ exit code, return True if the tool exited succesfully and False
+ otherwise. No exceptions should be thrown. If this code encounters
+ an exception, it returns True so that the workflow can continue;
+ otherwise, a bug in this code could halt workflow progress.
+ Note that, if the tool did not define any exit code handling or
+ any stdio/stderr handling, then it reverts back to previous behavior:
+ if stderr contains anything, then False is returned.
+ """
+ job = self.get_job()
+ err_msg = ""
+ # By default, the tool succeeded. This covers the case where the code
+ # has a bug but the tool was ok, and it lets a workflow continue.
+ success = True
+
+ try:
+ # Check exit codes and match regular expressions against stdout and
+ # stderr if this tool was configured to do so.
+ if ( len( self.tool.stdio_regexes ) > 0 or
+ len( self.tool.stdio_exit_codes ) > 0 ):
+ # We will check the exit code ranges in the order in which
+ # they were specified. Each exit_code is a ToolStdioExitCode
+ # that includes an applicable range. If the exit code was in
+ # that range, then apply the error level and add in a message.
+ # If we've reached a fatal error rule, then stop.
+ max_error_level = galaxy.tools.StdioErrorLevel.NO_ERROR
+ for stdio_exit_code in self.tool.stdio_exit_codes:
+ if ( tool_exit_code >= stdio_exit_code.range_start and
+ tool_exit_code <= stdio_exit_code.range_end ):
+ if None != stdio_exit_code.desc:
+ err_msg += stdio_exit_code.desc
+ # TODO: Find somewhere to stick the err_msg - possibly to
+ # the source (stderr/stdout), possibly in a new db column.
+ max_error_level = max( max_error_level,
+ stdio_exit_code.error_level )
+ if max_error_level >= galaxy.tools.StdioErrorLevel.FATAL:
+ break
+
+ # If there is a regular expression for scanning stdout/stderr,
+ # then we assume that the tool writer overwrote the default
+ # behavior of just setting an error if there is *anything* on
+ # stderr.
+ if max_error_level < galaxy.tools.StdioErrorLevel.FATAL:
+ # We'll examine every regex. Each regex specifies whether
+ # it is to be run on stdout, stderr, or both. (It is
+ # possible for neither stdout nor stderr to be scanned,
+ # but those won't be scanned.) We record the highest
+ # error level, which are currently "warning" and "fatal".
+ # If fatal, then we set the job's state to ERROR.
+ # If warning, then we still set the job's state to OK
+ # but include a message. We'll do this if we haven't seen
+ # a fatal error yet
+ for regex in self.tool.stdio_regexes:
+ # If ( this regex should be matched against stdout )
+ # - Run the regex's match pattern against stdout
+ # - If it matched, then determine the error level.
+ # o If it was fatal, then we're done - break.
+ # Repeat the stdout stuff for stderr.
+ # TODO: Collapse this into a single function.
+ if ( regex.stdout_match ):
+ regex_match = re.search( regex.match, stdout )
+ if ( regex_match ):
+ err_msg += self.regex_err_msg( regex_match, regex )
+ max_error_level = max( max_error_level, regex.error_level )
+ if max_error_level >= galaxy.tools.StdioErrorLevel.FATAL:
+ break
+ if ( regex.stderr_match ):
+ regex_match = re.search( regex.match, stderr )
+ if ( regex_match ):
+ err_msg += self.regex_err_msg( regex_match, regex )
+ max_error_level = max( max_error_level,
+ regex.error_level )
+ if max_error_level >= galaxy.tools.StdioErrorLevel.FATAL:
+ break
+
+ # If we encountered a fatal error, then we'll need to set the
+ # job state accordingly. Otherwise the job is ok:
+ if max_error_level >= galaxy.tools.StdioErrorLevel.FATAL:
+ success = False
+ else:
+ success = True
+
+ # When there are no regular expressions and no exit codes to check,
+ # default to the previous behavior: when there's anything on stderr
+ # the job has an error, and the job is ok otherwise.
+ else:
+ log.debug( "The tool did not define exit code or stdio handling; "
+ + "checking stderr for success" )
+ if stderr:
+ success = False
+ else:
+ success = True
+ # On any exception, return True.
+ except:
+ log.warning( "Tool check encountered unexpected exception; "
+ + "assuming tool was successful" )
+ success = True
+ return success
+
def regex_err_msg( self, match, regex ):
"""
Return a message about the match on tool output using the given
@@ -970,7 +1005,7 @@
self.sa_session.add( task )
self.sa_session.flush()
- def finish( self, stdout, stderr ):
+ def finish( self, stdout, stderr, tool_exit_code=0 ):
# DBTODO integrate previous finish logic.
# Simple finish for tasks. Just set the flag OK.
log.debug( 'task %s for job %d ended' % (self.task_id, self.job_id) )
@@ -991,10 +1026,10 @@
# Job was deleted by an administrator
self.fail( task.info )
return
- if stderr:
+ if ( self.check_tool_output( stdout, stderr, tool_exit_code ) ):
+ task.state = task.states.OK
+ else:
task.state = task.states.ERROR
- else:
- task.state = task.states.OK
# Save stdout and stderr
if len( stdout ) > 32768:
log.error( "stdout for task %d is greater than 32K, only first part will be logged to database" % task.id )
diff -r fb67f73df9a24cb9eba278e7c62ba996fa11a6be -r 9576fb8cd71e8ad16e9a6426c0c42dc6aba63716 lib/galaxy/jobs/runners/pbs.py
--- a/lib/galaxy/jobs/runners/pbs.py
+++ b/lib/galaxy/jobs/runners/pbs.py
@@ -560,7 +560,7 @@
def fail_job( self, pbs_job_state ):
"""
- Seperated out so we can use the worker threads for it.
+ Separated out so we can use the worker threads for it.
"""
if pbs_job_state.stop_job:
self.stop_job( self.sa_session.query( self.app.model.Job ).get( pbs_job_state.job_wrapper.job_id ) )
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.