diff --git a/lib/galaxy/config.py b/lib/galaxy/config.py
index a8df982d53c..332c16a2491 100644
--- a/lib/galaxy/config.py
+++ b/lib/galaxy/config.py
@@ -40,6 +40,7 @@ class Configuration( object ):
self.remote_user_maildomain = kwargs.get( "remote_user_maildomain", None )
self.require_login = string_as_bool( kwargs.get( "require_login", "False" ) )
self.allow_user_creation = string_as_bool( kwargs.get( "allow_user_creation", "True" ) )
+ self.allow_user_deletion = string_as_bool( kwargs.get( "allow_user_deletion", "False" ) )
self.template_path = resolve_path( kwargs.get( "template_path", "templates" ), self.root )
self.template_cache = resolve_path( kwargs.get( "template_cache_path", "database/compiled_templates" ), self.root )
self.local_job_queue_workers = int( kwargs.get( "local_job_queue_workers", "5" ) )
@@ -58,10 +59,10 @@ class Configuration( object ):
self.pbs_dataset_server = kwargs.get('pbs_dataset_server', "" )
self.pbs_dataset_path = kwargs.get('pbs_dataset_path', "" )
self.pbs_stage_path = kwargs.get('pbs_stage_path', "" )
- self.use_heartbeat = string_as_bool( kwargs.get( 'use_heartbeat', False ) )
- self.use_memdump = string_as_bool( kwargs.get( 'use_memdump', False ) )
- self.log_memory_usage = string_as_bool( kwargs.get( 'log_memory_usage', False ) )
- self.log_events = string_as_bool( kwargs.get( 'log_events', False ) )
+ self.use_heartbeat = string_as_bool( kwargs.get( 'use_heartbeat', 'False' ) )
+ self.use_memdump = string_as_bool( kwargs.get( 'use_memdump', 'False' ) )
+ self.log_memory_usage = string_as_bool( kwargs.get( 'log_memory_usage', 'False' ) )
+ self.log_events = string_as_bool( kwargs.get( 'log_events', 'False' ) )
self.ucsc_display_sites = kwargs.get( 'ucsc_display_sites', "main,test,archaea" ).lower().split(",")
self.gbrowse_display_sites = kwargs.get( 'gbrowse_display_sites', "wormbase,flybase,elegans" ).lower().split(",")
self.brand = kwargs.get( 'brand', None )
diff --git a/lib/galaxy/model/__init__.py b/lib/galaxy/model/__init__.py
index 5d6bf13d240..66fe0130b36 100644
--- a/lib/galaxy/model/__init__.py
+++ b/lib/galaxy/model/__init__.py
@@ -32,6 +32,8 @@ class User( object ):
self.email = email
self.password = password
self.external = False
+ self.deleted = False
+ self.purged = False
# Relationships
self.histories = []
@@ -143,6 +145,7 @@ class Group( object ):
permitted_actions = galaxy.security.get_permitted_actions( 'GROUP' )
def __init__( self, name = None ):
self.name = name
+ self.deleted = False
class UserGroupAssociation( object ):
def __init__( self, user, group ):
diff --git a/lib/galaxy/model/mapping.py b/lib/galaxy/model/mapping.py
index 4696c0ca1ad..86aa72579cd 100644
--- a/lib/galaxy/model/mapping.py
+++ b/lib/galaxy/model/mapping.py
@@ -43,7 +43,9 @@ User.table = Table( "galaxy_user", metadata,
Column( "update_time", DateTime, default=now, onupdate=now ),
Column( "email", TrimmedString( 255 ), nullable=False ),
Column( "password", TrimmedString( 40 ), nullable=False ),
- Column( "external", Boolean, default=False ) )
+ Column( "external", Boolean, default=False ),
+ Column( "deleted", Boolean, index=True, default=False ),
+ Column( "purged", Boolean, index=True, default=False ) )
History.table = Table( "history", metadata,
Column( "id", Integer, primary_key=True),
diff --git a/lib/galaxy/web/controllers/admin.py b/lib/galaxy/web/controllers/admin.py
index dc72c913abe..832a0d4f74f 100644
--- a/lib/galaxy/web/controllers/admin.py
+++ b/lib/galaxy/web/controllers/admin.py
@@ -3,12 +3,7 @@ from galaxy import util, datatypes
from galaxy.web.base.controller import *
from galaxy.datatypes import sniff
from galaxy.security import RBACAgent
-import galaxy.model
from galaxy.model.orm import *
-from xml.sax.saxutils import escape, unescape
-import pkg_resources
-pkg_resources.require( "SQLAlchemy >= 0.4" )
-import sqlalchemy as sa
import logging
log = logging.getLogger( __name__ )
@@ -60,9 +55,9 @@ class Admin( BaseController ):
params = util.Params( kwd )
msg = params.msg
messagetype = params.get( 'messagetype', 'done' )
- users=trans.app.model.User.query().order_by( trans.app.model.User.table.c.email ).all()
+ users = trans.app.model.User.filter( trans.app.model.User.table.c.deleted==False ).order_by( trans.app.model.User.table.c.email ).all()
groups = trans.app.model.Group.query() \
- .filter( galaxy.model.Group.table.c.deleted==False ) \
+ .filter( trans.app.model.Group.table.c.deleted==False ) \
.order_by( trans.app.model.Group.table.c.name ) \
.all()
return trans.fill_template( '/admin/dataset_security/role_create.mako',
@@ -84,23 +79,21 @@ class Admin( BaseController ):
trans.response.send_redirect( web.url_for( action='create_role', msg=msg, messagetype='error' ) )
else:
# Create the role
- role = galaxy.model.Role( name=name,
- description=description,
- type=trans.app.model.Role.types.ADMIN )
+ role = trans.app.model.Role( name=name, description=description, type=trans.app.model.Role.types.ADMIN )
role.flush()
# Add the users
users = util.listify( params.users )
for user_id in users:
- user = galaxy.model.User.get( user_id )
+ user = trans.app.model.User.get( user_id )
# Create the UserRoleAssociation
- ura = galaxy.model.UserRoleAssociation( user, role )
+ ura = trans.app.model.UserRoleAssociation( user, role )
ura.flush()
# Add the groups
groups = util.listify( params.groups )
for group_id in groups:
- group = galaxy.model.Group.get( group_id )
+ group = trans.app.model.Group.get( group_id )
# Create the GroupRoleAssociation
- gra = galaxy.model.GroupRoleAssociation( group, role )
+ gra = trans.app.model.GroupRoleAssociation( group, role )
gra.flush()
msg = "The new role has been created with %s associated users and %s associated groups" % ( str( len( users ) ), str( len( groups ) ) )
trans.response.send_redirect( web.url_for( action='roles', msg=msg, messagetype='done' ) )
@@ -115,7 +108,7 @@ class Admin( BaseController ):
out_users = []
in_groups = []
out_groups = []
- for user in trans.app.model.User.query().order_by( trans.app.model.User.table.c.email ).all():
+ for user in trans.app.model.User.filter( trans.app.model.User.table.c.deleted==False ).order_by( trans.app.model.User.table.c.email ).all():
if user in [ x.user for x in role.users ]:
in_users.append( ( user.id, user.email ) )
else:
@@ -163,7 +156,7 @@ class Admin( BaseController ):
@web.require_admin
def role_members_edit( self, trans, **kwd ):
params = util.Params( kwd )
- role = galaxy.model.Role.get( int( params.role_id ) )
+ role = trans.app.model.Role.get( int( params.role_id ) )
in_users = [ trans.app.model.User.get( x ) for x in util.listify( params.in_users ) ]
for ura in role.users:
user = trans.app.model.User.get( ura.user_id )
@@ -188,7 +181,7 @@ class Admin( BaseController ):
@web.require_admin
def mark_role_deleted( self, trans, **kwd ):
params = util.Params( kwd )
- role = galaxy.model.Role.get( int( params.role_id ) )
+ role = trans.app.model.Role.get( int( params.role_id ) )
role.deleted = True
role.flush()
msg = "The role has been marked as deleted."
@@ -202,17 +195,17 @@ class Admin( BaseController ):
# Build a list of tuples which are roles followed by lists of groups and users
# [ ( role, [ group, group, group ], [ user, user ] ), ( role, [ group, group ], [ user ] ) ]
roles_groups_users = []
- roles = galaxy.model.Role.query() \
- .filter( galaxy.model.Role.table.c.deleted==True ) \
- .order_by( galaxy.model.Role.table.c.name ) \
+ roles = trans.app.model.Role.query() \
+ .filter( trans.app.model.Role.table.c.deleted==True ) \
+ .order_by( trans.app.model.Role.table.c.name ) \
.all()
for role in roles:
groups = []
for gra in role.groups:
- groups.append( galaxy.model.Group.get( gra.group_id ) )
+ groups.append( trans.app.model.Group.get( gra.group_id ) )
users = []
for ura in role.users:
- users.append( galaxy.model.User.get( ura.user_id ) )
+ users.append( trans.app.model.User.get( ura.user_id ) )
roles_groups_users.append( ( role, groups, users ) )
return trans.fill_template( '/admin/dataset_security/deleted_roles.mako',
roles_groups_users=roles_groups_users,
@@ -222,7 +215,7 @@ class Admin( BaseController ):
@web.require_admin
def undelete_role( self, trans, **kwd ):
params = util.Params( kwd )
- role = galaxy.model.Role.get( int( params.role_id ) )
+ role = trans.app.model.Role.get( int( params.role_id ) )
role.deleted = False
role.flush()
msg = "The role has been marked as not deleted."
@@ -230,8 +223,19 @@ class Admin( BaseController ):
@web.expose
@web.require_admin
def purge_role( self, trans, **kwd ):
+ # This method should only be called for a Role that has previously been deleted.
+ # Purging a deleted Role deletes all of the following from the database:
+ # - UserRoleAssociations where role_id == Role.id
+ # - DefaultUserPermissions where role_id == Role.id
+ # - DefaultHistoryPermissions where role_id == Role.id
+ # - GroupRoleAssociations where role_id == Role.id
+ # - ActionDatasetRoleAssociations where role_id == Role.id
params = util.Params( kwd )
- role = galaxy.model.Role.get( int( params.role_id ) )
+ role = trans.app.model.Role.get( int( params.role_id ) )
+ if not role.deleted:
+ # We should never reach here, but just in case there is a bug somewhere...
+ msg = "The role has not been deleted, so it cannot be purged."
+ trans.response.send_redirect( web.url_for( action='roles', msg=msg, messagetype='error' ) )
# Delete UserRoleAssociations
for ura in role.users:
user = trans.app.model.User.get( ura.user_id )
@@ -252,10 +256,12 @@ class Admin( BaseController ):
for gra in role.groups:
gra.delete()
gra.flush()
- # Delete the Role
- role.delete()
- role.flush()
- msg = "The role has been purged from the database."
+ # Delete ActionDatasetRoleAssociations
+ for adra in role.actions:
+ adra.delete()
+ adra.flush()
+ msg = "The following have been purged from the database for the role: "
+ msg += "DefaultUserPermissions, DefaultHistoryPermissions, UserRoleAssociations, GroupRoleAssociations, ActionDatasetRoleAssociations."
trans.response.send_redirect( web.url_for( action='deleted_roles', msg=msg, messagetype='done' ) )
# Galaxy Group Stuff
@@ -268,17 +274,17 @@ class Admin( BaseController ):
# Build a list of tuples which are groups followed by lists of members and roles
# [ ( group, [ member, member, member ], [ role, role ] ), ( group, [ member, member ], [ role ] ) ]
groups_members_roles = []
- groups = galaxy.model.Group.query() \
- .filter( galaxy.model.Group.table.c.deleted==False ) \
- .order_by( galaxy.model.Group.table.c.name ) \
+ groups = trans.app.model.Group.query() \
+ .filter( trans.app.model.Group.table.c.deleted==False ) \
+ .order_by( trans.app.model.Group.table.c.name ) \
.all()
for group in groups:
members = []
for uga in group.members:
- members.append( galaxy.model.User.get( uga.user_id ) )
+ members.append( trans.app.model.User.get( uga.user_id ) )
roles = []
for gra in group.roles:
- roles.append( galaxy.model.Role.get( gra.role_id ) )
+ roles.append( trans.app.model.Role.get( gra.role_id ) )
groups_members_roles.append( ( group, members, roles ) )
return trans.fill_template( '/admin/dataset_security/groups.mako',
groups_members_roles=groups_members_roles,
@@ -290,10 +296,10 @@ class Admin( BaseController ):
params = util.Params( kwd )
msg = params.msg
messagetype = params.get( 'messagetype', 'done' )
- users=trans.app.model.User.query().order_by( trans.app.model.User.table.c.email ).all()
+ users = trans.app.model.User.filter( trans.app.model.User.table.c.deleted==False ).order_by( trans.app.model.User.table.c.email ).all()
roles = trans.app.model.Role.query() \
- .filter( and_( galaxy.model.Role.table.c.deleted == False,
- galaxy.model.Role.table.c.type != trans.app.model.Role.types.PRIVATE ) ) \
+ .filter( and_( trans.app.model.Role.table.c.deleted == False,
+ trans.app.model.Role.table.c.type != trans.app.model.Role.types.PRIVATE ) ) \
.order_by( trans.app.model.Role.table.c.name ) \
.all()
return trans.fill_template( '/admin/dataset_security/group_create.mako',
@@ -314,14 +320,14 @@ class Admin( BaseController ):
trans.response.send_redirect( web.url_for( action='create_group', msg=msg, messagetype='error' ) )
else:
# Create the group
- group = galaxy.model.Group( name )
+ group = trans.app.model.Group( name )
group.flush()
# Add the members
members = util.listify( params.members )
for user_id in members:
- user = galaxy.model.User.get( user_id )
+ user = trans.app.model.User.get( user_id )
# Create the UserGroupAssociation
- uga = galaxy.model.UserGroupAssociation( user, group )
+ uga = trans.app.model.UserGroupAssociation( user, group )
uga.flush()
# Add the roles
roles = params.roles
@@ -330,9 +336,9 @@ class Admin( BaseController ):
elif roles is None:
roles = []
for role_id in roles:
- role = galaxy.model.Role.get( role_id )
+ role = trans.app.model.Role.get( role_id )
# Create the GroupRoleAssociation
- gra = galaxy.model.GroupRoleAssociation( group, role )
+ gra = trans.app.model.GroupRoleAssociation( group, role )
gra.flush()
msg = "The new group has been created with %s members and %s associated roles" % ( str( len( members ) ), str( len( roles ) ) )
trans.response.send_redirect( web.url_for( action='groups', msg=msg, messagetype='done' ) )
@@ -342,14 +348,15 @@ class Admin( BaseController ):
params = util.Params( kwd )
msg = params.msg
messagetype = params.get( 'messagetype', 'done' )
- group = galaxy.model.Group.get( int( params.group_id ) )
+ group = trans.app.model.Group.get( int( params.group_id ) )
members = []
for uga in group.members:
- members.append ( galaxy.model.User.get( uga.user_id ) )
+ members.append ( trans.app.model.User.get( uga.user_id ) )
+ users = trans.app.model.User.filter( trans.app.model.User.table.c.deleted==False ).order_by( trans.app.model.User.table.c.email ).all()
return trans.fill_template( '/admin/dataset_security/group_members_edit.mako',
group=group,
members=members,
- users=galaxy.model.User.query().order_by( galaxy.model.User.table.c.email ).all(),
+ users=users,
msg=msg,
messagetype=messagetype )
@web.expose
@@ -358,7 +365,7 @@ class Admin( BaseController ):
params = util.Params( kwd )
group_id = int( params.group_id )
members = util.listify( params.members )
- group = galaxy.model.Group.get( group_id )
+ group = trans.app.model.Group.get( group_id )
# This is tricky since we have default association tables with
# records referring to members of this group. Because of this,
# we'll need to handle changes to the member list rather than the
@@ -372,9 +379,9 @@ class Admin( BaseController ):
uga.flush()
# Then add all new members to the group
for user_id in members:
- user = galaxy.model.User.get( user_id )
+ user = trans.app.model.User.get( user_id )
if user not in group.members:
- uga = galaxy.model.UserGroupAssociation( user, group )
+ uga = trans.app.model.UserGroupAssociation( user, group )
uga.flush()
msg = "Group membership has been updated with a total of %s members" % len( members )
trans.response.send_redirect( web.url_for( action='groups', msg=msg, messagetype='done' ) )
@@ -386,14 +393,14 @@ class Admin( BaseController ):
params = util.Params( kwd )
msg = params.msg
messagetype = params.get( 'messagetype', 'done' )
- group = galaxy.model.Group.get( int( params.group_id ) )
+ group = trans.app.model.Group.get( int( params.group_id ) )
group_roles = []
for gra in group.roles:
- group_roles.append ( galaxy.model.Role.get( gra.role_id ) )
+ group_roles.append ( trans.app.model.Role.get( gra.role_id ) )
return trans.fill_template( '/admin/dataset_security/group_roles_edit.mako',
group=group,
group_roles=group_roles,
- roles=galaxy.model.Role.query().order_by( galaxy.model.Role.table.c.name ).all(),
+ roles=trans.app.model.Role.query().order_by( trans.app.model.Role.table.c.name ).all(),
msg=msg,
messagetype=messagetype )
@web.expose
@@ -402,7 +409,7 @@ class Admin( BaseController ):
params = util.Params( kwd )
group_id = int( params.group_id )
roles = util.listify( params.roles )
- group = galaxy.model.Group.get( group_id )
+ group = trans.app.model.Group.get( group_id )
# This is tricky since we have default association tables with
# records referring to members of this group. Because of this,
# we'll need to handle changes to the member list rather than the
@@ -416,9 +423,9 @@ class Admin( BaseController ):
gra.flush()
# Then add all new roles to the group
for role_id in roles:
- role = galaxy.model.Role.get( role_id )
+ role = trans.app.model.Role.get( role_id )
if role not in group.roles:
- gra = galaxy.model.GroupRoleAssociation( group, role )
+ gra = trans.app.model.GroupRoleAssociation( group, role )
gra.flush()
msg = "Group updated with a total of %s associated roles" % len( roles )
trans.response.send_redirect( web.url_for( action='groups', msg=msg, messagetype='done' ) )
@@ -426,7 +433,7 @@ class Admin( BaseController ):
@web.require_admin
def mark_group_deleted( self, trans, **kwd ):
params = util.Params( kwd )
- group = galaxy.model.Group.get( int( params.group_id ) )
+ group = trans.app.model.Group.get( int( params.group_id ) )
group.deleted = True
group.flush()
msg = "The group has been marked as deleted."
@@ -440,17 +447,17 @@ class Admin( BaseController ):
# Build a list of tuples which are groups followed by lists of members and roles
# [ ( group, [ member, member, member ], [ role, role ] ), ( group, [ member, member ], [ role ] ) ]
groups_members_roles = []
- groups = galaxy.model.Group.query() \
- .filter( galaxy.model.Group.table.c.deleted==True ) \
- .order_by( galaxy.model.Group.table.c.name ) \
+ groups = trans.app.model.Group.query() \
+ .filter( trans.app.model.Group.table.c.deleted==True ) \
+ .order_by( trans.app.model.Group.table.c.name ) \
.all()
for group in groups:
members = []
for uga in group.members:
- members.append( galaxy.model.User.get( uga.user_id ) )
+ members.append( trans.app.model.User.get( uga.user_id ) )
roles = []
for gra in group.roles:
- roles.append( galaxy.model.Role.get( gra.role_id ) )
+ roles.append( trans.app.model.Role.get( gra.role_id ) )
groups_members_roles.append( ( group, members, roles ) )
return trans.fill_template( '/admin/dataset_security/deleted_groups.mako',
groups_members_roles=groups_members_roles,
@@ -460,7 +467,7 @@ class Admin( BaseController ):
@web.require_admin
def undelete_group( self, trans, **kwd ):
params = util.Params( kwd )
- group = galaxy.model.Group.get( int( params.group_id ) )
+ group = trans.app.model.Group.get( int( params.group_id ) )
group.deleted = False
group.flush()
msg = "The group has been marked as not deleted."
@@ -468,8 +475,14 @@ class Admin( BaseController ):
@web.expose
@web.require_admin
def purge_group( self, trans, **kwd ):
+ # This method should only be called for a Group that has previously been deleted.
+ # Purging a deleted Group simply deletes all UserGroupAssociations and GroupRoleAssociations.
params = util.Params( kwd )
- group = galaxy.model.Group.get( int( params.group_id ) )
+ group = trans.app.model.Group.get( int( params.group_id ) )
+ if not group.deleted:
+ # We should never reach here, but just in case there is a bug somewhere...
+ msg = "The group has not been deleted, so it cannot be purged."
+ trans.response.send_redirect( web.url_for( action='groups', msg=msg, messagetype='error' ) )
# Delete UserGroupAssociations
for uga in group.users:
uga.delete()
@@ -479,27 +492,40 @@ class Admin( BaseController ):
gra.delete()
gra.flush()
# Delete the Group
- group.delete()
- group.flush()
- msg = "The group has been purged from the database."
+ msg = "The following have been purged from the database for the group: UserGroupAssociations, GroupRoleAssociations."
trans.response.send_redirect( web.url_for( action='deleted_groups', msg=msg, messagetype='done' ) )
# Galaxy User Stuff
@web.expose
@web.require_admin
- def create_new_user( self, trans, email='', password='', confirm='', subscribe=False ):
- email_error = password_error = confirm_error = None
- if email:
+ def create_new_user( self, trans, **kwd ):
+ params = util.Params( kwd )
+ msg = params.msg
+ email = ''
+ password = ''
+ confirm = ''
+ subscribe = False
+ messagetype = params.get( 'messagetype', 'done' )
+ if 'user_create_button' in kwd:
+ if 'email' in kwd:
+ email = kwd[ 'email' ]
+ if 'password' in kwd:
+ password = kwd[ 'password' ]
+ if 'confirm' in kwd:
+ confirm = kwd[ 'confirm' ]
+ if 'subscribe' in kwd:
+ subscribe = kwd[ 'subscribe' ]
+ messagetype = 'error'
if len( email ) == 0 or "@" not in email or "." not in email:
- email_error = "Please enter a real email address"
+ msg = "Please enter a real email address"
elif len( email) > 255:
- email_error = "Email address exceeds maximum allowable length"
- elif trans.app.model.User.filter_by( email=email ).first():
- email_error = "User with that email already exists"
+ msg = "Email address exceeds maximum allowable length"
+ elif trans.app.model.User.filter( trans.app.model.User.table.c.email==email ).first():
+ msg = "User with that email already exists"
elif len( password ) < 6:
- password_error = "Please use a password of at least 6 characters"
+ msg = "Please use a password of at least 6 characters"
elif password != confirm:
- confirm_error = "Passwords do not match"
+ msg = "Passwords do not match"
else:
user = trans.app.model.User( email=email )
user.set_password_cleartext( password )
@@ -507,25 +533,147 @@ class Admin( BaseController ):
trans.app.security_agent.create_private_user_role( user )
trans.app.security_agent.user_set_default_permissions( user, history=False, dataset=False )
trans.log_event( "Admin created a new account for user %s" % email )
- msg = 'Created new account'
+ msg = 'Created new user account'
messagetype = 'done'
#subscribe user to email list
if subscribe:
mail = os.popen( "%s -t" % trans.app.config.sendmail_path, 'w' )
- mail.write( "To: %s\nFrom: %s\nSubject: Join Mailing List\n\nJoin Mailing list." % ( trans.app.config.mailing_join_addr,email ) )
+ mail.write( "To: %s\nFrom: %s\nSubject: Join Mailing List\n\nJoin Mailing list." % ( trans.app.config.mailing_join_addr, email ) )
if mail.close():
msg + ". However, subscribing to the mailing list has failed."
messagetype = 'error'
- trans.response.send_redirect( web.url_for( action='users', msg=msg, messagetype=messagetype ) )
- # TODO: make this a mako template
- return trans.show_form(
- web.FormBuilder( web.url_for(), "Create account", submit_text="Create" )
- .add_text( "email", "Email address", value=email, error=email_error )
- .add_password( "password", "Password", value='', error=password_error )
- .add_password( "confirm", "Confirm password", value='', error=confirm_error )
- .add_input( "checkbox","Subscribe To Mailing List","subscribe", value='subscribe' ) )
-
-
+ trans.response.send_redirect( web.url_for( action='users', msg=msg, messagetype=messagetype ) )
+ return trans.fill_template( '/admin/user/create.mako',
+ msg=msg,
+ messagetype=messagetype,
+ email=email,
+ password=password,
+ confirm=confirm,
+ subscribe=subscribe )
+ @web.expose
+ @web.require_admin
+ def reset_user_password( self, trans, **kwd ):
+ params = util.Params( kwd )
+ msg = params.msg
+ user_id = int( params.user_id )
+ user = trans.app.model.User.filter( trans.app.model.User.table.c.id==user_id ).first()
+ password = ''
+ confirm = ''
+ messagetype = params.get( 'messagetype', 'done' )
+ if 'reset_user_password_button' in kwd:
+ if 'password' in kwd:
+ password = kwd[ 'password' ]
+ if 'confirm' in kwd:
+ confirm = kwd[ 'confirm' ]
+ messagetype = 'error'
+ if len( password ) < 6:
+ msg = "Please use a password of at least 6 characters"
+ elif password != confirm:
+ msg = "Passwords do not match"
+ else:
+ user.set_password_cleartext( password )
+ user.flush()
+ trans.log_event( "Admin reset password for user %s" % user.email )
+ msg = 'Password reset'
+ messagetype = 'done'
+ trans.response.send_redirect( web.url_for( action='users', msg=msg, messagetype=messagetype ) )
+ return trans.fill_template( '/admin/user/reset_password.mako',
+ msg=msg,
+ messagetype=messagetype,
+ user=user,
+ password=password,
+ confirm=confirm )
+ @web.expose
+ @web.require_admin
+ def mark_user_deleted( self, trans, **kwd ):
+ params = util.Params( kwd )
+ msg = params.msg
+ messagetype = params.get( 'messagetype', 'done' )
+ user = trans.app.model.User.get( int( params.user_id ) )
+ user.deleted = True
+ user.flush()
+ msg = "The user has been marked as deleted."
+ trans.response.send_redirect( web.url_for( action='users', msg=msg, messagetype='done' ) )
+ @web.expose
+ @web.require_admin
+ def undelete_user( self, trans, **kwd ):
+ params = util.Params( kwd )
+ user = trans.app.model.User.get( int( params.user_id ) )
+ user.deleted = False
+ user.flush()
+ msg = "The user has been marked as not deleted."
+ trans.response.send_redirect( web.url_for( action='users', msg=msg, messagetype='done' ) )
+ @web.expose
+ @web.require_admin
+ def purge_user( self, trans, **kwd ):
+ # This method should only be called for a User that has previously been deleted.
+ # We keep the User in the database ( marked as purged ), and stuff associated
+ # with the user's private role in case we want the ability to unpurge the user
+ # some time in the future.
+ # Purging a deleted User deletes all of the following:
+ # - DefaultUserPermissions where user_id == User.id EXCEPT FOR THE PRIVATE ROLE
+ # - History where user_id = User.id
+ # - DefaultHistoryPermissions where history_id == History.id EXCEPT FOR THE PRIVATE ROLE
+ # - HistoryDatasetAssociation where history_id = History.id
+ # - Dataset where HistoryDatasetAssociation.dataset_id = Dataset.id
+ # - UserGroupAssociation where user_id == User.id
+ # - UserRoleAssociation where user_id == User.id EXCEPT FOR THE PRIVATE ROLE
+ # Purging Histories and Datasets must be handled via the cleanup_datasets.py script
+ params = util.Params( kwd )
+ user = trans.app.model.User.get( int( params.user_id ) )
+ if not user.deleted:
+ # We should never reach here, but just in case there is a bug somewhere...
+ msg = "The account has not been deleted, so it cannot be purged."
+ trans.response.send_redirect( web.url_for( action='users', msg=msg, messagetype='error' ) )
+ private_role = trans.app.security_agent.get_private_user_role( user )
+ # Delete DefaultUserPermissions EXCEPT FOR THE PRIVATE ROLE
+ for dup in user.default_permissions:
+ if dup.role_id != private_role.id:
+ dup.delete()
+ dup.flush()
+ # Delete History
+ for h in user.active_histories:
+ h.refresh()
+ # Delete DefaultHistoryPermissions EXCEPT FOR THE PRIVATE ROLE
+ for dp in h.default_permissions:
+ if dp.role_id != private_role.id:
+ dp.delete()
+ dp.flush()
+ for hda in h.active_datasets:
+ # Delete HistoryDatasetAssociation
+ d = trans.app.model.Dataset.get( hda.dataset_id )
+ # Delete Dataset
+ if not d.deleted:
+ d.deleted = True
+ d.flush()
+ hda.deleted = True
+ hda.flush()
+ h.deleted = True
+ h.flush()
+ # Delete UserGroupAssociations
+ for uga in user.groups:
+ uga.delete()
+ uga.flush()
+ # Delete UserRoleAssociations EXCEPT FOR THE PRIVATE ROLE
+ for ura in user.roles:
+ if ura.role_id != private_role.id:
+ ura.delete()
+ ura.flush()
+ # Purge the user
+ user.purged = True
+ user.flush()
+ msg = "The user has been marked as purged."
+ trans.response.send_redirect( web.url_for( action='deleted_users', msg=msg, messagetype='done' ) )
+ @web.expose
+ @web.require_admin
+ def deleted_users( self, trans, **kwd ):
+ params = util.Params( kwd )
+ msg = params.msg
+ messagetype = params.get( 'messagetype', 'done' )
+ users = trans.app.model.User.filter( and_( trans.app.model.User.table.c.deleted==True, trans.app.model.User.table.c.purged==False ) ) \
+ .order_by( trans.app.model.User.table.c.email ) \
+ .all()
+ return trans.fill_template( '/admin/user/deleted_users.mako', users=users, msg=msg, messagetype=messagetype )
@web.expose
@web.require_admin
def users( self, trans, **kwd ):
@@ -535,24 +683,25 @@ class Admin( BaseController ):
# Build a list of tuples which are users followed by lists of groups and roles
# [ ( user, [ group, group, group ], [ role, role ] ), ( user, [ group, group ], [ role ] ) ]
users_groups_roles = []
- users = trans.app.model.User.query().order_by( trans.app.model.User.table.c.email ).all()
+ users = trans.app.model.User.filter( trans.app.model.User.table.c.deleted==False ).order_by( trans.app.model.User.table.c.email ).all()
for user in users:
groups = []
for uga in user.groups:
- groups.append( galaxy.model.Group.get( uga.group_id ) )
+ groups.append( trans.app.model.Group.get( uga.group_id ) )
roles = []
for ura in user.non_private_roles:
- roles.append( galaxy.model.Role.get( ura.role_id ) )
+ roles.append( trans.app.model.Role.get( ura.role_id ) )
users_groups_roles.append( ( user, groups, roles ) )
return trans.fill_template( '/admin/dataset_security/users.mako',
users_groups_roles=users_groups_roles,
+ allow_user_deletion=trans.app.config.allow_user_deletion,
msg=msg,
messagetype=messagetype )
@web.expose
@web.require_admin
def user( self, trans, **kwd ):
params = util.Params( kwd )
- user_id = params.user_id
+ user_id = int( params.user_id )
msg = params.msg
messagetype = params.get( 'messagetype', 'done' )
user = trans.app.model.User.get( user_id )
@@ -560,7 +709,7 @@ class Admin( BaseController ):
groups = trans.app.model.Group.query() \
.select_from( ( outerjoin( trans.app.model.Group, trans.app.model.UserGroupAssociation ) ) \
.outerjoin( trans.app.model.User ) ) \
- .filter( and_( trans.app.model.Group.deleted == False, trans.app.model.User.id == user_id ) ) \
+ .filter( and_( trans.app.model.Group.deleted==False, trans.app.model.User.id==user_id ) ) \
.order_by( trans.app.model.Group.table.c.name ) \
.all()
roles = user.all_roles()
@@ -576,13 +725,13 @@ class Admin( BaseController ):
params = util.Params( kwd )
msg = params.msg
messagetype = params.get( 'messagetype', 'done' )
- user = galaxy.model.User.get( int( params.user_id ) )
+ user = trans.app.model.User.get( int( params.user_id ) )
user_groups = []
for uga in user.groups:
- user_groups.append ( galaxy.model.Group.get( uga.group_id ) )
- groups = galaxy.model.Group.query() \
- .filter( galaxy.model.Group.table.c.deleted==False ) \
- .order_by( galaxy.model.Group.table.c.name ) \
+ user_groups.append ( trans.app.model.Group.get( uga.group_id ) )
+ groups = trans.app.model.Group.query() \
+ .filter( trans.app.model.Group.table.c.deleted==False ) \
+ .order_by( trans.app.model.Group.table.c.name ) \
.all()
return trans.fill_template( '/admin/dataset_security/user_groups_edit.mako',
user=user,
@@ -596,7 +745,7 @@ class Admin( BaseController ):
params = util.Params( kwd )
user_id = int( params.user_id )
groups = util.listify( params.groups )
- user = galaxy.model.User.get( user_id )
+ user = trans.app.model.User.get( user_id )
# First remove existing UserGroupAssociations that are not in the received groups param
for uga in user.groups:
if uga.group_id not in groups:
@@ -605,9 +754,9 @@ class Admin( BaseController ):
uga.flush()
# Then add all new groups to the user
for group_id in groups:
- group = galaxy.model.Group.get( group_id )
+ group = trans.app.model.Group.get( group_id )
if group not in user.groups:
- uga = galaxy.model.UserGroupAssociation( user, group )
+ uga = trans.app.model.UserGroupAssociation( user, group )
uga.flush()
msg = "The user now belongs to a total of %s groups" % len( groups )
trans.response.send_redirect( web.url_for( action='users', msg=msg, messagetype='done' ) )
@@ -713,7 +862,7 @@ class Admin( BaseController ):
@web.require_admin
def undelete_library( self, trans, **kwd ):
params = util.Params( kwd )
- library = galaxy.model.Library.get( int( params.id ) )
+ library = trans.app.model.Library.get( int( params.id ) )
def undelete_folder( library_folder ):
for folder in library_folder.folders:
undelete_folder( folder )
@@ -731,7 +880,7 @@ class Admin( BaseController ):
@web.require_admin
def purge_library( self, trans, **kwd ):
params = util.Params( kwd )
- library = galaxy.model.Library.get( int( params.id ) )
+ library = trans.app.model.Library.get( int( params.id ) )
def purge_folder( library_folder ):
for lf in library_folder.folders:
purge_folder( lf )
@@ -877,7 +1026,7 @@ class Admin( BaseController ):
dataset.flush()
if roles:
for role in roles:
- adra = galaxy.model.ActionDatasetRoleAssociation( RBACAgent.permitted_actions.DATASET_ACCESS.action, dataset.dataset, role )
+ adra = trans.app.model.ActionDatasetRoleAssociation( RBACAgent.permitted_actions.DATASET_ACCESS.action, dataset.dataset, role )
adra.flush()
shutil.move( temp_name, dataset.dataset.file_name )
dataset.dataset.state = dataset.dataset.states.OK
@@ -914,7 +1063,7 @@ class Admin( BaseController ):
roles = []
role_ids = params.get( 'roles', [] )
for role_id in util.listify( role_ids ):
- roles.append( galaxy.model.Role.get( role_id ) )
+ roles.append( trans.app.model.Role.get( role_id ) )
temp_name = ""
data_list = []
created_lfda_ids = ''
@@ -1139,7 +1288,8 @@ class Admin( BaseController ):
msg = params.get( 'msg', None )
messagetype = params.get( 'messagetype', 'done' )
# See if the current history is empty
- history=trans.get_history()
+ history = trans.get_history()
+ history.refresh()
if not history.active_datasets:
msg = 'Your current history is empty'
return trans.response.send_redirect( web.url_for( action='library_browser', msg=msg, messagetype='error' ) )
diff --git a/lib/galaxy/web/controllers/root.py b/lib/galaxy/web/controllers/root.py
index beb32d121fa..5cb9c8d48df 100644
--- a/lib/galaxy/web/controllers/root.py
+++ b/lib/galaxy/web/controllers/root.py
@@ -527,7 +527,7 @@ class RootController( BaseController ):
if not email:
return trans.fill_template("/history/share.mako", histories=histories, email=email, send_to_err=send_to_err)
user = trans.get_user()
- send_to_user = trans.app.model.User.filter_by( email=email ).first()
+ send_to_user = trans.app.model.User.filter( trans.app.model.User.table.c.email==email ).first()
p = util.Params( kwd )
if p.action and p.action == "no_share":
trans.response.send_redirect( url_for( action='history_options' ) )
diff --git a/lib/galaxy/web/controllers/user.py b/lib/galaxy/web/controllers/user.py
index 9e197c021dd..7f65379f858 100644
--- a/lib/galaxy/web/controllers/user.py
+++ b/lib/galaxy/web/controllers/user.py
@@ -88,12 +88,13 @@ class User( BaseController ):
else:
refresh_frames = [ 'masthead', 'history' ]
if email or password:
- user = trans.app.model.User.filter_by( email=email ).first()
+ user = trans.app.model.User.filter( trans.app.model.User.table.c.email==email ).first()
if not user:
email_error = "No such user"
+ elif user.deleted:
+ email_error = "This account has been marked deleted, contact your Galaxy administrator to restore the account."
elif user.external:
- return trans.show_error_message( "This account was created for use with an external authentication "
- + "method. Please contact your local Galaxy administrator to activate it." )
+ email_error = "This account was created for use with an external authentication method, contact your local Galaxy administrator to activate it."
elif not user.check_password( password ):
password_error = "Invalid password"
else:
@@ -118,9 +119,6 @@ class User( BaseController ):
@web.expose
def logout( self, trans ):
- if trans.app.memory_usage:
- # Keep track of memory usage
- m0 = trans.app.memory_usage.memory()
if trans.app.config.require_login:
refresh_frames = [ 'masthead', 'history', 'tools' ]
else:
@@ -128,9 +126,6 @@ class User( BaseController ):
# Since logging an event requires a session, we'll log prior to ending the session
trans.log_event( "User logged out" )
trans.handle_user_logout()
- if trans.app.memory_usage:
- m1 = trans.app.memory_usage.memory( m0, pretty=True )
- log.info( "End of user/logout, memory used increased by %s" % m1 )
msg = "You are no longer logged in."
if trans.app.config.require_login:
msg += ' Click here to return to the login page.' % web.url_for( controller='user', action='login' )
@@ -148,9 +143,10 @@ class User( BaseController ):
if email:
if len( email ) == 0 or "@" not in email or "." not in email:
email_error = "Please enter a real email address"
- elif len( email) > 255:
+ elif len( email ) > 255:
email_error = "Email address exceeds maximum allowable length"
- elif trans.app.model.User.filter_by( email=email ).first():
+ elif trans.app.model.User.filter( and_( trans.app.model.User.table.c.email==email,
+ trans.app.model.User.table.c.deleted==False ) ).first():
email_error = "User with that email already exists"
elif len( password ) < 6:
password_error = "Please use a password of at least 6 characters"
@@ -182,11 +178,11 @@ class User( BaseController ):
@web.expose
def reset_password( self, trans, email=None, **kwd ):
error = ''
- reset_user = trans.app.model.User.filter_by( email=email ).first()
+ reset_user = trans.app.model.User.filter( trans.app.model.User.table.c.email==email ).first()
user = trans.get_user()
if reset_user:
if user and user.id != reset_user.id:
- error = "You may only reset your own password"
+ error = "You may only reset your own password"
else:
chars = string.letters + string.digits
new_pass = ""
diff --git a/lib/galaxy/web/controllers/workflow.py b/lib/galaxy/web/controllers/workflow.py
index 35c6eed0d0b..beaa684818a 100644
--- a/lib/galaxy/web/controllers/workflow.py
+++ b/lib/galaxy/web/controllers/workflow.py
@@ -13,6 +13,7 @@ from galaxy.util.bunch import Bunch
from galaxy.util.topsort import topsort, topsort_levels, CycleError
from galaxy.workflow.modules import *
from galaxy.model.mapping import desc
+from galaxy.model.orm import *
class WorkflowController( BaseController ):
@@ -48,7 +49,8 @@ class WorkflowController( BaseController ):
# Load workflow from database
stored = get_stored_workflow( trans, id )
if email:
- other = model.User.filter_by( email=email ).first()
+ other = model.User.filter( and_( model.user.table.c.email==email,
+ model.User.table.c.deleted==False ) ).first()
if not other:
mtype = "error"
msg = ( "User '%s' does not exist" % email )
diff --git a/lib/galaxy/web/framework/__init__.py b/lib/galaxy/web/framework/__init__.py
index af93111117f..59324a80479 100644
--- a/lib/galaxy/web/framework/__init__.py
+++ b/lib/galaxy/web/framework/__init__.py
@@ -221,8 +221,7 @@ class UniverseWebTransaction( base.DefaultWebTransaction ):
galaxy_session.user = self.__get_or_create_remote_user( remote_user_email )
galaxy_session_requires_flush = True
elif galaxy_session.user.email != remote_user_email:
- # Session exists but is not associated with the correct
- # remote user
+ # Session exists but is not associated with the correct remote user
invalidate_existing_session = True
user_for_new_session = self.__get_or_create_remote_user( remote_user_email )
log.warning( "User logged in as '%s' externally, but has a cookie as '%s' invalidating session",
@@ -293,16 +292,27 @@ class UniverseWebTransaction( base.DefaultWebTransaction ):
def __get_or_create_remote_user( self, remote_user_email ):
"""
Return the user in $HTTP_REMOTE_USER and create if necessary
-
Caller is responsible for flushing the returned user.
"""
# remote_user middleware ensures HTTP_REMOTE_USER exists
- user = self.app.model.User.filter_by( email=remote_user_email ).first()
+ user = self.app.model.User.filter( self.app.model.User.table.c.email==remote_user_email ).first()
if user is None:
user = self.app.model.User( email=remote_user_email )
user.set_password_cleartext( 'external' )
user.external = True
self.log_event( "Automatically created account '%s'", user.email )
+ # TODO: make sure this correctly handles deleted / purged users
+ elif user.deleted:
+ if user.purged:
+ # If the user has been purged, all associations have been deleted except for the private role
+ # and the DefaultUserPermissions and DefaultHistoryPermissions associated with it. We'll
+ # restore the user, but all of their previous histories and other associations will have been
+ # deleted.
+ user.purged = False
+ # If the user was not purged, the state of all of their associations at the time they were deleted
+ # will have been preserved.
+ user.deleted = False
+ user.flush()
return user
def __update_session_cookie( self ):
"""
diff --git a/templates/admin/dataset_security/users.mako b/templates/admin/dataset_security/users.mako
index 39dad937182..e149d23cdb4 100644
--- a/templates/admin/dataset_security/users.mako
+++ b/templates/admin/dataset_security/users.mako
@@ -16,7 +16,11 @@
${user.email}
@@ -44,6 +48,9 @@
%if msg:
diff --git a/templates/admin/library/browser.mako b/templates/admin/library/browser.mako
index ed9da94ade4..f268a8a11dd 100644
--- a/templates/admin/library/browser.mako
+++ b/templates/admin/library/browser.mako
@@ -103,7 +103,7 @@
Create a new sub-folder in this folder
Rename this folder
%if subfolder:
- Remove this folder and its contents from the library
+ Remove this folder and its contents from the library
%endif
%endif
diff --git a/templates/admin/user/create.mako b/templates/admin/user/create.mako
new file mode 100644
index 00000000000..e119e29e6c0
--- /dev/null
+++ b/templates/admin/user/create.mako
@@ -0,0 +1,43 @@
+<%inherit file="/base.mako"/>
+<%namespace file="/message.mako" import="render_msg" />
+
+%if msg:
+ ${render_msg( msg, messagetype )}
+%endif
+
+
diff --git a/templates/admin/user/deleted_users.mako b/templates/admin/user/deleted_users.mako
new file mode 100644
index 00000000000..0847cf25a63
--- /dev/null
+++ b/templates/admin/user/deleted_users.mako
@@ -0,0 +1,91 @@
+<%inherit file="/base.mako"/>
+<%namespace file="/message.mako" import="render_msg" />
+
+## Render a row
+<%def name="render_row( user, ctr, anchored, curr_anchor )">
+ %if ctr % 2 == 1:
+ |
+ %else:
+
+ %endif
+ |
+ ${user.email}
+
+
+ %if not anchored:
+
+
+ %endif
+ |
+
+%def>
+
+Deleted Users
+
+%if msg:
+ ${render_msg( msg, messagetype )}
+%endif
+
+%if len( users ) == 0:
+ There are no deleted Galaxy users
+%else:
+
+ <%
+ render_quick_find = len( users ) > 50
+ ctr = 0
+ %>
+ %if render_quick_find:
+ <%
+ anchors = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z']
+ anchor_loc = 0
+ anchored = False
+ curr_anchor = 'A'
+ %>
+
+ |
+ Jump to letter:
+ %for a in anchors:
+ | ${a}
+ %endfor
+ |
+
+ %endif
+
+ %for ctr, user in enumerate( users ):
+ %if render_quick_find and not user.email.upper().startswith( curr_anchor ):
+ <% anchored = False %>
+ %endif
+ %if render_quick_find and user.email.upper().startswith( curr_anchor ):
+ %if not anchored:
+ ${render_row( user, ctr, anchored, curr_anchor )}
+ <% anchored = True %>
+ %else:
+ ${render_row( user, ctr, anchored, curr_anchor )}
+ %endif
+ %elif render_quick_find:
+ %for anchor in anchors[ anchor_loc: ]:
+ %if user.email.upper().startswith( anchor ):
+ %if not anchored:
+ <% curr_anchor = anchor %>
+ ${render_row( user, ctr, anchored, curr_anchor )}
+ <% anchored = True %>
+ %else:
+ ${render_row( user, ctr, anchored, curr_anchor )}
+ %endif
+ <%
+ anchor_loc = anchors.index( anchor )
+ break
+ %>
+ %endif
+ %endfor
+ %else:
+ ${render_row( user, ctr, True, '' )}
+ %endif
+ %endfor
+
+%endif
diff --git a/templates/admin/user/reset_password.mako b/templates/admin/user/reset_password.mako
new file mode 100644
index 00000000000..5c0e92de957
--- /dev/null
+++ b/templates/admin/user/reset_password.mako
@@ -0,0 +1,35 @@
+<%inherit file="/base.mako"/>
+<%namespace file="/message.mako" import="render_msg" />
+
+%if msg:
+ ${render_msg( msg, messagetype )}
+%endif
+
+
diff --git a/test/base/twilltestcase.py b/test/base/twilltestcase.py
index dff00121fc0..9288f4c58da 100644
--- a/test/base/twilltestcase.py
+++ b/test/base/twilltestcase.py
@@ -362,16 +362,13 @@ class TwillTestCase( unittest.TestCase ):
self.assertTrue( genome_build == dbkey )
# Functions associated with user accounts
- def create( self, email='test@bx.psu.edu', password='testuser', confirm='testuser' ):
- self.visit_page( "user/create?email=%s&password=%s&confirm=%s" %(email, password, confirm) )
- try:
- self.check_page_for_string( "User with that email already exists" )
- except:
- self.check_page_for_string( "Now logged in as %s" %email )
- self.home()
- # Make sure a new private role was created for the user
- self.visit_page( "user/set_default_permissions" )
- self.check_page_for_string( email )
+ def create( self, email='test@bx.psu.edu', password='testuser' ):
+ self.visit_page( "user/create?email=%s&password=%s&confirm=%s" % ( email, password, password ) )
+ self.check_page_for_string( "Now logged in as %s" %email )
+ self.home()
+ # Make sure a new private role was created for the user
+ self.visit_page( "user/set_default_permissions" )
+ self.check_page_for_string( email )
self.home()
def user_set_default_permissions( self, permissions_out=[], permissions_in=[], role_id=2 ): # role.id = 2 is Private Role for test2@bx.psu.edu
# NOTE: Twill has a bug that requires the ~/user/permissions page to contain at least 1 option value
@@ -405,14 +402,19 @@ class TwillTestCase( unittest.TestCase ):
self.last_page()
self.check_page_for_string( 'Default history permissions have been changed.' )
self.home()
- def login( self, email='test@bx.psu.edu', password='testuser'):
+ def login( self, email='test@bx.psu.edu', password='testuser' ):
# test@bx.psu.edu is configured as an admin user
- self.create( email=email, password=password, confirm=password )
- self.visit_page( "user/login?email=%s&password=%s" % (email, password) )
- self.check_page_for_string( "Now logged in as %s" %email )
- self.home()
+ try:
+ self.create( email=email, password=password )
+ except:
+ self.home()
+ self.visit_page( "user/login?email=%s&password=%s" % ( email, password ) )
+ self.last_page()
+ self.check_page_for_string( "Now logged in as %s" %email )
+ self.home()
def logout( self ):
self.visit_page( "user/logout" )
+ self.last_page()
self.check_page_for_string( "You are no longer logged in" )
self.home()
# Functions associated with browsers, cookies, HTML forms and page visits
@@ -581,7 +583,41 @@ class TwillTestCase( unittest.TestCase ):
self.assertNotEqual(count, maxiter)
# Dataset Security stuff
- def create_role( self, name='New Test Role', description="Very cool new test role", user_ids=[], group_ids=[], private_role='' ):
+ def create_new_account_as_admin( self, email='test4@bx.psu.edu', password='testuser' ):
+ """Create a new account for another user"""
+ self.visit_url( "%s/admin/create_new_user?email=%s&password=%s&confirm=%s&user_create_button=%s" \
+ % ( self.url, email, password, password, 'Create' ) )
+ self.last_page()
+ self.check_page_for_string( "Created new user account" )
+ self.home()
+ def reset_password_as_admin( self, user_id=4, password='testreset' ):
+ """Reset a user password"""
+ self.visit_url( "%s/admin/reset_user_password?user_id=%s" % ( self.url, str( user_id ) ) )
+ tc.fv( "1", "password", password )
+ tc.fv( "1", "confirm", password )
+ tc.submit( "reset_user_password_button" )
+ self.last_page()
+ self.check_page_for_string( "Password reset" )
+ self.home()
+ def mark_user_deleted( self, user_id=4 ):
+ """Mark a user as deleted"""
+ self.visit_url( "%s/admin/mark_user_deleted?user_id=%s" % ( self.url, str( user_id ) ) )
+ self.last_page()
+ self.check_page_for_string( "The user has been marked as deleted." )
+ self.home()
+ def undelete_user( self, user_id ):
+ """Undelete a user"""
+ self.visit_url( "%s/admin/undelete_user?user_id=%s" % ( self.url, user_id ) )
+ self.last_page()
+ self.check_page_for_string( 'The user has been marked as not deleted' )
+ self.home()
+ def purge_user( self, user_id ):
+ """Purge a user account"""
+ self.visit_url( "%s/admin/purge_user?user_id=%s" % ( self.url, user_id ) )
+ self.last_page()
+ self.check_page_for_string( 'The user has been marked as purged.' )
+ self.home()
+ def create_role( self, name='Role One', description="This is Role One", user_ids=[], group_ids=[], private_role='' ):
"""Create a new role"""
self.visit_url( "%s/admin/create_role" % self.url )
form = tc.show()
@@ -632,15 +668,15 @@ class TwillTestCase( unittest.TestCase ):
self.last_page()
self.check_page_for_string( 'The role has been marked as not deleted' )
self.home()
- def purge_role( self, role_id, deleted=False ):
+ def purge_role( self, role_id ):
"""Purge an existing role"""
- if not deleted:
- self.mark_role_deleted( role_id )
self.visit_url( "%s/admin/purge_role?role_id=%s" % ( self.url, role_id ) )
self.last_page()
- self.check_page_for_string( 'The role has been purged from the database' )
+ msg = "The following have been purged from the database for the role: "
+ msg += "DefaultUserPermissions, DefaultHistoryPermissions, UserRoleAssociations, GroupRoleAssociations, ActionDatasetRoleAssociations."
+ self.check_page_for_string( msg )
self.home()
- def create_group( self, name='New Test Group', user_ids=[], role_ids=[] ):
+ def create_group( self, name='Group One', user_ids=[], role_ids=[] ):
"""Create a new group with 2 members and 1 associated role"""
self.visit_url( "%s/admin/create_group" % self.url )
form = tc.show()
@@ -685,23 +721,17 @@ class TwillTestCase( unittest.TestCase ):
self.home()
raise AssertionError( 'Exception caught attempting to create group: %s' % str( err ) )
self.home()
- def associate_groups_with_role( self, role_id, group_ids=[] ):
+ def associate_groups_with_role( self, role_id, group_names=[] ):
"""Add groups to an existing role"""
# NOTE: To get this to work with twill, all select lists must contain at least 1 option value
- # or twill throws an exception, which is: ParseError: OPTION outside of SELECT
+ # before tc.submit or twill throws an exception, which is: ParseError: OPTION outside of SELECT
self.visit_url( "%s/admin/role?role_id=%s" % ( self.url, role_id ) )
self.check_page_for_string( 'Groups associated with' )
- # All groups must be in the out_groups form field
- try:
- for group in groups:
- tc.fv( "1", "7", group_id ) # form field 7 is the select list named out_groups, note the buttons...
- tc.submit( "groups_add_button" )
- tc.submit( "role_button" )
- except AssertionError, err:
- self.home()
- raise AssertionError( 'Exception caught attempting to associated groups with a role: %s' % str( err ) )
- except:
- pass
+ # All group_ids passed in MUST be in the out_groups form field
+ for group_name in group_names:
+ tc.fv( "1", "out_groups", group_name ) # note the buttons...
+ tc.submit( "groups_add_button" )
+ tc.submit( "role_button" )
self.home()
def mark_group_deleted( self, group_id ):
"""Mark a group as deleted"""
@@ -715,17 +745,15 @@ class TwillTestCase( unittest.TestCase ):
self.last_page()
self.check_page_for_string( 'The group has been marked as not deleted' )
self.home()
- def purge_group( self, group_id, deleted=False ):
+ def purge_group( self, group_id ):
"""Purge an existing group"""
- if not deleted:
- self.mark_group_deleted( group_id )
self.visit_url( "%s/admin/purge_group?group_id=%s" % ( self.url, group_id ) )
self.last_page()
- self.check_page_for_string( 'The group has been purged from the database' )
+ self.check_page_for_string( "The following have been purged from the database for the group: UserGroupAssociations, GroupRoleAssociations." )
self.home()
# Library stuff
- def create_library( self, name='New Test Library', description='New Test Library Description' ):
+ def create_library( self, name='Library One', description='This is Library One' ):
"""Create a new library"""
try:
self.visit_url( "%s/admin/library?new=True" % self.url )
@@ -738,7 +766,7 @@ class TwillTestCase( unittest.TestCase ):
self.home()
raise AssertionError( 'Exception caught attempting to create library: %s' % str( err ) )
self.home()
- def rename_library( self, library_id, name='New Test Library Renamed', description='New Test Library Description Re-described', root_folder='' ):
+ def rename_library( self, library_id, name='Library One Renamed', description='This is Library One Re-described', root_folder='' ):
"""Rename a library"""
try:
self.visit_url( "%s/admin/library?rename=True&id=%s" % ( self.url, library_id ) )
@@ -759,7 +787,7 @@ class TwillTestCase( unittest.TestCase ):
self.home()
raise AssertionError( 'Exception caught attempting to rename a library: %s' % str( err ) )
self.home()
- def add_folder( self, folder_id, name='New Test Folder', description='New Test Folder Description' ):
+ def add_folder( self, folder_id, name='Folder One', description='NThis is Folder One' ):
"""Create a new folder"""
try:
self.visit_url( "%s/admin/folder?id=%s&new=True" % ( self.url, folder_id ) )
@@ -772,7 +800,7 @@ class TwillTestCase( unittest.TestCase ):
self.home()
raise AssertionError( 'Exception caught attempting to create a new folder: %s' % str( err ) )
self.home()
- def rename_folder( self, folder_id, name='New Test Folder Renamed', description='New Test Folder Description Re-described' ):
+ def rename_folder( self, folder_id, name='Folder One Renamed', description='This is Folder One Re-described' ):
"""Rename a Folder"""
try:
self.visit_url( "%s/admin/folder?rename=True&id=%s" % ( self.url, folder_id ) )
@@ -811,7 +839,6 @@ class TwillTestCase( unittest.TestCase ):
# Create a new history
self.new_history()
self.upload_file( "1.bed" )
- self.verify_dataset_correctness( "1.bed" )
self.visit_url( "%s/admin/add_dataset_to_folder_from_history?folder_id=%s" % ( self.url, folder_id ) )
self.last_page()
self.check_page_for_string( 'Active datasets in your current history' )
@@ -822,7 +849,7 @@ class TwillTestCase( unittest.TestCase ):
self.check_page_for_string( 'Added the following datasets to the library folder: 1.bed' )
except AssertionError, err:
self.home()
- raise AssertionError( 'Exception caught attempting to create add a dataset to a folder: %s' % str( err ) )
+ raise AssertionError( 'Exception caught attempting to add a dataset to a folder: %s' % str( err ) )
self.home()
def add_datasets_from_library_dir( self, folder_id, extension='auto', dbkey='hg18', roles_tuple=[] ):
"""Add a directory of datasets to a folder"""
diff --git a/test/functional/__init__.py b/test/functional/__init__.py
index 828bb247ef6..95722061d54 100644
--- a/test/functional/__init__.py
+++ b/test/functional/__init__.py
@@ -37,7 +37,6 @@ def setup():
galaxy_test_port = os.environ.get( 'GALAXY_TEST_PORT', default_galaxy_test_port )
start_server = 'GALAXY_TEST_EXTERNAL' not in os.environ
-
if start_server:
if 'GALAXY_TEST_DBPATH' in os.environ:
db_path = os.environ['GALAXY_TEST_DBPATH']
@@ -73,10 +72,12 @@ def setup():
tool_path = "tools",
test_conf = "test.conf",
log_destination = "stdout",
- use_heartbeat=False,
+ use_heartbeat = False,
+ allow_user_creation = True,
+ allow_user_deletion = True,
admin_users = 'test@bx.psu.edu',
library_import_dir = galaxy_test_file_dir,
- global_conf= { "__file__": "universe_wsgi.ini.sample" } )
+ global_conf = { "__file__": "universe_wsgi.ini.sample" } )
log.info( "Embedded Universe application started" )
diff --git a/test/functional/test_security_and_libraries.py b/test/functional/test_security_and_libraries.py
index 8a95da8122e..f8a8a1664d7 100644
--- a/test/functional/test_security_and_libraries.py
+++ b/test/functional/test_security_and_libraries.py
@@ -1,4 +1,3 @@
-import sys
import galaxy.model
from galaxy.model.orm import *
from base.twilltestcase import *
@@ -6,7 +5,7 @@ from base.twilltestcase import *
not_logged_in_security_msg = 'You must be logged in as an administrator to access this feature.'
logged_in_security_msg = 'You must be an administrator to access this feature.'
-class TestHistory( TwillTestCase ):
+class TestSecurityAndLibraries( TwillTestCase ):
def test_00_admin_features_when_not_logged_in( self ):
"""Testing admin_features when not logged in"""
self.logout()
@@ -113,9 +112,11 @@ class TestHistory( TwillTestCase ):
self.new_history()
latest_history = galaxy.model.History.query().order_by( desc( galaxy.model.History.table.c.create_time ) ).first()
if not latest_history.default_permissions:
- raise AssertionError( 'No DefaultHistoryPermissions were created for history id %d when DefaultHistoryPermissions were changed' % latest_history.id )
+ raise AssertionError( 'No DefaultHistoryPermissions were created for history id %d when DefaultHistoryPermissions were changed' % \
+ latest_history.id )
if len( latest_history.default_permissions ) != len( galaxy.model.Dataset.permitted_actions.items() ):
- raise AssertionError( '%d DefaultHistoryPermissions were created for history id %d, should have been %d' % ( len( latest_history.default_permissions ), latest_history.id, len( galaxy.model.Dataset.permitted_actions ) ) )
+ raise AssertionError( '%d DefaultHistoryPermissions were created for history id %d, should have been %d' % \
+ ( len( latest_history.default_permissions ), latest_history.id, len( galaxy.model.Dataset.permitted_actions ) ) )
dhps = []
for dhp in latest_history.default_permissions:
dhps.append( dhp.action )
@@ -131,7 +132,8 @@ class TestHistory( TwillTestCase ):
if not latest_dataset.actions:
raise AssertionError( 'No ActionDatasetRoleAssociations were created for dataset id %d when it was created' % latest_dataset.id )
if len( latest_dataset.actions ) != len( latest_history.default_permissions ):
- raise AssertionError( '%d ActionDatasetRoleAssociations were created for dataset id %d when it was created ( should have been %d )' % ( len( latest_dataset.actions ), latest_dataset.id, len( latest_history.default_permissions ) ) )
+ raise AssertionError( '%d ActionDatasetRoleAssociations were created for dataset id %d when it was created ( should have been %d )' % \
+ ( len( latest_dataset.actions ), latest_dataset.id, len( latest_history.default_permissions ) ) )
adras = []
for adra in latest_dataset.actions:
adras.append( adra.action )
@@ -171,7 +173,8 @@ class TestHistory( TwillTestCase ):
# Change DefaultHistoryPermissions for the current history
self.history_set_default_permissions( permissions_out=permissions_out, permissions_in=permissions_in, role_id=role_id )
if not latest_history.default_permissions:
- raise AssertionError( 'No DefaultHistoryPermissions were created for history id %d when DefaultHistoryPermissions were changed' % latest_history.id )
+ raise AssertionError( 'No DefaultHistoryPermissions were created for history id %d when DefaultHistoryPermissions were changed' % \
+ latest_history.id )
if len( latest_history.default_permissions ) != len( actions_in ):
raise AssertionError( '%d DefaultHistoryPermissions were created for history id %d, should have been %d' \
% ( len( latest_history.default_permissions ), latest_history.id, len( permissions_in ) ) )
@@ -189,7 +192,8 @@ class TestHistory( TwillTestCase ):
if not latest_dataset.actions:
raise AssertionError( 'No ActionDatasetRoleAssociations were created for dataset id %d when it was created' % latest_dataset.id )
if len( latest_dataset.actions ) != len( latest_history.default_permissions ):
- raise AssertionError( '%d ActionDatasetRoleAssociations were created for dataset id %d when it was created ( should have been %d )' % ( len( latest_dataset.actions ), latest_dataset.id, len( latest_history.default_permissions ) ) )
+ raise AssertionError( '%d ActionDatasetRoleAssociations were created for dataset id %d when it was created ( should have been %d )' \
+ % ( len( latest_dataset.actions ), latest_dataset.id, len( latest_history.default_permissions ) ) )
adras = []
for adra in latest_dataset.actions:
adras.append( adra.action )
@@ -197,55 +201,201 @@ class TestHistory( TwillTestCase ):
adras.sort()
self.home()
self.logout()
- def test_12_create_role( self ):
- """Testing creating new non-private role with 2 members"""
- self.login( email=testuser1.email )
- name = 'New Test Role'
- description = 'Very cool new test role'
- self.create_role( name=name, description=description, user_ids=[ str( testuser1.id ), str( testuser2.id ) ], private_role=testuser1.email )
+ def test_12_create_new_user_account_as_admin( self ):
+ """Testing creating a new user account as admin"""
+ self.login( email='test@bx.psu.edu' )
+ email = 'test4@bx.psu.edu'
+ password = 'testuser'
+ self.create_new_account_as_admin( email=email, password=password )
+ # Get the user object for later tests
+ global testuser4
+ testuser4 = galaxy.model.User.filter( galaxy.model.User.table.c.email=='test4@bx.psu.edu' ).first()
+ # Make sure DefaultUserPermissions were created
+ if not testuser4.default_permissions:
+ raise AssertionError( 'No DefaultUserPermissions were created for user %s when the admin created the account' % email )
+ # Make sure a private role was created for the user
+ if not testuser4.roles:
+ raise AssertionError( 'No UserRoleAssociations were created for user %s when the admin created the account' % email )
+ if len( testuser4.roles ) != 1:
+ raise AssertionError( '%d UserRoleAssociations were created for user %s when the admin created the account ( should have been 1 )' \
+ % len( testuser4.roles ) )
+ for ura in testuser4.roles:
+ role = galaxy.model.Role.get( ura.role_id )
+ if role.type != 'private':
+ raise AssertionError( 'Role created for user %s when the admin created the account is not private, type is' \
+ % str( role.type ) )
+ # Make sure a history was not created
+ histories = galaxy.model.History.filter( galaxy.model.History.table.c.user_id==testuser4.id ).all()
+ if histories:
+ raise AssertionError( 'Histories were incorrectly created for user %s when the admin created the account' % email )
+ # Make sure the user was not associated with any groups
+ if testuser4.groups:
+ raise AssertionError( 'Groups were incorrectly associated with user %s when the admin created the account' % email )
+ def test_15_reset_password_as_admin( self ):
+ """Testing reseting a user password as admin"""
+ email = 'test4@bx.psu.edu'
+ self.reset_password_as_admin( user_id=testuser4.id, password='testreset' )
+ self.home()
+ self.logout()
+ def test_18_login_after_password_reset( self ):
+ """Testing logging in after an admin reset a password - tests DefaultHistoryPermissions for accounts created by an admin"""
+ self.login( email='test4@bx.psu.edu', password='testreset' )
+ # Make sure a History and HistoryDefaultPermissions exist for the user
+ latest_history = galaxy.model.History.query().order_by( desc( galaxy.model.History.table.c.create_time ) ).first()
+ if not latest_history.user_id == testuser4.id:
+ raise AssertionError( 'A history was not created for user %s when he logged in' % email )
+ if not latest_history.default_permissions:
+ raise AssertionError( 'No DefaultHistoryPermissions were created for history id %d when it was created' % latest_history.id )
+ if len( latest_history.default_permissions ) > 1:
+ raise AssertionError( 'More than 1 DefaultHistoryPermissions were created for history id %d when it was created' % latest_history.id )
+ dhp = galaxy.model.DefaultHistoryPermissions.filter( galaxy.model.DefaultHistoryPermissions.table.c.history_id==latest_history.id ).first()
+ if not dhp.action == galaxy.model.Dataset.permitted_actions.DATASET_MANAGE_PERMISSIONS.action:
+ raise AssertionError( 'The DefaultHistoryPermission.action for history id %d is "%s", but it should be "%s"' \
+ % ( latest_history.id, dhp.action, galaxy.model.Dataset.permitted_actions.DATASET_MANAGE_PERMISSIONS.action ) )
+ # Upload a file to create a HistoryDatasetAssociation
+ self.upload_file( '1.bed' )
+ self.home()
+ self.logout()
+ def test_21_mark_user_deleted( self ):
+ """Testing marking a user account as deleted"""
+ self.login( email='test@bx.psu.edu' )
+ self.mark_user_deleted( user_id=testuser4.id )
+ def test_24_undelete_user( self ):
+ """Testing undeleting a user account"""
+ self.undelete_user( user_id=testuser4.id )
+ def test_27_create_role( self ):
+ """Testing creating new non-private role with 3 members"""
+ name = 'Role One'
+ description = 'This is Role One'
+ user_ids=[ str( testuser1.id ), str( testuser2.id ), str( testuser4.id ) ]
+ self.create_role( name=name, description=description, user_ids=user_ids, private_role=testuser1.email )
# Get the role object for later tests
- global new_test_role
- new_test_role = galaxy.model.Role.filter( galaxy.model.Role.table.c.name==name ).first()
- def test_15_create_group( self ):
- """Testing creating new group with 2 members and 1 associated role"""
- name = 'New Test Group'
- self.create_group( name=name, user_ids=[ str( testuser1.id ), str( testuser2.id ) ], role_ids=[ str( new_test_role.id ) ] )
+ global role_one
+ role_one = galaxy.model.Role.filter( galaxy.model.Role.table.c.name==name ).first()
+ # Make sure UserRoleAssociations are correct
+ if not role_one.users:
+ raise AssertionError( 'No UserRoleAssociations were created for role id %d when it was created with 3 members' % role_one.id )
+ if len( role_one.users ) != len( user_ids ):
+ raise AssertionError( '%d UserRoleAssociations were created for role id %d when it was created ( should have been %d )' \
+ % ( len( role_one.users ), role_one.id, len( user_ids ) ) )
+ # Each user should now have 2 role associations, their private role and role_one
+ for user in [ testuser1, testuser2, testuser4 ]:
+ user.refresh()
+ if not user.roles:
+ raise AssertionError( 'No UserRoleAssociations were created for user %s when a new role was created' % user.email )
+ if len( user.roles ) != 2:
+ raise AssertionError( '%d UserRoleAssociations are associated with user %s ( should be 2 )' % ( len( user.roles ), user.email ) )
+ def test_30_create_group( self ):
+ """Testing creating new group with 3 members and 1 associated role"""
+ name = 'Group One'
+ user_ids=[ str( testuser1.id ), str( testuser2.id ), str( testuser4.id ) ]
+ role_ids=[ str( role_one.id ) ]
+ self.create_group( name=name, user_ids=user_ids, role_ids=role_ids )
# Get the group object for later tests
- global new_test_group
- new_test_group = galaxy.model.Group.filter( galaxy.model.Group.table.c.name==name ).first()
- def test_18_add_group_member( self ):
+ global group_one
+ group_one = galaxy.model.Group.filter( galaxy.model.Group.table.c.name==name ).first()
+ # Make sure UserGroupAssociations are correct
+ if not group_one.users:
+ raise AssertionError( 'No UserGroupAssociations were created for group id %d when it was created with 3 members' % group_one.id )
+ if len( group_one.users ) != len( user_ids ):
+ raise AssertionError( '%d UserGroupAssociations were created for group id %d when it was created ( should have been %d )' \
+ % ( len( group_one.users ), group_one.id, len( user_ids ) ) )
+ # Each user should now have 1 group association, group_one
+ for user in [ testuser1, testuser2, testuser4 ]:
+ user.refresh()
+ if not user.groups:
+ raise AssertionError( 'No UserGroupAssociations were created for user %s when a new group was created' % user.email )
+ if len( user.groups ) != 1:
+ raise AssertionError( '%d UserGroupAssociations are associated with user %s ( should be 1 )' % ( len( user.groups ), user.email ) )
+ # Make sure GroupRoleAssociations are correct
+ if not group_one.roles:
+ raise AssertionError( 'No GroupRoleAssociations were created for group id %d when it was created with 3 members' % group_one.id )
+ if len( group_one.roles ) != len( role_ids ):
+ raise AssertionError( '%d GroupRoleAssociations were created for group id %d when it was created ( should have been %d )' \
+ % ( len( group_one.roles ), group_one.id, len( role_ids ) ) )
+ def test_33_add_group_member( self ):
"""Testing editing membership of an existing group"""
- name = 'Another Test Group'
+ name = 'Group Two'
self.create_group( name=name )
# Get the group object for later tests
- global another_test_group
- another_test_group = galaxy.model.Group.filter( galaxy.model.Group.table.c.name==name ).first()
- self.add_group_members( str( another_test_group.id ), [ str( testuser3.id ) ] )
- self.visit_url( "%s/admin/group_members_edit?group_id=%s" % ( self.url, str( another_test_group.id ) ) )
+ global group_two
+ group_two = galaxy.model.Group.filter( galaxy.model.Group.table.c.name==name ).first()
+ user_ids = [ str( testuser3.id ) ]
+ self.add_group_members( str( group_two.id ), user_ids )
+ self.visit_url( "%s/admin/group_members_edit?group_id=%s" % ( self.url, str( group_two.id ) ) )
self.check_page_for_string( testuser3.email )
- def test_21_associate_groups_with_role( self ):
+ # Make sure UserGroupAssociations are correct
+ if not group_two.users:
+ raise AssertionError( 'No UserGroupAssociations were created for group id %d when %d members were added' \
+ % ( group_two.id, len( user_ids ) ) )
+ if len( group_two.users ) != len( user_ids ):
+ raise AssertionError( '%d UserGroupAssociations were created for group id %d when %d members were added' \
+ % ( len( group_two.users ), group_two.id, len( user_ids ) ) )
+ # Create another group -needed for the following test
+ name = 'Group Three'
+ self.create_group( name=name )
+ # Get the group object for later tests
+ global group_three
+ group_three = galaxy.model.Group.filter( galaxy.model.Group.table.c.name==name ).first()
+ def test_36_associate_groups_with_role( self ):
"""Testing adding existing groups to an existing role"""
# NOTE: To get this to work with twill, all select lists on the ~/admin/role page must contain at least
# 1 option value or twill throws an exception, which is: ParseError: OPTION outside of SELECT
- # Due to this bug in twill, we create the role, associating it with at least 1 user and 1 group...
- name = 'Another Test Role'
- description = 'Another cool new test role'
+ # Due to this bug in twill, we create the role, associating it with at least 1 user and 1 group. We
+ # also must ensure that each of the form fields will contain at least 1 value prior to submitting the form,
+ # so we had to create group_three in the previous test
+ name = 'Role Two'
+ description = 'This is Role Two'
+ user_ids=[ str( testuser1.id ) ]
+ group_ids=[ str( group_two.id ) ]
+ private_role=testuser1.email
+ # STEP 1: create the role
self.create_role( name=name,
description=description,
- user_ids=[ str( testuser1.id ) ],
- group_ids=[ str( another_test_group.id ) ],
- private_role=testuser1.email )
+ user_ids=user_ids,
+ group_ids=group_ids,
+ private_role=private_role )
# Get the role object for later tests
- global another_test_role
- another_test_role = galaxy.model.Role.filter( galaxy.model.Role.table.c.name==name ).first()
- # ...and then we associate the role with a group not yet associated
- self.associate_groups_with_role( str( another_test_role.id ), group_ids=[ str( new_test_group.id ) ] )
- self.visit_page( 'admin/roles' )
- self.check_page_for_string( new_test_group.name )
- def test_24_create_library( self ):
+ global role_two
+ role_two = galaxy.model.Role.filter( galaxy.model.Role.table.c.name==name ).first()
+ # Make sure UserRoleAssociations are correct
+ if not role_two.users:
+ raise AssertionError( 'No UserRoleAssociations were created for role id %d when it was created with %d members' \
+ % ( role_two.id, len( user_ids ) ) )
+ if len( role_two.users ) != len( user_ids ):
+ raise AssertionError( '%d UserRoleAssociations were created for role id %d when it was created with %d members' \
+ % ( len( role_two.users ), role_two.id, len( user_ids ) ) )
+ # testuser1 should now have 3 role associations, private role, role_one, another+test_role
+ for user in [ testuser1 ]:
+ user.refresh()
+ if not user.roles:
+ raise AssertionError( 'No UserRoleAssociations were created for user %s when a new role was created' % user.email )
+ if len( user.roles ) != 3:
+ raise AssertionError( '%d UserRoleAssociations are associated with user %s ( should be 3 )' % ( len( user.roles ), user.email ) )
+ # Make sure GroupRoleAssociations are correct
+ if not role_two.groups:
+ raise AssertionError( 'No GroupRoleAssociations were created for role id %d when it was created with %d groups' \
+ % ( role_two.id, len( group_ids ) ) )
+ if len( role_two.groups ) != len( group_ids ):
+ raise AssertionError( '%d GroupRoleAssociations were created for role id %d when it was created ( should have been %d )' \
+ % ( len( role_two.groups ), role_two.id, len( group_ids ) ) )
+ # The group should also now be associated with 1 role
+ for group in [ group_two ]:
+ group.refresh()
+ if not group.roles:
+ raise AssertionError( 'No GroupRoleAssociations were created for group id %d when a new role was created' % group.id )
+ if len( group.roles ) != 1:
+ raise AssertionError( '%d GroupRoleAssociations are associated with group id %d ( should be 1 )' % ( len( group.roles ), group.id ) )
+ # STEP 2: associate the role with a group not yet associated
+ # TODO: Twill throws an exception on this...
+ #group_names = [ group_one.name ]
+ #self.associate_groups_with_role( str( role_two.id ), group_names=group_names )
+ #self.visit_page( 'admin/roles' )
+ #self.check_page_for_string( group_one.name )
+ def test_39_create_library( self ):
"""Testing creating new library"""
- name = 'New Test Library'
- description = 'New Test Library Description'
+ name = 'Library One'
+ description = 'This is Library One'
self.create_library( name=name, description=description )
self.visit_page( 'admin/libraries' )
self.check_page_for_string( name )
@@ -254,20 +404,20 @@ class TestHistory( TwillTestCase ):
library = galaxy.model.Library.filter( and_( galaxy.model.Library.table.c.name==name,
galaxy.model.Library.table.c.description==description,
galaxy.model.Library.table.c.deleted==False ) ).first()
- def test_27_rename_library( self ):
+ def test_42_rename_library( self ):
"""Testing renaming a library"""
- self.rename_library( str( library.id ), name='New Test Library Renamed', description='New Test Library Description Re-described', root_folder='on' )
+ self.rename_library( str( library.id ), name='Library One Renamed', description='This is Library One Re-described', root_folder='on' )
self.visit_page( 'admin/libraries' )
- self.check_page_for_string( "New Test Library Renamed" )
+ self.check_page_for_string( "Library One Renamed" )
# Rename it back to what it was originally
- self.rename_library( str( library.id ), name='New Test Library', description='New Test Library Description', root_folder='on' )
- def test_30_rename_root_folder( self ):
+ self.rename_library( str( library.id ), name='Library One', description='This is Library One', root_folder='on' )
+ def test_45_rename_root_folder( self ):
"""Testing renaming a library root folder"""
folder = library.root_folder
- self.rename_folder( str( folder.id ), name='New Test Library Root Folder', description='New Test Library Root Folder Description' )
+ self.rename_folder( str( folder.id ), name='Library One Root Folder', description='This is Library One root folder' )
self.visit_page( 'admin/libraries' )
- self.check_page_for_string( "New Test Library Root Folder" )
- def test_33_add_public_dataset_to_root_folder( self ):
+ self.check_page_for_string( "Library One Root Folder" )
+ def test_48_add_public_dataset_to_root_folder( self ):
"""Testing adding a public dataset to a library root folder"""
folder = library.root_folder
self.add_dataset( '1.bed', str( folder.id ), extension='bed', dbkey='hg18', roles=[] )
@@ -275,7 +425,7 @@ class TestHistory( TwillTestCase ):
self.check_page_for_string( "1.bed" )
self.check_page_for_string( "bed" )
self.check_page_for_string( "hg18" )
- def test_36_copy_dataset_from_history_to_root_folder( self ):
+ def test_51_copy_dataset_from_history_to_root_folder( self ):
"""Testing copying a dataset from the current history to a library root folder"""
folder = library.root_folder
self.add_dataset_to_folder_from_history( str( folder.id ) )
@@ -289,40 +439,41 @@ class TestHistory( TwillTestCase ):
raise AssertionError( 'More than 1 ActionDatasetRoleAssociations created for dataset id: %d' % last_dataset_created.id )
for adra in adras:
if not adra.action == 'manage permissions':
- raise AssertionError( 'ActionDatasetRoleAssociation.action "%s" is not the DefaultHistoryPermission setting, which is "manage permissions"' % str( adra.action ) )
- def test_39_add_new_folder( self ):
+ raise AssertionError( 'ActionDatasetRoleAssociation.action "%s" is not the DefaultHistoryPermission setting, which is "manage permissions"' % \
+ str( adra.action ) )
+ def test_54_add_new_folder( self ):
"""Testing adding a folder to a library root folder"""
root_folder = library.root_folder
- name = 'New Test Folder'
- description = 'New Test Folder Description'
+ name = 'Folder One'
+ description = 'This is Folder One'
self.add_folder( str( root_folder.id ), name=name, description=description )
global new_test_folder
new_test_folder = galaxy.model.LibraryFolder.filter( and_( galaxy.model.LibraryFolder.table.c.parent_id==root_folder.id,
galaxy.model.LibraryFolder.table.c.name==name,
galaxy.model.LibraryFolder.table.c.description==description ) ).first()
self.visit_page( 'admin/libraries' )
- self.check_page_for_string( "New Test Folder" )
- def test_42_add_datasets_from_library_dir( self ):
+ self.check_page_for_string( "Folder One" )
+ def test_57_add_datasets_from_library_dir( self ):
"""Testing adding several datasets from library directory to sub-folder"""
- roles_tuple = [ ( str( new_test_role.id ), new_test_role.description ) ]
+ roles_tuple = [ ( str( role_one.id ), role_one.description ) ]
self.add_datasets_from_library_dir( str( new_test_folder.id ), roles_tuple=roles_tuple )
- def test_45_mark_group_deleted( self ):
+ def test_60_mark_group_deleted( self ):
"""Testing marking a group as deleted"""
self.visit_page( "admin/groups" )
- self.check_page_for_string( another_test_group.name )
- self.mark_group_deleted( str( another_test_group.id ) )
- def test_48_undelete_group( self ):
+ self.check_page_for_string( group_two.name )
+ self.mark_group_deleted( str( group_two.id ) )
+ def test_63_undelete_group( self ):
"""Testing undeleting a deleted group"""
- self.undelete_group( str( another_test_group.id ) )
- def test_51_mark_role_deleted( self ):
+ self.undelete_group( str( group_two.id ) )
+ def test_66_mark_role_deleted( self ):
"""Testing marking a role as deleted"""
self.visit_page( "admin/roles" )
- self.check_page_for_string( another_test_role.description )
- self.mark_role_deleted( str( another_test_role.id ) )
- def test_54_undelete_role( self ):
+ self.check_page_for_string( role_two.description )
+ self.mark_role_deleted( str( role_two.id ) )
+ def test_69_undelete_role( self ):
"""Testing undeleting a deleted role"""
- self.undelete_role( str( another_test_role.id ) )
- def test_57_mark_library_deleted( self ):
+ self.undelete_role( str( role_two.id ) )
+ def test_72_mark_library_deleted( self ):
"""Testing marking a library as deleted"""
self.mark_library_deleted( str( library.id ) )
# Make sure the library was deleted
@@ -334,19 +485,21 @@ class TestHistory( TwillTestCase ):
folder.refresh()
# Make sure all of the library_folders are deleted
if not folder.deleted:
- raise AssertionError( 'The library_folder named "%s" has not been marked as deleted ( library.id: %s ).' % ( folder.name, str( library.id ) ) )
+ raise AssertionError( 'The library_folder named "%s" has not been marked as deleted ( library.id: %s ).' % \
+ ( folder.name, str( library.id ) ) )
check_folder( folder )
# Make sure all of the library_folder_dataset_associations are deleted
for lfda in library_folder.datasets:
lfda.refresh()
if not lfda.deleted:
- raise AssertionError( 'The library_folder_dataset_association id %s named "%s" has not been marked as deleted ( library.id: %s ).' % ( str( lfda.id ), lfda.name, str( library.id ) ) )
+ raise AssertionError( 'The library_folder_dataset_association id %s named "%s" has not been marked as deleted ( library.id: %s ).' % \
+ ( str( lfda.id ), lfda.name, str( library.id ) ) )
# Make sure none of the datasets have been deleted since that should occur only when the library is purged
lfda.dataset.refresh()
if lfda.dataset.deleted:
raise AssertionError( 'The dataset with id "%s" has been marked as deleted when it should not have been.' % lfda.dataset.id )
check_folder( library.root_folder )
- def test_60_mark_library_undeleted( self ):
+ def test_75_mark_library_undeleted( self ):
"""Testing marking a library as not deleted"""
self.mark_library_undeleted( str( library.id ) )
# Make sure the library is undeleted
@@ -358,13 +511,15 @@ class TestHistory( TwillTestCase ):
folder.refresh()
# Make sure all of the library_folders are undeleted
if folder.deleted:
- raise AssertionError( 'The library_folder id %s named "%s" has not been marked as undeleted ( library.id: %s ).' % ( str( folder.id ), folder.name, str( library.id ) ) )
+ raise AssertionError( 'The library_folder id %s named "%s" has not been marked as undeleted ( library.id: %s ).' % \
+ ( str( folder.id ), folder.name, str( library.id ) ) )
check_folder( folder )
# Make sure all of the library_folder_dataset_associations are undeleted
for lfda in library_folder.datasets:
lfda.refresh()
if lfda.deleted:
- raise AssertionError( 'The library_folder_dataset_association id %s named "%s" has not been marked as undeleted ( library.id: %s ).' % ( str( lfda.id ), lfda.name, str( library.id ) ) )
+ raise AssertionError( 'The library_folder_dataset_association id %s named "%s" has not been marked as undeleted ( library.id: %s ).' % \
+ ( str( lfda.id ), lfda.name, str( library.id ) ) )
# Make sure all of the datasets have been undeleted
if lfda.dataset.deleted:
raise AssertionError( 'The dataset with id "%s" has not been marked as undeleted.' % lfda.dataset.id )
@@ -374,10 +529,55 @@ class TestHistory( TwillTestCase ):
# Make sure the library is deleted again
library.refresh()
if not library.deleted:
- raise AssertionError( 'The library id %s named "%s" has not been marked as deleted after it was undeleted.' % ( str( library.id ), library.name ) )
- def test_63_purge_group( self ):
+ raise AssertionError( 'The library id %s named "%s" has not been marked as deleted after it was undeleted.' % \
+ ( str( library.id ), library.name ) )
+ def test_78_purge_user( self ):
+ """Testing purging a user account"""
+ self.mark_user_deleted( user_id=testuser4.id )
+ self.purge_user( user_id=testuser4.id )
+ testuser4.refresh()
+ if not testuser4.purged:
+ raise AssertionError( 'User %s was not marked as purged.' % testuser4.email )
+ # Make sure DefaultUserPermissions deleted EXCEPT FOR THE PRIVATE ROLE
+ if len( testuser4.default_permissions ) != 1:
+ raise AssertionError( 'DefaultUserPermissions for user %s were not deleted.' % testuser4.email )
+ for dup in testuser4.default_permissions:
+ role = galaxy.model.Role.get( dup.role_id )
+ if role.type != 'private':
+ raise AssertionError( 'DefaultUserPermissions for user %s are not related with the private role.' % testuser4.email )
+ # Make sure History deleted
+ for history in testuser4.histories:
+ if not history.deleted:
+ raise AssertionError( 'User %s has active history id %d after their account was marked as purged.' % ( testuser4.email, hda.id ) )
+ # Make sure DefaultHistoryPermissions deleted EXCEPT FOR THE PRIVATE ROLE
+ if len( history.default_permissions ) != 1:
+ raise AssertionError( 'DefaultHistoryPermissions for history id %d were not deleted.' % history.id )
+ for dhp in history.default_permissions:
+ role = galaxy.model.Role.get( dhp.role_id )
+ if role.type != 'private':
+ raise AssertionError( 'DefaultHistoryPermissions for history id %d are not related with the private role.' % history.id )
+ # Make sure HistoryDatasetAssociation deleted
+ for hda in history.datasets:
+ if not hda.deleted:
+ raise AssertionError( 'HistoryDatasetAssociation id %d was not deleted.' % hda.id )
+ # Make sure Dataset deleted
+ d = galaxy.model.Dataset.filter( galaxy.model.Dataset.table.c.id==hda.dataset_id ).first()
+ if not d.deleted:
+ raise AssertionError( 'Dataset id %d was not deleted.' % d.id )
+ # Make sure UserGroupAssociations deleted
+ if testuser4.groups:
+ raise AssertionError( 'User %s has active group id %d after their account was marked as purged.' % ( testuser4.email, uga.id ) )
+ # Make sure UserRoleAssociations deleted EXCEPT FOR THE PRIVATE ROLE
+ if len( testuser4.roles ) != 1:
+ raise AssertionError( 'UserRoleAssociations for user %s were not deleted.' % testuser4.email )
+ for ura in testuser4.roles:
+ role = galaxy.model.Role.get( ura.role_id )
+ if role.type != 'private':
+ raise AssertionError( 'UserRoleAssociations for user %s are not related with the private role.' % testuser4.email )
+ def test_81_purge_group( self ):
"""Testing purging a group"""
- group_id = str( another_test_group.id )
+ group_id = str( group_two.id )
+ self.mark_group_deleted( group_id )
self.purge_group( group_id )
# Make sure there are no UserGroupAssociations
uga = galaxy.model.UserGroupAssociation.filter( galaxy.model.UserGroupAssociation.table.c.group_id == group_id ).all()
@@ -387,10 +587,23 @@ class TestHistory( TwillTestCase ):
gra = galaxy.model.GroupRoleAssociation.filter( galaxy.model.GroupRoleAssociation.table.c.group_id == group_id ).all()
if gra:
raise AssertionError( "Purging the group did not delete the GroupRoleAssociations for group_id '%s'" % group_id )
- def test_66_purge_role( self ):
+ def test_84_purge_role( self ):
"""Testing purging a role"""
- role_id = str( another_test_role.id )
+ role_id = str( role_two.id )
+ self.mark_role_deleted( role_id )
self.purge_role( role_id )
+ # Make sure there are no UserRoleAssociations
+ uras = galaxy.model.UserRoleAssociation.filter( galaxy.model.UserRoleAssociation.table.c.role_id == role_id ).all()
+ if uras:
+ raise AssertionError( "Purging the role did not delete the UserRoleAssociations for role_id '%s'" % role_id )
+ # Make sure there are no DefaultUserPermissions associated with the Role
+ dups = galaxy.model.DefaultUserPermissions.filter( galaxy.model.DefaultUserPermissions.table.c.role_id == role_id ).all()
+ if dups:
+ raise AssertionError( "Purging the role did not delete the DefaultUserPermissions for role_id '%s'" % role_id )
+ # Make sure there are no DefaultHistoryPermissions associated with the Role
+ dhps = galaxy.model.DefaultHistoryPermissions.filter( galaxy.model.DefaultHistoryPermissions.table.c.role_id == role_id ).all()
+ if dhps:
+ raise AssertionError( "Purging the role did not delete the DefaultHistoryPermissions for role_id '%s'" % role_id )
# Make sure there are no GroupRoleAssociations
gra = galaxy.model.GroupRoleAssociation.filter( galaxy.model.GroupRoleAssociation.table.c.role_id == role_id ).all()
if gra:
@@ -399,7 +612,7 @@ class TestHistory( TwillTestCase ):
adra = galaxy.model.ActionDatasetRoleAssociation.filter( galaxy.model.ActionDatasetRoleAssociation.table.c.role_id == role_id ).all()
if adra:
raise AssertionError( "Purging the role did not delete the ActionDatasetRoleAssociations for role_id '%s'" % role_id )
- def test_69_purge_library( self ):
+ def test_87_purge_library( self ):
"""Testing purging a library"""
self.purge_library( str( library.id ) )
# Make sure the library was purged
@@ -417,10 +630,12 @@ class TestHistory( TwillTestCase ):
for lfda in library_folder.datasets:
lfda.refresh()
if not lfda.deleted:
- raise AssertionError( 'The library_folder_dataset_association id %s named "%s" has not been marked as deleted.' % ( str( lfda.id ), lfda.name ) )
+ raise AssertionError( 'The library_folder_dataset_association id %s named "%s" has not been marked as deleted.' % \
+ ( str( lfda.id ), lfda.name ) )
# Make sure all of the datasets have been deleted
dataset = lfda.dataset
dataset.refresh()
if not dataset.deleted:
- raise AssertionError( 'The dataset with id "%s" has not been marked as deleted when it should have been.' % str( lfda.dataset.id ) )
+ raise AssertionError( 'The dataset with id "%s" has not been marked as deleted when it should have been.' % \
+ str( lfda.dataset.id ) )
check_folder( library.root_folder )
diff --git a/universe_wsgi.ini.sample b/universe_wsgi.ini.sample
index c6bc12a88ce..63021acc775 100644
--- a/universe_wsgi.ini.sample
+++ b/universe_wsgi.ini.sample
@@ -141,6 +141,9 @@ use_interactive = True
# Can users register new accounts?
#allow_user_creation = True
+# Can an admin user delete user accounts?
+#allow_user_deletion = False
+
# ---- Job Execution --------------------------------------------------------
# Number of concurrent jobs to run (local job runner)