diff --git a/lib/galaxy/model/mapping.py b/lib/galaxy/model/mapping.py
index 3d5af25b96e..c1fde7915ab 100644
--- a/lib/galaxy/model/mapping.py
+++ b/lib/galaxy/model/mapping.py
@@ -76,6 +76,7 @@ UserOpenID.table = Table( "galaxy_user_openid", metadata,
Column( "session_id", Integer, ForeignKey( "galaxy_session.id" ), index=True ),
Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ),
Column( "openid", TEXT, index=True, unique=True ),
+ Column( "provider", TrimmedString( 255 ) ),
)
History.table = Table( "history", metadata,
diff --git a/lib/galaxy/model/migrate/versions/0096_openid_provider.py b/lib/galaxy/model/migrate/versions/0096_openid_provider.py
new file mode 100644
index 00000000000..b839fef1635
--- /dev/null
+++ b/lib/galaxy/model/migrate/versions/0096_openid_provider.py
@@ -0,0 +1,45 @@
+"""
+Migration script to add column to openid table for provider.
+Remove any OpenID entries with nonunique GenomeSpace Identifier
+"""
+
+BAD_IDENTIFIER = 'https://identity.genomespace.org/identityServer/xrd.jsp'
+from sqlalchemy import *
+from sqlalchemy.orm import *
+from migrate import *
+from migrate.changeset import *
+from galaxy.model.custom_types import TrimmedString
+
+import logging
+log = logging.getLogger( __name__ )
+
+metadata = MetaData( migrate_engine )
+db_session = scoped_session( sessionmaker( bind=migrate_engine, autoflush=False, autocommit=True ) )
+
+def upgrade():
+ print __doc__
+ metadata.reflect()
+
+ try:
+ OpenID_table = Table( "galaxy_user_openid", metadata, autoload=True )
+ c = Column( "provider", TrimmedString( 255 ) )
+ c.create( OpenID_table )
+ assert c is OpenID_table.c.provider
+ except Exception, e:
+ print "Adding provider column to galaxy_user_openid table failed: %s" % str( e )
+ log.debug( "Adding provider column to galaxy_user_openid table failed: %s" % str( e ) )
+
+ try:
+ cmd = "DELETE FROM galaxy_user_openid WHERE openid='%s'" % ( BAD_IDENTIFIER )
+ db_session.execute( cmd )
+ except Exception, e:
+ log.debug( "Deleting bad Identifiers from galaxy_user_openid failed: %s" % str( e ) )
+
+def downgrade():
+ metadata.reflect()
+ try:
+ OpenID_table = Table( "galaxy_user_openid", metadata, autoload=True )
+ OpenID_table.c.provider.drop()
+ except Exception, e:
+ print "Dropping provider column from galaxy_user_openid table failed: %s" % str( e )
+ log.debug( "Dropping provider column from galaxy_user_openid table failed: %s" % str( e ) )
\ No newline at end of file
diff --git a/lib/galaxy/openid/providers.py b/lib/galaxy/openid/providers.py
index b31f0073a10..7e14eafb366 100644
--- a/lib/galaxy/openid/providers.py
+++ b/lib/galaxy/openid/providers.py
@@ -9,6 +9,9 @@ from galaxy.util.odict import odict
log = logging.getLogger( __name__ )
+NO_PROVIDER_ID = 'None'
+RESERVED_PROVIDER_IDS = [ NO_PROVIDER_ID ]
+
class OpenIDProvider( object ):
'''An OpenID Provider object.'''
@classmethod
@@ -22,7 +25,9 @@ class OpenIDProvider( object ):
op_endpoint_url = provider_elem.find( 'op_endpoint_url' )
if op_endpoint_url is not None:
op_endpoint_url = op_endpoint_url.text
+ never_associate_with_user = string_as_bool( provider_elem.get( 'never_associate_with_user', 'False' ) )
assert (provider_id and provider_name and op_endpoint_url), Exception( "OpenID Provider improperly configured" )
+ assert provider_id not in RESERVED_PROVIDER_IDS, Exception( 'Specified OpenID Provider uses a reserved id: %s' % ( provider_id ) )
sreg_required = []
sreg_optional = []
use_for = {}
@@ -45,8 +50,8 @@ class OpenIDProvider( object ):
sreg_required = None
sreg_optional = None
use_for = None
- return cls( provider_id, provider_name, op_endpoint_url, sreg_required, sreg_optional, use_for, store_user_preference )
- def __init__( self, id, name, op_endpoint_url, sreg_required=None, sreg_optional=None, use_for=None, store_user_preference=None ):
+ return cls( provider_id, provider_name, op_endpoint_url, sreg_required=sreg_required, sreg_optional=sreg_optional, use_for=use_for, store_user_preference=store_user_preference, never_associate_with_user=never_associate_with_user )
+ def __init__( self, id, name, op_endpoint_url, sreg_required=None, sreg_optional=None, use_for=None, store_user_preference=None, never_associate_with_user=None ):
'''When sreg options are not specified, defaults are used.'''
self.id = id
self.name = name
@@ -71,6 +76,10 @@ class OpenIDProvider( object ):
self.store_user_preference = store_user_preference
else:
self.store_user_preference = {}
+ if never_associate_with_user:
+ self.never_associate_with_user = True
+ else:
+ self.never_associate_with_user = False
def post_authentication( self, trans, openid_manager, info ):
sreg_attributes = openid_manager.get_sreg( info )
for store_pref_name, store_pref_value_name in self.store_user_preference.iteritems():
@@ -80,9 +89,12 @@ class OpenIDProvider( object ):
raise Exception( 'Only sreg is currently supported.' )
trans.sa_session.add( trans.user )
trans.sa_session.flush()
+ def has_post_authentication_actions( self ):
+ return bool( self.store_user_preference )
class OpenIDProviders( object ):
'''Collection of OpenID Providers'''
+ NO_PROVIDER_ID = NO_PROVIDER_ID
@classmethod
def from_file( cls, filename ):
try:
@@ -107,6 +119,7 @@ class OpenIDProviders( object ):
self.providers = providers
else:
self.providers = odict()
+ self._banned_identifiers = [ provider.op_endpoint_url for provider in self.providers.itervalues() if provider.never_associate_with_user ]
def __iter__( self ):
for provider in self.providers.itervalues():
yield provider
@@ -115,3 +128,5 @@ class OpenIDProviders( object ):
return self.providers[ name ]
else:
return default
+ def new_provider_from_identifier( self, identifier ):
+ return OpenIDProvider( None, identifier, identifier, never_associate_with_user = identifier in self._banned_identifiers )
diff --git a/lib/galaxy/web/controllers/tool_runner.py b/lib/galaxy/web/controllers/tool_runner.py
index 5a78ca3ab59..b43e7472e44 100644
--- a/lib/galaxy/web/controllers/tool_runner.py
+++ b/lib/galaxy/web/controllers/tool_runner.py
@@ -59,7 +59,7 @@ class ToolRunner( BaseUIController ):
trans.log_event( "Tool id '%s' does not exist" % tool_id )
return "Tool '%s' does not exist, kwd=%s " % (tool_id, kwd)
if tool.require_login and not trans.user:
- return trans.response.send_redirect( url_for( controller='user', action='login', cntrller='user', message="You must be logged in to use this tool.", status="info", referer=url_for( controller='/tool_runner', action='index', tool_id=tool_id, **kwd ) ) )
+ return trans.response.send_redirect( url_for( controller='user', action='login', cntrller='user', message="You must be logged in to use this tool.", status="info", redirect=url_for( controller='/tool_runner', action='index', tool_id=tool_id, **kwd ) ) )
params = util.Params( kwd, sanitize = False ) #Sanitize parameters when substituting into command line via input wrappers
#do param translation here, used by datasource tools
if tool.input_translator:
diff --git a/lib/galaxy/web/controllers/user.py b/lib/galaxy/web/controllers/user.py
index 3e3a11fdde9..00ffb9d833b 100644
--- a/lib/galaxy/web/controllers/user.py
+++ b/lib/galaxy/web/controllers/user.py
@@ -11,7 +11,6 @@ from galaxy.web.form_builder import *
from galaxy.util.json import from_json_string, to_json_string
from galaxy.web.framework.helpers import iff
from galaxy.security.validate_user_input import validate_email, validate_publicname, validate_password
-from galaxy.openid.providers import OpenIDProvider
log = logging.getLogger( __name__ )
@@ -30,7 +29,7 @@ class UserOpenIDGrid( grids.Grid ):
default_filter = { "openid" : "All" }
default_sort_key = "-create_time"
columns = [
- grids.TextColumn( "OpenID URL", key="openid" ),
+ grids.TextColumn( "OpenID URL", key="openid", link=( lambda x: dict( operation='openid_auth', login_button="Login", openid_url=x.openid if not x.provider else '', openid_provider=x.provider, auto_associate=True ) ) ),
grids.GridColumn( "Created", key="create_time", format=time_ago ),
]
operations = [
@@ -48,32 +47,30 @@ class User( BaseUIController, UsesFormDefinitions ):
return trans.fill_template( '/user/index.mako', cntrller=cntrller, webapp=webapp )
@web.expose
def openid_auth( self, trans, webapp='galaxy', **kwd ):
+ '''Handles user request to access an OpenID provider'''
if not trans.app.config.enable_openid:
return trans.show_error_message( 'OpenID authentication is not enabled in this instance of Galaxy' )
message = 'Unspecified failure authenticating via OpenID'
status = kwd.get( 'status', 'done' )
openid_url = kwd.get( 'openid_url', '' )
openid_provider = kwd.get( 'openid_provider', '' )
- referer = kwd.get( 'referer', trans.request.referer )
+ if not openid_provider or openid_url:
+ openid_provider = trans.app.openid_providers.NO_PROVIDER_ID #empty fields cause validation errors
+ redirect = kwd.get( 'redirect', '' )
auto_associate = util.string_as_bool( kwd.get( 'auto_associate', False ) )
use_panels = util.string_as_bool( kwd.get( 'use_panels', False ) )
action = 'login'
- if auto_associate:
- action = 'openid_manage'
- if not referer:
- referer = url_for( '/' )
+ if not redirect:
+ redirect = url_for( '/' )
consumer = trans.app.openid_manager.get_consumer( trans )
- openid_provider_obj = None
- if not openid_url and openid_provider and trans.app.openid_providers.get( openid_provider ):
- openid_provider_obj = trans.app.openid_providers.get( openid_provider )
- elif openid_url:
- openid_provider_obj = OpenIDProvider( openid_url, openid_url, openid_url ) #for manually entered links use the link for id, name and url
- elif openid_provider:
- message = 'Invalid OpenID provider specified: %s' % ( openid_provider )
+ if openid_url:
+ openid_provider_obj = trans.app.openid_providers.new_provider_from_identifier( openid_url )
else:
+ openid_provider_obj = trans.app.openid_providers.get( openid_provider )
+ if not openid_url and openid_provider == trans.app.openid_providers.NO_PROVIDER_ID:
message = 'An OpenID provider was not specified'
- process_url = trans.request.base.rstrip( '/' ) + url_for( controller='user', action='openid_process', referer=referer, auto_associate=auto_associate, openid_provider=openid_provider )
- if openid_provider_obj is not None:
+ elif openid_provider_obj:
+ process_url = trans.request.base.rstrip( '/' ) + url_for( controller='user', action='openid_process', redirect=redirect, openid_provider=openid_provider, auto_associate=auto_associate )
request = None
try:
request = consumer.begin( openid_provider_obj.op_endpoint_url )
@@ -87,84 +84,96 @@ class User( BaseUIController, UsesFormDefinitions ):
redirect_url = request.redirectURL(
trans.request.base, process_url )
trans.app.openid_manager.persist_session( trans, consumer )
- trans.response.send_redirect( redirect_url )
- return
+ return trans.response.send_redirect( redirect_url )
else:
form = request.htmlMarkup( trans.request.base, process_url, form_tag_attrs={'id':'openid_message','target':'_top'} )
trans.app.openid_manager.persist_session( trans, consumer )
return form
return trans.response.send_redirect( url_for( controller='user',
action=action,
+ redirect=redirect,
use_panels=use_panels,
message=message,
status='error' ) )
@web.expose
def openid_process( self, trans, webapp='galaxy', **kwd ):
+ '''Handle's response from OpenID Providers'''
if not trans.app.config.enable_openid:
return trans.show_error_message( 'OpenID authentication is not enabled in this instance of Galaxy' )
auto_associate = util.string_as_bool( kwd.get( 'auto_associate', False ) )
action = 'login'
- if auto_associate:
+ if trans.user:
action = 'openid_manage'
if trans.app.config.support_url is not None:
contact = 'support' % trans.app.config.support_url
else:
contact = 'support'
- message = 'Verification failed for an unknown reason. Please contact support for assistance.'
+ message = 'Verification failed for an unknown reason. Please contact %s for assistance.' % ( contact )
status = 'error'
consumer = trans.app.openid_manager.get_consumer( trans )
info = consumer.complete( kwd, trans.request.url )
display_identifier = info.getDisplayIdentifier()
- redirect_url = kwd.get( 'referer', url_for( '/' ) )
- openid_provider = kwd.get( 'openid_provider', '' )
+ redirect = kwd.get( 'redirect', url_for( '/' ) )
+ openid_provider = kwd.get( 'openid_provider', None )
if info.status == trans.app.openid_manager.FAILURE and display_identifier:
message = "Login via OpenID failed. The technical reason for this follows, please include this message in your email if you need to %s to resolve this problem: %s" % ( contact, info.message )
return trans.response.send_redirect( url_for( controller='user',
action=action,
use_panels=True,
+ redirect=redirect,
message=message,
status='error' ) )
elif info.status == trans.app.openid_manager.SUCCESS:
if info.endpoint.canonicalID:
display_identifier = info.endpoint.canonicalID
- user_openid = trans.sa_session.query( trans.app.model.UserOpenID ).filter( trans.app.model.UserOpenID.table.c.openid == display_identifier ).first()
openid_provider_obj = trans.app.openid_providers.get( openid_provider )
+ user_openid = trans.sa_session.query( trans.app.model.UserOpenID ).filter( trans.app.model.UserOpenID.table.c.openid == display_identifier ).first()
+ if not openid_provider_obj and user_openid and user_openid.provider:
+ openid_provider_obj = trans.app.openid_providers.get( user_openid.provider )
if not openid_provider_obj:
- openid_provider_obj = OpenIDProvider( display_identifier, display_identifier, display_identifier )
+ openid_provider_obj = trans.app.openid_providers.new_provider_from_identifier( display_identifier )
if not user_openid:
user_openid = trans.app.model.UserOpenID( session=trans.galaxy_session, openid=display_identifier )
- elif not user_openid.user and user_openid.session.id != trans.galaxy_session.id:
+ if not user_openid.user:
user_openid.session = trans.galaxy_session
- elif user_openid.user and not auto_associate:
- trans.handle_user_login( user_openid.user, webapp )
- trans.log_event( "User logged in via OpenID: %s" % display_identifier )
- openid_provider_obj.post_authentication( trans, trans.app.openid_manager, info )
- trans.response.send_redirect( redirect_url )
- return
- if auto_associate and trans.user:
- # The user is already logged in and requested association from
- # the user prefs as opposed to using the OpenID form on the
- # login page.
+ if not user_openid.provider and openid_provider:
+ user_openid.provider = openid_provider
+ if trans.user:
if user_openid.user and user_openid.user.id != trans.user.id:
message = "The OpenID %s is already associated with another Galaxy account, %s. Please disassociate it from that account before attempting to associate it with a new account." % ( display_identifier, user_openid.user.email )
status = "error"
- elif user_openid.user and user_openid.user.id == trans.user.id:
- message = "The OpenID %s is already associated with your Galaxy account, %s." % ( display_identifier, trans.user.email )
- status = "warning"
- else:
- user_openid.user_id = trans.user.id
- trans.sa_session.add( user_openid )
- trans.sa_session.flush()
- trans.log_event( "User associated OpenID: %s" % display_identifier )
- message = "The OpenID %s has been associated with your Galaxy account, %s." % ( display_identifier, trans.user.email )
- status = "done"
+ elif not user_openid.user or user_openid.user == trans.user:
+ if openid_provider_obj.id:
+ user_openid.provider = openid_provider_obj.id
+ user_openid.session = trans.galaxy_session
+ if not openid_provider_obj.never_associate_with_user:
+ if not auto_associate and ( user_openid.user and user_openid.user.id == trans.user.id ):
+ message = "The OpenID %s is already associated with your Galaxy account, %s." % ( display_identifier, trans.user.email )
+ status = "warning"
+ else:
+ message = "The OpenID %s has been associated with your Galaxy account, %s." % ( display_identifier, trans.user.email )
+ status = "done"
+ user_openid.user = trans.user
+ trans.sa_session.add( user_openid )
+ trans.sa_session.flush()
+ trans.log_event( "User associated OpenID: %s" % display_identifier )
+ else:
+ message = "The OpenID %s cannot be used to log into your Galaxy account, but any post authentication actions have been performed." % ( openid_provider_obj.name )
+ status ="info"
openid_provider_obj.post_authentication( trans, trans.app.openid_manager, info )
- trans.response.send_redirect( url_for( controller='user',
+ if redirect:
+ message = '%s
Click here to return to the page you were previously viewing.' % ( message, redirect )
+ return trans.response.send_redirect( url_for( controller='user',
action='openid_manage',
use_panels=True,
+ redirect=redirect,
message=message,
status=status ) )
- return
+ elif user_openid.user:
+ trans.handle_user_login( user_openid.user, webapp )
+ trans.log_event( "User logged in via OpenID: %s" % display_identifier )
+ openid_provider_obj.post_authentication( trans, trans.app.openid_manager, info )
+ return trans.response.send_redirect( redirect )
trans.sa_session.add( user_openid )
trans.sa_session.flush()
message = "OpenID authentication was successful, but you need to associate your OpenID with a Galaxy account."
@@ -179,10 +188,11 @@ class User( BaseUIController, UsesFormDefinitions ):
email = sreg_resp.get( sreg_email_name, '' )
except AttributeError:
email = ''
- trans.response.send_redirect( url_for( controller='user',
+ #OpenID success, but user not logged in, and not previously associated
+ return trans.response.send_redirect( url_for( controller='user',
action='openid_associate',
- openid_provider=openid_provider,
use_panels=True,
+ redirect=redirect,
username=username,
email=email,
message=message,
@@ -198,10 +208,12 @@ class User( BaseUIController, UsesFormDefinitions ):
return trans.response.send_redirect( url_for( controller='user',
action=action,
use_panels=True,
+ redirect=redirect,
message=message,
status=status ) )
@web.expose
def openid_associate( self, trans, cntrller='user', webapp='galaxy', **kwd ):
+ '''Associates a user with an OpenID log in'''
if not trans.app.config.enable_openid:
return trans.show_error_message( 'OpenID authentication is not enabled in this instance of Galaxy' )
use_panels = util.string_as_bool( kwd.get( 'use_panels', False ) )
@@ -209,9 +221,7 @@ class User( BaseUIController, UsesFormDefinitions ):
status = kwd.get( 'status', 'done' )
email = kwd.get( 'email', '' )
username = kwd.get( 'username', '' )
- referer = kwd.get( 'referer', trans.request.referer )
- openid_provider = kwd.get( 'openid_provider', '' )
- openid_provider_obj = trans.app.openid_providers.get( openid_provider )
+ redirect = kwd.get( 'redirect', '' )
params = util.Params( kwd )
is_admin = cntrller == 'admin' and trans.user_is_admin()
openids = trans.galaxy_session.openids
@@ -223,18 +233,33 @@ class User( BaseUIController, UsesFormDefinitions ):
if kwd.get( 'login_button', False ):
message, status, user, success = self.__validate_login( trans, webapp, **kwd )
if success:
+ openid_objs = []
for openid in openids:
- openid.user = user
- trans.sa_session.add( openid )
+ openid_provider_obj = trans.app.openid_providers.get( openid.provider )
+ if not openid_provider_obj or not openid_provider_obj.never_associate_with_user:
+ openid.user = user
+ trans.sa_session.add( openid )
+ trans.log_event( "User associated OpenID: %s" % openid.openid )
+ if openid_provider_obj and openid_provider_obj.has_post_authentication_actions():
+ openid_objs.append( openid_provider_obj )
trans.sa_session.flush()
- for openid in openids:
- trans.log_event( "User associated OpenID: %s" % openid.openid )
- redirect_url = referer
- if not redirect_url:
- redirect_url = url_for( '/' )
- if openid_provider_obj:
- return trans.response.send_redirect( url_for( controller='user', action='openid_auth', openid_provider=openid_provider, referer=redirect_url ) )
- return trans.response.send_redirect( redirect_url )
+ if len( openid_objs ) == 1:
+ return trans.response.send_redirect( url_for( controller='user', action='openid_auth', openid_provider=openid_objs[0].id, redirect=redirect, auto_associate=True ) )
+ elif openid_objs:
+ message = 'You have authenticated with several OpenID providers, please click the following links to execute the post authentication actions. '
+ message = "%s