Reintroduce roles. Unfinished stuff: setting default user and history

permissions, user-side permissions settings on datasets, viewing
associated datasets under the role page in the admin controler (is this
even necessary?), filtering out roles by type.  I haven't addressed
tests, so the security-related functional tests will fail.  I haven't
looked at history sharing yet either.  Cleanup to remove unused methods
is needed in the admin controller.  And the admin templates are a
bit messy.
This commit is contained in:
Nate Coraor
2008-10-09 17:05:57 -04:00
parent 8887c27785
commit dfdbef9d36
26 changed files with 642 additions and 849 deletions
+59 -55
View File
@@ -103,70 +103,16 @@ class JobToOutputDatasetAssociation( object ):
self.name = name
self.dataset = dataset
class GroupDatasetAssociation( object ):
def __init__( self, group, dataset, permitted_actions=[] ):
if isinstance( group, GroupDatasetAssociation ) or \
isinstance( group, DefaultUserGroupAssociation ) or \
isinstance( group, DefaultHistoryGroupAssociation ):
group = group.group
self.group = group
if isinstance( dataset, HistoryDatasetAssociation ):
dataset = dataset.dataset
self.dataset = dataset
self.permitted_actions = permitted_actions
def add_permitted_action( self, action ):
if action not in self.permitted_actions:
return self.permitted_actions.append( action )
raise 'action (%s) already exists in permitted actions list (%s: %s).' % ( action, str( self.id ), str( self.permitted_actions ) )
def remove_permitted_action( self, action ):
return self.permitted_actions.remove( action )
class Group( object ):
public_id = None
permitted_actions = galaxy.security.get_permitted_actions( 'GROUP' )
def __init__( self, name = None, priority = 0 ):
def __init__( self, name = None ):
self.name = name
self.priority = priority
@classmethod
def get_public_group( cls ):
return Group.get( cls.public_id )
@classmethod
def set_public_group( cls, group ):
# We store the id instead of the object, because of alchemy sessions
if isinstance( group, Group ):
group = group.id
cls.public_id = group
@classmethod
def guess_public_group( cls ):
# Retrieve from database and store public group id
group = Group.filter_by( name='public' ).first()
cls.set_public_group( group )
class UserGroupAssociation( object ):
def __init__( self, user, group ):
self.user = user
self.group = group
class DefaultUserGroupAssociation( object ):
def __init__( self, user, group, permitted_actions ):
if isinstance( group, GroupDatasetAssociation ) or \
isinstance( group, DefaultUserGroupAssociation ) or \
isinstance( group, DefaultHistoryGroupAssociation ):
group = group.group
self.user = user
self.group = group
self.permitted_actions = permitted_actions
class DefaultHistoryGroupAssociation( object ):
def __init__( self, history, group, permitted_actions ):
if isinstance( group, GroupDatasetAssociation ) or \
isinstance( group, DefaultUserGroupAssociation ) or \
isinstance( group, DefaultHistoryGroupAssociation ):
group = group.group
self.history = history
self.group = group
self.permitted_actions = permitted_actions
class History( object ):
def __init__( self, id=None, name=None, user=None ):
self.id = id
@@ -239,6 +185,64 @@ class History( object ):
# self.history = history
# self.datasets = []
class UserRoleAssociation( object ):
def __init__( self, user, role ):
self.user = user
self.role = role
class GroupRoleAssociation( object ):
def __init__( self, group, role ):
self.group = group
self.role = role
class Role( object ):
private_id = None
types = Bunch(
PRIVATE = 'private',
SYSTEM = 'system',
USER = 'user',
ADMIN = 'admin'
)
def __init__( self, name="", description="", type="system", deleted=False ):
self.name = name
self.description = description
self.type = type
self.deleted = deleted
class ActionObjectRolesAssociation( object ):
"""
Base class for an action->something->roles association, so set_roles
doesn't have to be a member of each class that uses it.
"""
def set_roles( self, roles ):
"""
Convenience method to allow roles to be a list of Roles or role ids.
"""
def get_id( x ):
if isinstance( x, Role ):
return x.id
else:
return x
self.role_ids = map( get_id, roles )
class ActionDatasetRolesAssociation( ActionObjectRolesAssociation ):
def __init__( self, action, dataset, roles ):
self.action = action
self.dataset = dataset
self.set_roles( roles )
class DefaultUserPermissions( ActionObjectRolesAssociation ):
def __init__( self, user, action, roles ):
self.user = user
self.action = action
self.set_roles( roles )
class DefaultHistoryPermissions( ActionObjectRolesAssociation ):
def __init__( self, history, action, roles ):
self.history = history
self.action = action
self.set_roles( roles )
class Dataset( object ):
states = Bunch( NEW = 'new',
QUEUED = 'queued',
+75 -39
View File
@@ -117,7 +117,6 @@ Group.table = Table( "galaxy_group", metadata,
Column( "create_time", DateTime, default=now ),
Column( "update_time", DateTime, default=now, onupdate=now ),
Column( "name", TEXT, index=True, unique=True ),
Column( "priority", Integer ),
Column( "deleted", Boolean, index=True, default=False ) )
UserGroupAssociation.table = Table( "user_group_association", metadata,
@@ -127,32 +126,49 @@ UserGroupAssociation.table = Table( "user_group_association", metadata,
Column( "create_time", DateTime, default=now ),
Column( "update_time", DateTime, default=now, onupdate=now ) )
GroupDatasetAssociation.table = Table( "group_dataset_association", metadata,
UserRoleAssociation.table = Table( "user_role_association", metadata,
Column( "id", Integer, primary_key=True ),
Column( "group_id", Integer, ForeignKey( "galaxy_group.id" ), index=True ),
Column( "dataset_id", Integer, ForeignKey( "dataset.id" ), index=True ),
Column( "create_time", DateTime, default=now ),
Column( "update_time", DateTime, default=now, onupdate=now ),
Column( "permitted_actions", JSONType(), default=[] ) )
# The following table stores the permissions that are considered the defaults for new histories when they are created by a user
DefaultUserGroupAssociation.table = Table( "default_user_group_association", metadata,
Column( "id", Integer, primary_key=True ),
Column( "group_id", Integer, ForeignKey( "galaxy_group.id" ), index=True ),
Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ),
Column( "role_id", Integer, ForeignKey( "role.id" ), index=True ),
Column( "create_time", DateTime, default=now ),
Column( "update_time", DateTime, default=now, onupdate=now ),
Column( "permitted_actions", JSONType(), default=[] ) )
Column( "update_time", DateTime, default=now, onupdate=now ) )
# The following table stores the default permissions assigned to histories for datasets
# that need permissions ( dataset permissions that cannot be determined based on ancestor )
DefaultHistoryGroupAssociation.table = Table( "default_history_group_association", metadata,
GroupRoleAssociation.table = Table( "group_role_association", metadata,
Column( "id", Integer, primary_key=True ),
Column( "group_id", Integer, ForeignKey( "galaxy_group.id" ), index=True ),
Column( "history_id", Integer, ForeignKey( "history.id" ), index=True ),
Column( "role_id", Integer, ForeignKey( "role.id" ), index=True ),
Column( "create_time", DateTime, default=now ),
Column( "update_time", DateTime, default=now, onupdate=now ) )
Role.table = Table( "role", metadata,
Column( "id", Integer, primary_key=True ),
Column( "create_time", DateTime, default=now ),
Column( "update_time", DateTime, default=now, onupdate=now ),
Column( "permitted_actions", JSONType(), default=[] ) )
Column( "name", TEXT, index=True, unique=True ),
Column( "description", TEXT ),
Column( "type", TEXT, index=True ),
Column( "deleted", Boolean, index=True, default=False ) )
# TODO: should role_ids be made in to another association table?
ActionDatasetRolesAssociation.table = Table( "action_dataset_roles_association", metadata,
Column( "id", Integer, primary_key=True ),
Column( "create_time", DateTime, default=now ),
Column( "update_time", DateTime, default=now, onupdate=now ),
Column( "action", TEXT ),
Column( "dataset_id", Integer, ForeignKey( "dataset.id" ), index=True ),
Column( "role_ids", JSONType(), default=[] ) )
DefaultUserPermissions.table = Table( "default_user_permissions", metadata,
Column( "id", Integer, primary_key=True ),
Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ),
Column( "action", TEXT ),
Column( "role_ids", JSONType(), default=[] ) )
DefaultHistoryPermissions.table = Table( "default_history_permissions", metadata,
Column( "id", Integer, primary_key=True ),
Column( "history_id", Integer, ForeignKey( "history.id" ), index=True ),
Column( "action", TEXT ),
Column( "role_ids", JSONType(), default=[] ) )
LibraryFolderDatasetAssociation.table = Table( "library_folder_dataset_association", metadata,
Column( "id", Integer, primary_key=True ),
@@ -399,7 +415,7 @@ assign_mapper( context, History, History.table,
) )
assign_mapper( context, User, User.table,
properties=dict( histories=relation( History, backref="user",
properties=dict( histories=relation( History, backref="user",
order_by=desc(History.table.c.update_time) ),
active_histories=relation( History, primaryjoin=( ( History.table.c.user_id == User.table.c.id ) & ( not_( History.table.c.deleted ) ) ), order_by=desc( History.table.c.update_time ) ),
stored_workflow_menu_entries=relation( StoredWorkflowMenuEntry, backref="user",
@@ -408,29 +424,49 @@ assign_mapper( context, User, User.table,
) )
assign_mapper( context, Group, Group.table,
properties=dict( users=relation( UserGroupAssociation ),
datasets=relation( GroupDatasetAssociation ) ) )
properties=dict( users=relation( UserGroupAssociation ) ) )
assign_mapper( context, UserGroupAssociation, UserGroupAssociation.table,
properties=dict( user=relation( User, backref = "groups" ),
group=relation( Group, backref = "members" ) ) )
assign_mapper( context, GroupDatasetAssociation, GroupDatasetAssociation.table,
properties=dict( dataset=relation( Dataset, backref = "groups" ),
group=relation( Group, backref = "group_datasets" ) ) )
assign_mapper( context, DefaultUserPermissions, DefaultUserPermissions.table,
properties=dict( user=relation( User, backref = "default_permissions" ) ) )
assign_mapper( context, DefaultUserGroupAssociation, DefaultUserGroupAssociation.table,
properties=dict( user=relation( User, backref = "default_groups" ),
group=relation( Group ) ) )
assign_mapper( context, DefaultHistoryPermissions, DefaultHistoryPermissions.table,
properties=dict( history=relation( History, backref = "default_permissions" ) ) )
assign_mapper( context, DefaultHistoryGroupAssociation, DefaultHistoryGroupAssociation.table,
properties=dict( history=relation( History, backref = "default_groups" ),
group=relation( Group ) ) )
assign_mapper( context, Role, Role.table,
properties=dict(
users=relation( UserRoleAssociation ),
groups=relation( GroupRoleAssociation )
)
)
assign_mapper( context, UserRoleAssociation, UserRoleAssociation.table,
properties=dict(
user=relation( User, backref="roles" ),
role=relation( Role )
)
)
assign_mapper( context, GroupRoleAssociation, GroupRoleAssociation.table,
properties=dict(
group=relation( Group, backref="roles" ),
role=relation( Role )
)
)
assign_mapper( context, ActionDatasetRolesAssociation, ActionDatasetRolesAssociation.table,
properties=dict(
dataset=relation( Dataset, backref="actions" )
)
)
assign_mapper( context, Library, Library.table,
properties=dict(
root_folder=relation( LibraryFolder,
backref = backref( "library_root" ) )
backref=backref( "library_root" ) )
) )
assign_mapper( context, LibraryFolder, LibraryFolder.table,
@@ -605,13 +641,13 @@ def init( file_path, url, engine_options={}, create_tables=False ):
result.create_tables = create_tables
#load local galaxy security policy
result.security_agent = GalaxyRBACAgent( result )
# Ensure group named 'public' exists
public_group = result.Group.filter_by( name='public' ).first()
if not public_group:
public_group = result.security_agent.create_group( name = 'public' )
# Store public group id
result.security_agent.set_public_group( public_group )
log.debug( "Public Group identified as id = %s." % ( public_group.id ) )
# Create private roles if necessary.
if not len( result.Role.select() ):
for user in result.User.select():
role = Role( name = user.email, description = 'Private Role for ' + user.email, type = 'private' )
role.flush()
ura = UserRoleAssociation( user = user, role = role )
ura.flush()
return result
def get_suite():
+216 -198
View File
@@ -7,52 +7,55 @@ from galaxy.util.bunch import Bunch
log = logging.getLogger(__name__)
class Action( object ):
def __init__( self, action, description, model ):
self.action = action
self.description = description
self.model = model
class RBACAgent:
"""Class that handles galaxy security"""
permitted_actions = Bunch(
# The ability to edit the metadata of the associated dataset
DATASET_EDIT_METADATA = 'edit metadata',
# The ability to change the permissions of a dataset (so specifically, to add and modify
# group_dataset_association rows where the dataset is the dataset for which the permission is set).
DATASET_MANAGE_PERMISSIONS = 'manage permissions',
# The ability to perform any read only operation on the dataset (view, display at external site,
# use in a job, etc).
DATASET_ACCESS = 'access'
)
permitted_action_descriptions = Bunch(
DATASET_EDIT_METADATA = "User can edit this dataset's metadata in the library",
DATASET_MANAGE_PERMISSIONS = "User can manage the groups and group permitted actions associated with this dataset",
DATASET_ACCESS = "User can import this dataset into their history for analysis"
DATASET_EDIT_METADATA = Action(
"edit metadata", "Role members can edit this dataset's metadata in the library", "grant" ),
DATASET_MANAGE_PERMISSIONS = Action(
"manage permissions", "Role members can manage the groups and group permitted actions associated with this dataset", "grant" ),
DATASET_ACCESS = Action(
"access", "Role members can import this dataset into their history for analysis", "restrict" )
)
def get_action( self, name, default=None ):
"""
Get a permitted action by its dict key or action name
"""
for k, v in self.permitted_actions.items():
if k == name or v.action == name:
return v
return default
def get_actions( self ):
"""
Get all permitted actions as a list
"""
return self.permitted_actions.__dict__.values()
def allow_action( self, user, action, **kwd ):
raise 'No valid method of checking action (%s) on %s for user %s.' % ( action, kwd, user )
def guess_derived_permissions_for_datasets( self, datasets = [] ):
raise "Unimplemented Method"
def associate_components( self, **kwd ):
raise 'No valid method of associating provided components: %s' % kwd
def get_group( self, id ):
raise 'No valid method of retrieving group %s' % ( id )
def create_group( self, **kwd ):
raise 'No valid method of creating group with %s' % ( kwd )
def create_private_user_group( self, user ):
def create_private_user_role( self, user ):
raise "Unimplemented Method"
def user_set_default_access( self, user, permissions = None, history = False, dataset = False ):
def get_private_user_role( self, user ):
raise "Unimplemented Method"
def user_set_default_permissions( self, user, permissions = None, history = False, dataset = False ):
raise "Unimplemented Method"
def setup_new_user( self, user ):
self.user_set_default_access( user, history = True, dataset = True )
self.associate_components( user=user, group=self.get_public_group() )
def history_set_default_access( self, history, permissions=None, dataset=False ):
raise "Unimplemented Method"
def set_public_group( self, group ):
raise "Unimplemented Method"
def get_public_group( self ):
raise "Unimplemented Method"
def guess_public_group( self ):
self.create_private_user_role( user )
self.user_set_default_permissions( user, history = True, dataset = True )
#self.associate_components( user=user, group=self.get_public_group() )
def history_set_default_permissions( self, history, permissions=None, dataset=False, bypass_manage_permission=False ):
raise "Unimplemented Method"
def set_dataset_permissions( self, dataset, permissions ):
raise "Unimplemented Method"
def set_dataset_permitted_actions( self, dataset ):
raise "Unimplemented Method"
def get_component_associations( self, **kwd ):
raise "Unimplemented Method"
def components_are_associated( self, **kwd ):
@@ -63,18 +66,6 @@ class RBACAgent:
form, ensure that they match our actual permitted actions.
"""
return filter( lambda x: x is not None, [ self.permitted_actions.get( action_string ) for action_string in permitted_action_strings ] )
def get_permitted_action_description( self, permitted_action ):
"""
Return the description of a permitted_action, regardless of
whether permitted_action is a key or value.
"""
if self.permitted_action_descriptions.get( permitted_action ) is not None:
return self.permitted_action_descriptions.get( permitted_action )
else:
for k, v in self.permitted_actions.items():
if v == permitted_action:
return self.permitted_action_descriptions.get( k )
return permitted_action # so at least something useful is printable
class GalaxyRBACAgent( RBACAgent ):
def __init__( self, model, permitted_actions=None ):
@@ -89,184 +80,212 @@ class GalaxyRBACAgent( RBACAgent ):
"""Returns true when user has permission to perform an action"""
if not isinstance( dataset, self.model.Dataset ):
dataset = dataset.dataset
if user is None:
try:
public_assoc = [ gda for gda in dataset.groups if gda.group == self.get_public_group() ][0]
except:
# There'll be an IndexError if the dataset doesn't have a gda with public
return False
if action in public_assoc.permitted_actions:
return True
elif user is not None:
# Loop through permitted_actions and if allowed return true:
# Check permitted_actions associated with dataset through groups
for group_dataset_assoc in dataset.groups:
if self.components_are_associated( user = user, group = group_dataset_assoc.group ):
if action in group_dataset_assoc.permitted_actions:
return True
return False # No user and dataset not in public group, or user lacks permission
if not user:
if action == self.permitted_actions.DATASET_ACCESS and action.action not in [ adra.action for adra in dataset.actions ]:
return True # anons only get access, and only if there are no roles required for the access action
# other actions (or if the dataset has roles defined for the access action) fall through to the false below
elif action.action not in [ adra.action for adra in dataset.actions ]:
if action.model == 'restrict':
return True # implicit access to restrict-style actions if the dataset does not have the action
# grant-style actions fall through to the false below
else:
# collect a user's roles and their groups' roles
user_roles = [ ura.role for ura in user.roles ]
for group in [ uga.group for uga in user.groups ]:
for role in [ gra.role for gra in group.roles ]:
if role not in user_roles:
user_roles.append( role )
user_role_ids = sorted( [ r.id for r in user_roles ] )
for adra in dataset.actions:
if action.action != adra.action:
continue
# the filter() returns a list of the dataset's role ids
# of which the user is not a member. so an empty list
# means the user has all of the required roles.
if not filter( lambda x: x not in user_role_ids, adra.role_ids ):
return True # user has all of the roles required to perform the action
break # fall through to the false. user is missing at least one required role
return False # default is to reject
def guess_derived_permissions_for_datasets( self, datasets=[] ):
"""Returns a list of group/action tuples for the output dataset based upon provided datasets"""
intersect = None
priority_access_perms = None
"""Returns a dict of { action : [ role, role, ... ] } for the output dataset based upon provided datasets"""
perms = {}
for dataset in datasets:
# Determine access groups for output datasets - these groups are the
# intersection across all inputs. If we end up with no intersection
# between inputs, then we rely on priorities
if isinstance( dataset, self.model.HistoryDatasetAssociation ):
if not isinstance( dataset, self.model.Dataset ):
dataset = dataset.dataset
assocs = [ assoc for assoc in dataset.groups ]
for assoc in assocs:
if priority_access_perms is None or priority_access_perms[0].priority < assoc.group.priority:
priority_access_perms = ( assoc.group, assoc.permitted_actions )
if intersect is None:
intersect = [ (a.group, a.permitted_actions) for a in assocs ]
else:
# Intersect existing perms with new perms
#access_assocs = filter( lambda x: x.group in [ a.group for a in access_assocs ], assocs )
new_intersect = []
for group, actions in intersect:
matches = filter( lambda x: x.group == group, assocs )
# Could a dataset ever have more than one GDA with the same group id?
if len( matches ) > 1:
log.error( "Unable to derive permissions, duplicate group_dataset_association rows exist for group %d, dataset %d" % (group.id, dataset.id) )
elif len( matches ) == 1:
# compare permitted_actions
pa_intersect = filter( lambda x: x in actions, matches[0].permitted_actions )
new_intersect.append( ( group, pa_intersect ) )
intersect = new_intersect
# If we have no groups left after intersection, take the highest priority group
if not intersect:
if priority_access_perms:
intersect = [ priority_access_perms ]
return intersect
def get_group( self, id ):
return self.model.Group.get( id )
raise 'No valid method of retrieving requested group by id %s' % ( str( id ) )
def get_group_by_name( self, name ):
return self.model.Group.filter_by( name=name ).first()
raise 'No valid method of retrieving requested group by name %s' % ( str( name ) )
def create_group( self, **kwd ):
rval = self.model.Group( **kwd )
rval.flush()
return rval
raise 'No valid method of creating group with %s' % ( kwd )
these_perms = {}
# initialize blank perms
for a in self.get_actions():
these_perms[ a.action ] = []
# collect this dataset's perms
for adra in dataset.actions:
these_perms[ adra.action ] = adra.role_ids
# join or intersect this dataset's permissions with others
for action_name, role_ids in these_perms.items():
if action_name not in perms.keys():
perms[ action_name ] = role_ids
else:
if self.get_action( action_name ).model == 'grant':
# intersect existing roles with new roles
perms[ action_name ] = filter( lambda x: x in perms[ action_name ], role_ids )
elif self.get_action( action_name ).model == 'restrict':
# join existing roles with new roles
perms[ action_name ].extend( filter( lambda x: x not in perms[ action_name ], role_ids ) )
##perms = [ ( k, tuple( v ) ) for k, v in perms.items() ] # a list of ( action, ( role_id, role_id, ... ) ) tuples
return perms
def associate_components( self, **kwd ):
assert len( kwd ) == 2, 'You must specify exactly 2 Galaxy security components to associate.'
if 'dataset' in kwd:
if 'group' in kwd:
return self.associate_group_dataset( kwd['group'], kwd['dataset'] )
elif 'permissions' in kwd:
return self.associate_group_dataset( kwd['permissions'][0], kwd['dataset'], kwd['permissions'][1] )
elif 'user' in kwd:
##assert len( kwd ) == 2, 'You must specify exactly 2 Galaxy security components to associate.'
if 'user' in kwd:
if 'group' in kwd:
return self.associate_user_group( kwd['user'], kwd['group'] )
elif 'role' in kwd:
return self.associate_user_role( kwd['user'], kwd['role'] )
elif 'role' in kwd:
if 'group' in kwd:
return self.associate_group_role( kwd['group'], kwd['role'] )
if 'action' in kwd:
if 'dataset' in kwd and 'roles' in kwd:
return self.associate_action_dataset_roles( kwd['action'], kwd['dataset'], kwd['roles'] )
raise 'No valid method of associating provided components: %s' % kwd
def associate_group_dataset( self, group, dataset, permitted_actions=[ RBACAgent.permitted_actions.DATASET_ACCESS ] ):
# HACK: The default permitted_actions should really not be used... need to find cases where this is done and correct it
assoc = self.model.GroupDatasetAssociation( group, dataset, permitted_actions )
assoc.flush()
return assoc
def associate_user_group( self, user, group ):
assoc = self.model.UserGroupAssociation( user, group )
assoc.flush()
return assoc
def create_private_user_group( self, user ):
# Create private group
group_name = "%s private group" % user.email
group = self.model.Group( name=group_name, priority=10 )
group.flush()
# Add user to group
self.associate_components( group=group, user=user )
group.flush()
return group
def user_set_default_access( self, user, permissions = None, history = False, dataset = False ):
if permissions is None:
permissions = [ ( self.create_private_user_group( user ), self.permitted_actions.__dict__.values() ) ]
if permissions is not None:
# Delete all of the previous DefaultUserGroupAssociation
for duga in user.default_groups: #this is the association not the actual group
duga.delete()
duga.flush()
# Add the new DefaultUserGroupAssociation
for group, permitted_actions in permissions:
duga = self.model.DefaultUserGroupAssociation( user, group, permitted_actions )
duga.flush()
def associate_user_role( self, user, role ):
assoc = self.model.UserRoleAssociation( user, role )
assoc.flush()
return assoc
def associate_group_role( self, group, role ):
assoc = self.model.GroupRoleAssociation( group, role )
assoc.flush()
return assoc
def associate_action_dataset_roles( self, action, dataset, roles ):
assoc = self.model.ActionDatasetRolesAssociation( action, dataset, roles )
assoc.flush()
return assoc
def create_private_user_role( self, user ):
# Create private role
role = self.model.Role( name=user.email, description='Private Role for ' + user.email, type=self.model.Role.types.PRIVATE )
role.flush()
# Add user to role
self.associate_components( role=role, user=user )
return role
def get_private_user_role( self, user, auto_create=False ):
role = self.model.Role.get_by( name=user.email, type=self.model.Role.types.PRIVATE )
if not role:
if auto_create:
return self.create_private_user_role( user )
else:
return None
def user_set_default_permissions( self, user, permissions = {}, history = False, dataset = False ):
if user is None:
return None
if not permissions:
permissions = { self.permitted_actions.DATASET_MANAGE_PERMISSIONS : [ self.get_private_user_role( user, auto_create=True ) ] }
# Delete all of the previous defaults
for dup in user.default_permissions:
dup.delete()
dup.flush()
# Add the new defaults (if any)
for action, roles in permissions.items():
if isinstance( action, Action ):
action = action.action
dup = self.model.DefaultUserPermissions( user, action, roles )
dup.flush()
if history:
for history in user.active_histories:
self.history_set_default_access( history, permissions=permissions, dataset=dataset )
def user_get_default_access( self, user ):
return [ ( duga.group, duga.permitted_actions ) for duga in user.default_groups ]
def history_set_default_access( self, history, permissions=None, dataset=False ):
if permissions is None:
if history.user:
permissions = self.user_get_default_access( history.user )
else:
permissions = [ ( self.get_public_group(), self.permitted_actions.__dict__.values() ) ]
if permissions is not None:
# Delete all of the previous DefaultHistoryGroupAssociations
for dhga in history.default_groups: #this is the association not the actual group
dhga.delete()
dhga.flush()
# Add the new DefaultHistoryGroupAssociations
for group, permitted_actions in permissions:
dhga = self.model.DefaultHistoryGroupAssociation( history, group, permitted_actions )
dhga.flush()
self.history_set_default_permissions( history, permissions=permissions, dataset=dataset )
def user_get_default_permissions( self, user ):
perms = {}
for dup in user.default_permissions:
perms[ dup.action ] = dup.role_ids
return perms
def history_set_default_permissions( self, history, permissions = {}, dataset = False, bypass_manage_permission = False ):
if not history.user:
return None # default permissions on a userless history are none
if not permissions:
permissions = self.user_get_default_permissions( history.user )
for dhp in history.default_permissions:
dhp.delete()
dhp.flush()
for action, roles in permissions.items():
if isinstance( action, Action ):
action = action.action
dhp = self.model.DefaultHistoryPermissions( history, action, roles )
dhp.flush()
if dataset:
for hda in history.datasets:
for hda2 in hda.dataset.history_associations:
if history.user and hda2.history not in history.user.active_histories:
# This will occur when a user logs in and has datasets in their previously-public history.
self.set_dataset_permissions( hda.dataset, [ ( self.get_public_group(), [ self.permitted_actions.DATASET_ACCESS ] ) ] )
#break
for hda_in_history in history.datasets:
if len( hda_in_history.dataset.library_associations ):
continue # dataset has a library association, don't change the permissions
if len( [ hda for hda in hda_in_history.dataset.history_associations if hda.history not in history.user.histories ] ):
continue # dataset has a history association in a history the user doesn't own, don't change the permissions
# bypass is used to change permissions of datasets in a userless history when logging in
if bypass_manage_permission or self.allow_action( history.user, self.permitted_actions.DATASET_MANAGE_PERMISSIONS, dataset=hda_in_history.dataset ):
self.set_dataset_permissions( hda_in_history.dataset, permissions )
def history_get_default_permissions( self, history ):
perms = {}
for dhp in history.default_permissions:
perms[ dhp.action ] = dhp.role_ids
return perms
def set_dataset_permissions( self, dataset, permissions={} ):
# to delete permission on an action, pass in a blank list of role ids with that action
for action, role_ids in permissions.items():
if isinstance( action, Action ):
action = action.action
for adra in dataset.actions:
if adra.action != action:
continue
if not role_ids:
adra.delete()
else:
# At this point, we will have already created a gda for the public group and delete_assocs
# is set to False in order to preserve it.
if self.allow_action( history.user, self.permitted_actions.DATASET_MANAGE_PERMISSIONS, dataset=hda.dataset ):
self.set_dataset_permissions( hda.dataset, permissions, delete_existing_assocs=False )
def history_get_default_access( self, history ):
return [ ( dhga.group, dhga.permitted_actions ) for dhga in history.default_groups ]
def get_public_group( self ):
return self.model.Group.get_public_group()
def set_public_group( self, group ):
return self.model.Group.set_public_group( group )
def guess_public_group( self ):
return self.model.Group.guess_public_group()
def set_dataset_permissions( self, dataset, permissions, delete_existing_assocs=True ):
"""
Apply permissions (a list of (group, permitted_action) tuples)
to a dataset, removing any existing permissions. permissions can
also be a list of GroupDatasetAssociations (for simplicity).
"""
if isinstance( dataset, self.model.HistoryDatasetAssociation ):
dataset = dataset.dataset
if delete_existing_assocs:
for gda in dataset.groups:
gda.delete()
gda.flush()
if permissions and isinstance( permissions[0], self.model.GroupDatasetAssociation ):
permissions = [ ( gda.group, gda.permitted_actions ) for gda in permissions ]
for ptuple in permissions:
self.associate_components( dataset=dataset, permissions=ptuple )
def get_dataset_permissions( self, dataset, group_id=None ):
adra.set_roles( role_ids )
adra.flush()
break
else:
if role_ids:
self.associate_components( action=action, dataset=dataset, roles=role_ids )
def get_dataset_permissions( self, dataset ):
if not isinstance( dataset, self.model.Dataset ):
dataset = dataset.dataset
if group_id is not None:
return [ ( gda.group, gda.permitted_actions ) for gda in dataset.groups if gda.group_id == int(group_id) ][0]
else:
return [ ( gda.group, gda.permitted_actions ) for gda in dataset.groups ]
perms = {}
for k, v in self.model.Dataset.permitted_actions.items():
for adra in dataset.actions:
if adra.action != v.action:
continue
perms[ v.action ] = adra.role_ids
break
else:
perms[ v.action ] = []
return perms
def copy_dataset_permissions( self, src, dst ):
if not isinstance( src, self.model.Dataset ):
src = src.dataset
if not isinstance( dst, self.model.Dataset ):
dst = dst.dataset
self.set_dataset_permissions( dst, self.get_dataset_permissions( src ) )
def set_entity_role_associations( self, roles=[], users=[], groups=[], delete_existing_assocs=True ):
for role in roles:
if delete_existing_assocs:
for a in role.users + role.groups:
a.delete()
a.flush()
for user in users:
self.associate_components( user=user, role=role )
for group in groups:
self.associate_components( group=group, role=role )
def get_component_associations( self, **kwd ):
# TODO, Nate: Make sure this method is functionally correct.
assert len( kwd ) == 2, 'You must specify exactly 2 Galaxy security components to check for associations.'
if 'dataset' in kwd:
if 'group' in kwd:
return self.model.GroupDatasetAssociation.filter_by( group_id=kwd['group'].id, dataset_id=kwd['dataset'].id ).first()
if 'action' in kwd:
return self.model.ActionDatasetRolesAssociation.filter_by( action = kwd['action'].action, dataset_id = kwd['dataset'].id ).first()
elif 'user' in kwd:
if 'group' in kwd:
return self.model.UserGroupAssociation.filter_by( group_id=kwd['group'].id, user_id=kwd['user'].id ).first()
return self.model.UserGroupAssociation.filter_by( group_id = kwd['group'].id, user_id = kwd['user'].id ).first()
elif 'role' in kwd:
return self.model.UserRoleAssociation.filter_by( role_id = kwd['role'].id, user_id = kwd['user'].id ).first()
elif 'group' in kwd:
if 'role' in kwd:
return self.model.GroupRoleAssociation.filter_by( role_id = kwd['role'].id, group_id = kwd['group'].id ).first()
raise 'No valid method of associating provided components: %s' % kwd
def dataset_has_group( self, dataset_id, group_id ):
return bool( self.model.GroupDatasetAssociation.filter_by( group_id=group_id, dataset_id=dataset_id ).first() )
def check_folder_contents( self, user, entry ):
"""
Return true if there are any datasets under 'folder' that the
@@ -288,7 +307,6 @@ class GalaxyRBACAgent( RBACAgent ):
else:
raise 'Passed an illegal object to check_folder_contents: %s' % type( entry )
def get_permitted_actions( self, filter=None ):
'''Utility method to return a subset of RBACAgent's permitted actions'''
if filter is None:
+2 -2
View File
@@ -1202,7 +1202,7 @@ class Tool:
else: visible = False
ext = fields.pop(0).lower()
child_dataset = self.app.model.HistoryDatasetAssociation( extension=ext, parent_id=outdata.id, designation=designation, visible=visible, dbkey=outdata.dbkey, create_dataset=True )
self.app.security_agent.set_dataset_permissions( child_dataset.dataset, outdata.dataset.groups )
self.app.security_agent.copy_dataset_permissions( outdata.dataset, child_dataset.dataset )
# Move data from temp location to dataset location
shutil.move( filename, child_dataset.file_name )
child_dataset.flush()
@@ -1239,7 +1239,7 @@ class Tool:
ext = fields.pop(0).lower()
# Create new primary dataset
primary_data = self.app.model.HistoryDatasetAssociation( extension=ext, designation=designation, visible=visible, dbkey=outdata.dbkey, create_dataset=True )
self.app.security_agent.set_dataset_permissions( primary_data.dataset, outdata.dataset.groups )
self.app.security_agent.copy_dataset_permissions( outdata.dataset, primary_data.dataset )
primary_data.flush()
# Move data from temp location to dataset location
shutil.move( filename, primary_data.file_name )
+2 -2
View File
@@ -84,13 +84,13 @@ class DefaultToolAction( object ):
if data.dbkey not in [None, '?']:
input_dbkey = data.dbkey
# Determine output dataset permitted_actions list
# Determine output dataset permission/roles list
existing_datasets = [ inp for inp in inp_data.values() if inp ]
if existing_datasets:
output_permissions = trans.app.security_agent.guess_derived_permissions_for_datasets( existing_datasets )
else:
# No valid inputs, we will use history defaults
output_permissions = trans.app.security_agent.history_get_default_access( trans.history )
output_permissions = trans.app.security_agent.history_get_default_permissions( trans.history )
# Build name for output datasets based on tool name and input names
if len( input_names ) == 1:
on_text = input_names[0]
+2 -2
View File
@@ -78,7 +78,7 @@ class UploadToolAction( object ):
def upload_empty(self, trans, err_code, err_msg):
data = trans.app.model.HistoryDatasetAssociation( create_dataset=True )
trans.app.security_agent.set_dataset_permissions( data.dataset, trans.app.security_agent.history_get_default_access( trans.history ) )
trans.app.security_agent.set_dataset_permissions( data.dataset, trans.app.security_agent.history_get_default_permissions( trans.history ) )
data.name = err_code
data.extension = "txt"
data.dbkey = "?"
@@ -172,7 +172,7 @@ class UploadToolAction( object ):
info = 'uploaded %s file' %data_type
data = trans.app.model.HistoryDatasetAssociation( history = trans.history, extension = ext, create_dataset = True )
trans.app.security_agent.set_dataset_permissions( data.dataset, trans.app.security_agent.history_get_default_access( trans.history ) )
trans.app.security_agent.set_dataset_permissions( data.dataset, trans.app.security_agent.history_get_default_permissions( trans.history ) )
data.name = file_name
data.dbkey = dbkey
data.info = info
+1 -32
View File
@@ -984,38 +984,6 @@ class DataToolParameter( ToolParameter ):
TODO: The following must be fixed to test correctly for the new security_check tag in the DataToolParameter ( the last test below is broken )
Nate's next passs at the dataset security stuff will dramatically alter this anyway.
>>> # Mock up a history (not connected to database)
>>> from galaxy.model import History, HistoryDatasetAssociation, User, Group
>>> from galaxy.util.bunch import Bunch
>>> from galaxy.security import GalaxyRBACAgent
>>> import galaxy.model
>>> security_agent = GalaxyRBACAgent( galaxy.model )
>>> hist = History()
>>> hist.flush()
>>> group = Group( 'test' )
>>> group.flush()
>>> Group.public_id = group.id
>>> dataset1 = HistoryDatasetAssociation( id=1, extension='txt', create_dataset=True )
>>> dataset2 = HistoryDatasetAssociation( id=2, extension='bed', create_dataset=True )
>>> security_agent.set_dataset_permissions( dataset2, [ ( group, security_agent.permitted_actions.__dict__.values() ) ] )
>>> dataset3 = HistoryDatasetAssociation( id=3, extension='fasta', create_dataset=True )
>>> dataset4 = HistoryDatasetAssociation( id=4, extension='png', create_dataset=True )
>>> dataset5 = HistoryDatasetAssociation( id=5, extension='interval', create_dataset=True )
>>> security_agent.set_dataset_permissions( dataset5, [ ( group, security_agent.permitted_actions.__dict__.values() ) ] )
>>> hist.add_dataset( dataset1 )
>>> hist.add_dataset( dataset2 )
>>> hist.add_dataset( dataset3 )
>>> hist.add_dataset( dataset4 )
>>> hist.add_dataset( dataset5 )
>>> p = DataToolParameter( None, XML( '<param name="blah" type="data" format="txt"><security_check group="test" action="access"/></param>' ) )
>>> print p.name
blah
>>> print p.security_dict
{'test': ['access']}
>>> print p.get_html( trans=Bunch( history=hist, user=None, app=Bunch( security_agent = security_agent ) ) )
<select name="blah">
</select>
"""
def __init__( self, tool, elem ):
@@ -1077,6 +1045,7 @@ class DataToolParameter( ToolParameter ):
hid = "%s.%d" % ( parent_hid, i + 1 )
else:
hid = str( hda.hid )
# FIXME: This needs to be rewritten to use the new permissions model
if not hda.dataset.state in [galaxy.model.Dataset.states.ERROR, galaxy.model.Dataset.states.DISCARDED] and hda.visible and trans.app.security_agent.allow_action( trans.user, hda.permitted_actions.DATASET_ACCESS, dataset=hda ):
if self.security_dict:
passed_security_check = True
+149 -243
View File
@@ -59,6 +59,71 @@ class Admin( BaseController ):
msg = params.msg
return trans.fill_template( '/admin/dataset_security/index.mako', msg=msg )
# Galaxy Role Stuff
@web.expose
def roles( self, trans, **kwd ):
if not self.user_is_admin( trans ):
return trans.show_error_message( no_privilege_msg )
p = util.Params( kwd )
msg = p.msg
if 'create' in kwd:
if p.create == 'submitted':
role = trans.app.model.Role( name = util.restore_text( p.name ),
description = util.restore_text( p.description ),
type = trans.app.model.Role.types.ADMIN )
role.flush()
trans.response.send_redirect( url_for( action='roles', associate=True ) )
return trans.show_form(
web.FormBuilder( action = web.url_for(), title = "Create a new role", name="role", submit_text = "Create" )
.add_text( name = "name", label = "Name", value = "New Role" )
.add_text( name = "description", label = "Description", value = "" )
.add_input( 'hidden', '', 'create', 'submitted', use_label = False )
.add_input( 'hidden', '', 'id', id, use_label = False ) )
return trans.fill_template( '/admin/dataset_security/roles.mako',
roles=trans.app.model.Role.select(),
msg=msg )
@web.expose
def role( self, trans, id=None, **kwd ):
if not self.user_is_admin( trans ):
return trans.show_error_message( no_privilege_msg )
p = util.Params( kwd )
msg = p.msg
if not id:
return trans.show_error_message( "Galaxy can't associate roles unless you provide a role id" )
role = trans.app.model.Role.get( id )
if not role:
return trans.show_error_message( "The selected role is invalid" )
if role.type == role.types.PRIVATE:
return trans.show_error_message( "The Public Access Role cannot be modified" )
if 'submitted' in kwd:
if p.submitted == 'associate':
in_users = [ trans.app.model.User.get( x ) for x in listify( p.in_users ) ]
in_groups = [ trans.app.model.Group.get( x ) for x in listify( p.in_groups ) ]
trans.app.security_agent.set_entity_role_associations( roles=[ role ], users=in_users, groups=in_groups )
role.refresh()
in_users = []
out_users = []
in_groups = []
out_groups = []
for user in sorted( trans.app.model.User.select(), lambda x,y: cmp( x.email, y.email ) ):
if user in [ x.user for x in role.users ]:
in_users.append( ( user.id, user.email ) )
else:
out_users.append( ( user.id, user.email ) )
for group in sorted( trans.app.model.Group.select(), lambda x,y: cmp( x.name, y.name ) ):
if group in [ x.group for x in role.groups ]:
in_groups.append( ( group.id, group.name ) )
else:
out_groups.append( ( group.id, group.name ) )
return trans.fill_template( '/admin/dataset_security/role.mako',
role = role,
in_users = in_users,
out_users = out_users,
in_groups = in_groups,
out_groups = out_groups
)
# Galaxy Group Stuff
@web.expose
def groups( self, trans, **kwd ):
@@ -69,46 +134,19 @@ class Admin( BaseController ):
# This query retrieves groups that are not deleted and members of each group
q = sa.select( ( ( galaxy.model.Group.table.c.id ).label( 'group_id' ),
( galaxy.model.Group.table.c.name ).label( 'group_name' ),
( galaxy.model.Group.table.c.priority ).label( 'group_priority' ),
sa.func.count( galaxy.model.User.table.c.id ).label( 'total_members' ) ),
whereclause = galaxy.model.Group.table.c.deleted == False,
from_obj = [ sa.outerjoin( galaxy.model.Group.table,
galaxy.model.UserGroupAssociation.table
).outerjoin( galaxy.model.User.table ) ],
group_by = [ galaxy.model.Group.table.c.id,
galaxy.model.Group.table.c.name,
galaxy.model.Group.table.c.priority ],
galaxy.model.Group.table.c.name ],
order_by = [ galaxy.model.Group.table.c.name ] )
groups = []
for row in q.execute():
total_datasets = 0
permitted_actions = []
# This 2nd query retrieves the number of datasets and dataset permitted_actions associated with each group
q2 = sa.select( ( ( galaxy.model.Group.table.c.id ).label( 'group_id' ),
( galaxy.model.GroupDatasetAssociation.table.c.permitted_actions ).label( 'permitted_actions' ),
sa.func.count( galaxy.model.Dataset.table.c.id ).label( 'total_datasets' ) ),
whereclause = sa.and_( galaxy.model.Group.table.c.id == row.group_id,
galaxy.model.Dataset.table.c.deleted == False ),
from_obj = [ sa.outerjoin( galaxy.model.Group.table,
galaxy.model.GroupDatasetAssociation.table
).outerjoin( galaxy.model.Dataset.table ) ],
group_by = [ galaxy.model.Group.table.c.id,
galaxy.model.GroupDatasetAssociation.table.c.permitted_actions ] )
for row2 in q2.execute():
total_datasets = row2.total_datasets
permitted_actions = []
# There may not yet be any GroupDatasetAssociations, in which case no
# actions will be found
if row2.permitted_actions:
for action in row2.permitted_actions:
permitted_actions.append( action.encode( 'ascii' ) )
permitted_actions.sort()
groups.append( ( row.group_id,
escape( row.group_name, entities ),
row.group_priority,
row.total_members,
total_datasets,
permitted_actions ) )
row.total_members ) )
return trans.fill_template( '/admin/dataset_security/groups.mako',
groups=groups,
msg=msg )
@@ -143,12 +181,8 @@ class Admin( BaseController ):
msg = "A group with that name already exists"
trans.response.send_redirect( '/admin/create_group?msg=%s' % msg )
else:
try:
priority = int( params.priority )
except:
priority = 0
# Create the group
group = galaxy.model.Group( name, priority )
group = galaxy.model.Group( name )
group.flush()
# Add the members
members = params.members
@@ -163,7 +197,7 @@ class Admin( BaseController ):
# Create the UserGroupAssociation
user_group_association = galaxy.model.UserGroupAssociation( user, group )
user_group_association.flush()
msg = "The new group has been created with priority %s and %s members" % ( str( priority ), str( len( members ) ) )
msg = "The new group has been created with %s members" % str( len( members ) )
trans.response.send_redirect( '/admin/groups?msg=%s' % msg )
@web.expose
def group_members( self, trans, **kwd ):
@@ -279,50 +313,6 @@ class Admin( BaseController ):
msg = "Group membership has been updated with a total of %s members" % len( members )
trans.response.send_redirect( '/admin/group_members?group_id=%s&group_name=%s&msg=%s' % ( str( group_id ), params.group_name, msg ) )
@web.expose
def group_dataset_permitted_actions( self, trans, **kwd ):
if not self.user_is_admin( trans ):
return trans.show_error_message( no_privilege_msg )
params = util.Params( kwd )
msg = params.msg
group_id = int( params.group_id )
gdas = []
permitted_actions = []
group = galaxy.model.Group.get( group_id )
return trans.fill_template( '/admin/dataset_security/group_dataset_permitted_actions_edit.mako',
group=group,
gdas=group.datasets,
msg=msg )
@web.expose
def group_dataset_permitted_actions_edit( self, trans, **kwd ):
if not self.user_is_admin( trans ):
return trans.show_error_message( no_privilege_msg )
params = util.Params( kwd )
group_id = int( params.group_id )
permissions = {}
for gda_id in params.gdas.split(','):
permissions[gda_id] = params.get('gda_actions_%s' % gda_id, None)
if isinstance( permissions[gda_id], str ):
permissions[gda_id] = [ permissions[gda_id] ]
for gda_id, permitted_actions in permissions.items():
if permitted_actions is None:
gda = trans.app.model.GroupDatasetAssociation.get( int( gda_id ) )
if gda.permitted_actions is not []:
log.debug( "gda %s: permitted_actions emptied (set to none)" % gda_id )
gda.permitted_actions = []
gda.flush()
else:
valid_pas = trans.app.model.security_agent.convert_permitted_action_strings( permitted_actions )
if not valid_pas:
continue # this shouldn't happen, but might as well check
gda = trans.app.model.GroupDatasetAssociation.get( int( gda_id ) )
if sorted(gda.permitted_actions) != sorted(valid_pas):
log.debug( "gda %s: permitted_actions changed to %s" % ( gda_id, valid_pas ) )
gda = trans.app.model.GroupDatasetAssociation.get( int( gda_id ) )
gda.permitted_actions = valid_pas
gda.flush()
msg = "The dataset permitted actions have been updated"
trans.response.send_redirect( '/admin/groups?msg=%s' % msg )
@web.expose
def mark_group_deleted( self, trans, **kwd ):
if not self.user_is_admin( trans ):
return trans.show_error_message( no_privilege_msg )
@@ -343,45 +333,19 @@ class Admin( BaseController ):
# This query retrieves groups that are not deleted and members of each group
q = sa.select( ( ( galaxy.model.Group.table.c.id ).label( 'group_id' ),
( galaxy.model.Group.table.c.name ).label( 'group_name' ),
( galaxy.model.Group.table.c.priority ).label( 'group_priority' ),
sa.func.count( galaxy.model.User.table.c.id ).label( 'total_members' ) ),
whereclause = galaxy.model.Group.table.c.deleted == True,
from_obj = [ sa.outerjoin( galaxy.model.Group.table,
galaxy.model.UserGroupAssociation.table
).outerjoin( galaxy.model.User.table ) ],
group_by = [ galaxy.model.Group.table.c.id,
galaxy.model.Group.table.c.name,
galaxy.model.Group.table.c.priority ],
galaxy.model.Group.table.c.name ],
order_by = [ galaxy.model.Group.table.c.name ] )
groups = []
for row in q.execute():
total_datasets = 0
permitted_actions = []
# This 2nd query retrieves the number of datasets and dataset permitted_actions associated with each group
q2 = sa.select( ( ( galaxy.model.Group.table.c.id ).label( 'group_id' ),
( galaxy.model.GroupDatasetAssociation.table.c.permitted_actions ).label( 'permitted_actions' ),
sa.func.count( galaxy.model.Dataset.table.c.id ).label( 'total_datasets' ) ),
whereclause = sa.and_( galaxy.model.Group.table.c.id == row.group_id,
galaxy.model.Dataset.table.c.deleted == False ),
from_obj = [ sa.outerjoin( galaxy.model.Group.table,
galaxy.model.GroupDatasetAssociation.table
).outerjoin( galaxy.model.Dataset.table ) ],
group_by = [ galaxy.model.Group.table.c.id,
galaxy.model.GroupDatasetAssociation.table.c.permitted_actions ] )
for row2 in q2.execute():
total_datasets = row2.total_datasets
permitted_actions = []
# There may not yet be any GroupDatasetAssociations, in which case no actions will be found
if row2.permitted_actions:
for action in row2.permitted_actions:
permitted_actions.append( action.encode( 'ascii' ) )
permitted_actions.sort()
groups.append( ( row.group_id,
escape( row.group_name, entities ),
row.group_priority,
row.total_members,
total_datasets,
permitted_actions ) )
row.total_members ) )
return trans.fill_template( '/admin/dataset_security/deleted_groups.mako',
groups=groups,
msg=msg )
@@ -449,25 +413,6 @@ class Admin( BaseController ):
msg=msg )
@web.expose
def specified_users_groups( self, trans, **kwd ):
def renderable( component, group_id ):
#return True if component or at least one of components contents is
#associated with group_id
if isinstance( component, trans.app.model.LibraryFolder ):
# Check the folder's datasets to see what can be rendered
for library_folder_dataset_assoc in component.active_datasets:
if renderable( library_folder_dataset_assoc, group_id ):
return True
# Check the folder's sub-folders to see what can be rendered
for library_folder in component.active_folders:
if renderable( library_folder, group_id ):
return True
elif isinstance( component, trans.app.model.LibraryFolderDatasetAssociation ):
dataset = trans.app.model.Dataset.get( component.dataset_id )
for group_dataset_assoc in dataset.groups:
if group_dataset_assoc.group_id == group_id:
return True
return False
if not self.user_is_admin( trans ):
return trans.show_error_message( no_privilege_msg )
params = util.Params( kwd )
@@ -476,49 +421,15 @@ class Admin( BaseController ):
user_email = unescape( params.user_email, unentities )
# Get the groups to which the user belongs
q = sa.select( ( ( galaxy.model.Group.table.c.id ).label( 'group_id' ),
( galaxy.model.Group.table.c.name ).label( 'group_name' ),
( galaxy.model.Group.table.c.priority ).label( 'group_priority' ) ),
( galaxy.model.Group.table.c.name ).label( 'group_name' ) ),
whereclause = galaxy.model.User.table.c.id == user_id,
from_obj = [ sa.outerjoin( galaxy.model.User.table,
galaxy.model.UserGroupAssociation.table ).outerjoin( galaxy.model.Group.table ) ],
order_by = [ 'group_name' ] )
groups = []
for row in q.execute():
libraries = []
groups = []
permitted_actions = []
total_datasets = 0
# Perform a 2nd query to get datasets associated with each group
q2 = sa.select( ( ( galaxy.model.Group.table.c.id ).label( 'group_id' ),
( galaxy.model.GroupDatasetAssociation.table.c.permitted_actions ).label( 'permitted_actions' ),
sa.func.count( galaxy.model.Dataset.table.c.id ).label( 'total_datasets' ) ),
whereclause = sa.and_( galaxy.model.Group.table.c.id == row.group_id,
galaxy.model.Dataset.table.c.deleted == False ),
from_obj = [ sa.outerjoin( galaxy.model.Group.table,
galaxy.model.GroupDatasetAssociation.table
).outerjoin( galaxy.model.Dataset.table ) ],
group_by = [ galaxy.model.Group.table.c.id,
galaxy.model.GroupDatasetAssociation.table.c.permitted_actions ] )
for row2 in q2.execute():
libraries = []
permitted_actions = []
total_datasets = row2.total_datasets
# There may not yet be any GroupDatasetAssociations, in which case no
# actions will be found
if row2.permitted_actions:
for action in row2.permitted_actions:
permitted_actions.append( action.encode( 'ascii' ) )
permitted_actions.sort()
# If we have permitted actions, then we have at least 1 GroupDatasetAssociation, in
# which case, we can see if we have any Libraries that the user can access
libraries = [ library for library in trans.app.model.Library.select() if renderable( library.root_folder, row2.group_id ) ]
groups.append( ( row.group_id,
escape( row.group_name, entities ),
row.group_priority,
total_datasets,
permitted_actions,
libraries ) )
escape( row.group_name, entities ) ) )
return trans.fill_template( '/admin/dataset_security/specified_users_groups.mako',
user_id=user_id,
user_email=escape( user_email, entities ),
@@ -672,7 +583,7 @@ class Admin( BaseController ):
msg = params.msg
# add_file method
def add_file( file_obj, name, extension, dbkey, last_used_build, groups, info='no info', space_to_tab=False ):
def add_file( file_obj, name, extension, dbkey, last_used_build, roles, info='no info', space_to_tab=False ):
data_type = None
temp_name = sniff.stream_to_file( file_obj )
@@ -718,14 +629,9 @@ class Admin( BaseController ):
folder = trans.app.model.LibraryFolder.get( folder_id )
folder.add_dataset( dataset, genome_build=last_used_build )
dataset.flush()
# GroupDatasetAssociations will enable security on the dataset based on the permitted_actions
# associated with the GroupDatasetAssociation. The default permitted_actions at this point
# will be DATASET_ACCESS, but the user can change this after the file is uploaded.
permitted_actions = [ RBACAgent.permitted_actions.DATASET_ACCESS ]
for group_id in groups:
group = galaxy.model.Group.get( group_id )
group_dataset_assoc = galaxy.model.GroupDatasetAssociation( group, dataset.dataset, permitted_actions )
group_dataset_assoc.flush()
if roles:
adra = galaxy.model.ActionDatasetRolesAssociation( RBACAgent.permitted_actions.DATASET_ACCESS.action, dataset.dataset, roles )
adra.flush()
shutil.move( temp_name, dataset.dataset.file_name )
dataset.dataset.state = dataset.dataset.states.OK
dataset.init_meta()
@@ -760,16 +666,9 @@ class Admin( BaseController ):
if 'space_to_tab' in kwd:
if kwd['space_to_tab'] not in ["None", None]:
space_to_tab = True
if 'groups' not in kwd and 'users' not in kwd and 'public' not in kwd:
msg = 'The dataset must be associated with at least 1 user or group, or be set public.'
trans.response.send_redirect( web.url_for( action='dataset', folder_id=folder_id, msg=msg ) )
groups = []
if 'groups' in kwd:
groups.extend( listify(kwd['groups']) )
if 'users' in kwd:
groups.extend( listify(kwd['users']) )
if 'public' in kwd:
groups.extend( [ trans.app.security_agent.get_public_group().id ] )
roles = []
if 'roles' in kwd:
roles = listify( kwd['roles'] )
temp_name = ""
data_list = []
created_datasets = []
@@ -782,7 +681,7 @@ class Admin( BaseController ):
extension,
dbkey,
last_used_build,
groups,
roles,
info="uploaded file",
space_to_tab=space_to_tab )
elif url_paste not in [ None, "" ]:
@@ -796,7 +695,7 @@ class Admin( BaseController ):
extension,
dbkey,
last_used_build,
groups,
roles,
info="uploaded url",
space_to_tab=space_to_tab )
created_datasets.append( last_dataset_created )
@@ -813,7 +712,7 @@ class Admin( BaseController ):
extension,
dbkey,
last_used_build,
groups,
roles,
info="pasted entry",
space_to_tab=space_to_tab )
elif server_dir not in [ None, "", "None" ]:
@@ -831,7 +730,7 @@ class Admin( BaseController ):
extension,
dbkey,
last_used_build,
groups,
roles,
info="imported file",
space_to_tab=space_to_tab )
created_datasets.append( last_dataset_created )
@@ -862,19 +761,14 @@ class Admin( BaseController ):
yield build_name, dbkey, ( dbkey==last_used_build )
dbkeys = get_dbkey_options( last_used_build )
# Send list of groups to the form so the dataset can be associated with 1 or more of them.
groups = []
q = sa.select( ( ( galaxy.model.Group.table.c.id ).label( 'group_id' ),
( galaxy.model.Group.table.c.name ).label( 'group_name' ) ),
order_by = [ galaxy.model.Group.table.c.name ] )
for row in q.execute():
groups.append( ( row.group_id, row.group_name ) )
groups = sorted( groups, key=operator.itemgetter(1) )
#roles = trans.app.model.Role.select().order_by( 'role_id' )
roles = trans.app.model.Role.select( order_by=trans.app.model.Role.c.name )
return trans.fill_template( '/admin/library/new_dataset.mako',
folder_id=folder_id,
file_formats=file_formats,
dbkeys=dbkeys,
last_used_build=last_used_build,
groups=groups,
roles=roles,
msg=msg )
else:
if id.count( ',' ):
@@ -884,92 +778,111 @@ class Admin( BaseController ):
ids = None
# id specified, display attributes form
if id:
dataset = trans.app.model.LibraryFolderDatasetAssociation.get( id )
if not dataset:
lda = trans.app.model.LibraryFolderDatasetAssociation.get( id )
if not lda:
return trans.show_error_message( "Invalid dataset specified" )
# Copied from edit attributes for 'regular' datasets with some additions
p = util.Params(kwd, safe=False)
if p.change:
if p.update_roles:
# The user clicked the Save button on the 'Associate With Roles' form
permissions = {}
for k, v in trans.app.model.Dataset.permitted_actions.items():
in_roles = [ trans.app.model.Role.get( x ) for x in listify( p.get( k + '_in', [] ) ) ]
permissions[ trans.app.security_agent.get_action( v.action ) ] = in_roles
trans.app.security_agent.set_dataset_permissions( lda.dataset, permissions )
lda.dataset.refresh()
elif p.change:
# The user clicked the Save button on the 'Change data type' form
trans.app.datatypes_registry.change_datatype( dataset, p.datatype )
trans.app.datatypes_registry.change_datatype( lda, p.datatype )
trans.app.model.flush()
elif p.save:
# The user clicked the Save button on the 'Edit Attributes' form
dataset.name = name
dataset.info = info
lda.name = name
lda.info = info
# The following for loop will save all metadata_spec items
for name, spec in dataset.datatype.metadata_spec.items():
for name, spec in lda.datatype.metadata_spec.items():
if spec.get("readonly"):
continue
optional = p.get("is_"+name, None)
if optional and optional == 'true':
# optional element... == 'true' actually means it is NOT checked (and therefore ommitted)
setattr(dataset.metadata,name,None)
setattr(lda.metadata,name,None)
else:
setattr(dataset.metadata,name,spec.unwrap(p.get(name, None), p))
setattr(lda.metadata,name,spec.unwrap(p.get(name, None), p))
dataset.metadata.dbkey = dbkey
dataset.datatype.after_edit( dataset )
lda.metadata.dbkey = dbkey
lda.datatype.after_edit( lda )
trans.app.model.flush()
return trans.show_ok_message( "Attributes updated" )
elif p.detect:
# The user clicked the Auto-detect button on the 'Edit Attributes' form
for name, spec in dataset.datatype.metadata_spec.items():
for name, spec in lda.datatype.metadata_spec.items():
# We need to be careful about the attributes we are resetting
if name != 'name' and name != 'info' and name != 'dbkey':
if spec.get( 'default' ):
setattr( dataset.metadata,name,spec.unwrap( spec.get( 'default' ), spec ))
dataset.datatype.set_meta( dataset )
dataset.datatype.after_edit( dataset )
setattr( lda.metadata,name,spec.unwrap( spec.get( 'default' ), spec ))
lda.datatype.set_meta( lda )
lda.datatype.after_edit( lda )
trans.app.model.flush()
return trans.show_ok_message( "Attributes updated" )
elif p.delete:
dataset.deleted = True
dataset.flush()
lda.deleted = True
lda.flush()
trans.response.send_redirect( web.url_for( action='library_browser' ) )
dataset.datatype.before_edit( dataset )
if "dbkey" in dataset.datatype.metadata_spec and not dataset.metadata.dbkey:
lda.datatype.before_edit( lda )
if "dbkey" in lda.datatype.metadata_spec and not lda.metadata.dbkey:
# Copy dbkey into metadata, for backwards compatability
# This looks like it does nothing, but getting the dbkey
# returns the metadata dbkey unless it is None, in which
# case it resorts to the old dbkey. Setting the dbkey
# sets it properly in the metadata
dataset.metadata.dbkey = dataset.dbkey
lda.metadata.dbkey = lda.dbkey
metadata = list()
# a list of MetadataParemeters
for name, spec in dataset.datatype.metadata_spec.items():
for name, spec in lda.datatype.metadata_spec.items():
if spec.visible:
metadata.append( spec.wrap( dataset.metadata.get(name), dataset ) )
metadata.append( spec.wrap( lda.metadata.get(name), lda ) )
# let's not overwrite the imported datatypes module with the variable datatypes?
ldatatypes = [x for x in trans.app.datatypes_registry.datatypes_by_extension.iterkeys()]
ldatatypes.sort()
return trans.fill_template( "/admin/library/dataset.mako",
dataset=dataset,
dataset=lda,
metadata=metadata,
datatypes=ldatatypes,
err=None,
msg=msg )
# multiple ids specfied, display multi permission form
elif ids:
datasets = []
ldas = []
for id in [ int( id ) for id in ids ]:
d = trans.app.model.LibraryFolderDatasetAssociation.get( id )
if d is None:
lda = trans.app.model.LibraryFolderDatasetAssociation.get( id )
if lda is None:
return trans.show_error_message( 'You specified an invalid dataset' )
datasets.append( d )
if len( datasets ) < 2:
ldas.append( lda )
if len( ldas ) < 2:
return trans.show_error_message( 'You must specify at least two datasets to modify permissions on' )
# If the permissions on the first dataset don't match the intersection
# of permissions, the permissions across all datasets are not
# identical. Although, should we care, or should we just let the admin
# overwrite permissions regardless?
if trans.app.security_agent.get_dataset_permissions( datasets[0] ) != \
trans.app.security_agent.guess_derived_permissions_for_datasets( [ d.dataset for d in datasets ] ):
if 'update_roles' in kwd:
p = util.Params( kwd )
permissions = {}
for k, v in trans.app.model.Dataset.permitted_actions.items():
in_roles = [ trans.app.model.Role.get( x ) for x in listify( p.get( k + '_in', [] ) ) ]
permissions[ trans.app.security_agent.get_action( v.action ) ] = in_roles
for lda in ldas:
trans.app.security_agent.set_dataset_permissions( lda.dataset, permissions )
lda.dataset.refresh()
# Ensure that the permissions across all datasets are
# identical. Otherwise, we can't update together.
tmp = []
for lda in ldas:
perms = trans.app.security_agent.get_dataset_permissions( lda.dataset )
if perms not in tmp:
tmp.append( perms )
if len( tmp ) != 1:
return trans.show_error_message( "The datasets you selected do not have identical permissions, so they can not be updated together" )
else:
return trans.fill_template( "/admin/library/dataset.mako",
dataset=datasets )
dataset=ldas )
def check_gzip( self, temp_name ):
"""
Utility method to check gzipped uploads
@@ -1042,6 +955,9 @@ class Admin( BaseController ):
trans.response.send_redirect( web.url_for( action='library_browser' ) )
@web.expose
def datasets( self, trans, **kwd ):
"""
The datasets method is used by the dropdown box on the admin-side library browser.
"""
if not self.user_is_admin( trans ):
return trans.show_error_message( no_privilege_msg )
params = util.Params( kwd )
@@ -1086,18 +1002,6 @@ class Admin( BaseController ):
### -- Dan:
# Delete any GroupDatasetAssociations #perhaps group assocs should only be deleted when the Dataset object is purged?
#for group_dataset_assoc in dataset.groups:
# group_dataset_assoc.delete()
# group_dataset_assoc.flush()
# Delete any HistoryDatasetAssociations #Just because we delete this Library Dataset Association from the library, doesn't mean we want to remove it from a user's history (without their knowledge even)
#for history_dataset_assoc in dataset.history_associations:
# history_dataset_assoc.deleted = True #set deleted flag to True, do not delete row
# history_dataset_assoc.flush()
# Delete the dataset
#dataset.deleted = True
#dataset.flush()
# Delete the LibraryFolderDatasetAssociation
library_folder_dataset_assoc.deleted = True
library_folder_dataset_assoc.flush()
@@ -1144,11 +1048,13 @@ class Admin( BaseController ):
trans.response.send_redirect( web.url_for( action = 'libraries', msg = 'You have deleted the library %s.' % library.id ) )
return trans.show_error_message( "You did not specify a library to delete." )
def listify( item ):
def listify( item, return_none=False ):
"""
Since single params are not a single item list
"""
if isinstance( item, list ):
if item is None:
return []
elif isinstance( item, list ):
return item
else:
return [ item ]
+1 -1
View File
@@ -104,7 +104,7 @@ class ASync( BaseController ):
#history.datasets.add_dataset( data )
data = trans.app.model.HistoryDatasetAssociation( create_dataset = True, extension = GALAXY_TYPE )
trans.app.security_agent.set_dataset_permissions( data.dataset, trans.app.security_agent.history_get_default_access( trans.history ) )
trans.app.security_agent.set_dataset_permissions( data.dataset, trans.app.security_agent.history_get_default_permissions( trans.history ) )
data.name = GALAXY_NAME
data.dbkey = GALAXY_BUILD
data.info = GALAXY_INFO
+50 -8
View File
@@ -271,6 +271,8 @@ class RootController( BaseController ):
if target_type:
msg = data.datatype.convert_dataset(trans, data, target_type)
return trans.show_ok_message( msg, refresh_frames=['history'] )
'''
Form removed from template, needs to be remade
elif p.change_permission:
"""The user clicked the change_permission button on the 'Change permissions' form"""
if not trans.user:
@@ -287,6 +289,7 @@ class RootController( BaseController ):
return trans.show_ok_message( "Dataset permissions have been set.", refresh_frames=['history'] )
else:
return trans.show_error_message( "You are not authorized to change this dataset's permitted actions." )
'''
data.datatype.before_edit( data )
@@ -399,10 +402,10 @@ class RootController( BaseController ):
if history:
if history.user_id != None and user:
assert user.id == history.user_id, "History does not belong to current user"
# Delete DefaultHistoryGroupAssociations
for default_history_group_association in history.default_groups:
default_history_group_association.delete()
default_history_group_association.flush()
# Delete DefaultHistoryPermissions
for dhp in history.default_permissions:
dhp.delete()
dhp.flush()
# Mark history as deleted in db
history.deleted = True
history_names.append(history.name)
@@ -454,6 +457,42 @@ class RootController( BaseController ):
errors.append( "You must select at least one history to undelete." )
return self.history_available( trans, id=','.join( id ), show_deleted=True, ok_msg = ok_msg, error_msg = " ".join( errors ) )
@web.expose
def history_undelete( self, trans, id=[], **kwd):
"""Undeletes a list of histories, ensures that histories are owned by current user"""
history_names = []
errors = []
ok_msg = ""
if id:
if not isinstance( id, list ):
id = id.split( "," )
user = trans.get_user()
for hid in id:
try:
int( hid )
except:
errors.append( "Invalid history: %s" % str( hid ) )
continue
history = self.app.model.History.get( hid )
if history:
if history.user != user:
errors.append( "History does not belong to current user." )
continue
if history.purged:
errors.append( "History has already been purged and can not be undeleted." )
continue
history_names.append( history.name )
history.deleted = False
else:
errors.append( "Not able to find history %s." % str( hid ) )
trans.log_event( "History id %s marked as undeleted" % str(hid) )
self.app.model.flush()
if history_names:
ok_msg = "Histories (%s) have been undeleted." % ", ".join( history_names )
else:
errors.append( "You must select at least one history to undelete." )
return self.history_available( trans, id=','.join( id ), show_deleted=True, ok_msg = ok_msg, error_msg = " ".join( errors ) )
@web.expose
def clear_history( self, trans ):
"""Clears the history for a user"""
@@ -680,12 +719,13 @@ class RootController( BaseController ):
"""Adds a POSTed file to a History"""
try:
history = trans.app.model.History.get( history_id )
groups = trans.app.security_agent.history_get_default_access( history )
data = trans.app.model.HistoryDatasetAssociation( name = name, info = info, extension = ext, dbkey = dbkey, create_dataset = True )
if copy_access_from:
copy_access_from = trans.app.model.HistoryDatasetAssociation.get( copy_access_from )
group_dataset_associations = copy_access_from.dataset.groups
data = trans.app.model.HistoryDatasetAssociation( name = name, info = info, extension = ext, dbkey = dbkey, create_dataset = True )
trans.app.security_agent.set_dataset_permissions( data.dataset, group_dataset_associations )
trans.app.security_agent.copy_dataset_permissions( copy_access_from.dataset, data.dataset )
else:
permissions = trans.app.security_agent.history_get_default_permissions( history )
trans.app.security_agent.set_dataset_permissions( data.dataset, permissions )
data.flush()
data_file = open( data.file_name, "wb" )
file_data.file.seek( 0 )
@@ -710,6 +750,8 @@ class RootController( BaseController ):
def history_set_default_permitted_actions( self, trans, **kwd ):
"""Sets the user's default permitted_actions for the current history"""
if trans.user:
return trans.show_error_message( "This function is not implemented" )
# TODO: reimplement
if 'set_permitted_actions' in kwd:
"""The user clicked the set_permitted_actions button on the set_permitted_actions form"""
history = trans.get_history()
+2
View File
@@ -171,6 +171,8 @@ class User( BaseController ):
def set_default_permitted_actions( self, trans, **kwd ):
"""Sets the user's default permitted actions for the new histories"""
if trans.user:
return trans.show_error_message( "This function is not implemented" )
# TODO: reimplement
if 'set_permitted_actions' in kwd:
"""The user clicked the set_permitted_actions button on the set_permitted_actions form"""
group_args = [ k.replace('group_', '', 1) for k in kwd if k.startswith('group_') ]
+2 -2
View File
@@ -200,7 +200,7 @@ class UniverseWebTransaction( base.DefaultWebTransaction ):
history = self.app.model.History( user = self.user )
# Make sure we have an id
history.flush()
self.app.security_agent.history_set_default_access( history )
self.app.security_agent.history_set_default_permissions( history )
# Immediately associate the new history with self
self.__history = history
# Make sure we have a valid session to associate with the new history
@@ -436,7 +436,7 @@ class UniverseWebTransaction( base.DefaultWebTransaction ):
if not history.user:
# This user will now acquire previously non-owned history, so set permitted actions to user's default
history.user = user
self.app.security_agent.history_set_default_access( history, dataset=True )
self.app.security_agent.history_set_default_permissions( history, dataset=True, bypass_manage_permission=True )
history.user_id = user.id
history.flush()
self.__history = history
@@ -9,7 +9,6 @@
<%def name="render_row( group_name, group )">
<td><a href="${h.url_for( controller='admin', action='undelete_group', group_id=group[0] )}">Undelete</a></td>
<td>${group_name}</td>
<td>${group[2]}</td>
<td><a href="${h.url_for( controller='admin', action='group_members', group_id=group[0], group_name=group[1] )}">${group[3]}</a></td>
%if group[4] > 0:
<td><a href="${h.url_for( controller='admin', action='group_dataset_permitted_actions', group_id=group[0], group_name=group[1] )}">${group[4]}</a></td>
@@ -71,7 +70,6 @@
<tr class="header">
<td>&nbsp;</td>
<td>Group</td>
<td>Priority</td>
<td>Members</td>
<td>Datasets</td>
<td>Group Permitted Actions on Datasets</td>
@@ -22,7 +22,7 @@
<table border="0">
<form name="group_create" action="${h.url_for( controller='admin', action='new_group' )}" method="post" >
<tr>
<td>Name: <input name="name" type="textfield" value="" size=40">&nbsp;&nbsp;Priority: <input name="priority" type="textfield" value="0" size=3"></td>
<td>Name: <input name="name" type="textfield" value="" size=40"></td>
</tr>
%if len( users ) == 0:
<tr><td>There are no Galaxy users</td></tr>
+1 -19
View File
@@ -8,22 +8,7 @@
## Render a row
<%def name="render_row( group_name, group )">
<td>${group_name}</td>
<td>${group[2]}</td>
<td><a href="${h.url_for( controller='admin', action='group_members', group_id=group[0], group_name=group[1] )}">${group[3]}</a></td>
%if group[4] > 0:
<td><a href="${h.url_for( controller='admin', action='group_dataset_permitted_actions', group_id=group[0], group_name=group[1] )}">${group[4]}</a></td>
%else:
<td>${group[4]}</td>
%endif
<td>
%if len( group[5] ) == 1:
${group[5][0]}
%elif len( group[5] ) > 1:
%for da in group[5]:
${da}<br/>
%endfor
%endif
</td>
<td><a href="${h.url_for( controller='admin', action='group_members', group_id=group[0], group_name=group[1] )}">${group[2]}</a></td>
<td><a href="${h.url_for( controller='admin', action='mark_group_deleted', group_id=group[0] )}">Mark group deleted</a></td>
</%def>
@@ -75,10 +60,7 @@
%endif
<tr class="header">
<td>Name</td>
<td>Priority</td>
<td>Members</td>
<td>Datasets</td>
<td>Group Permitted Actions on Datasets</td>
<td>&nbsp;</td>
</tr>
%for group in groups:
@@ -25,19 +25,11 @@
<tr class="header">
<td>Group</td>
<td>Priority</td>
<td>Datasets</td>
<td>Permitted Actions on Datasets</td>
<td>Containing Libraries</td>
</tr>
<% ctr = 0 %>
%for group in groups:
<%
gn = unescape( group[1], unentities )
library_ids = ''
for library_id in group[5]:
library_ids += "%s," % library_id
library_ids = library_ids.rstrip( ',' )
%>
%if ctr % 2 == 1:
<tr class="odd_row">
@@ -45,24 +37,6 @@
<tr class="tr">
%endif
<td>${gn}</td>
<td>${group[2]}</td>
%if group[3] > 0:
<td><a href="${h.url_for( controller='admin', action='group_dataset_permitted_actions', group_id=group[0], group_name=group[1] )}">${group[3]}</a></td>
%else:
<td>${group[3]}</td>
%endif
<td>
%for da in group[4]:
${da}<br/>
%endfor
</td>
<td>
%if len( group[5] ) > 0:
<a href="${h.url_for( controller='admin', action='specified_users_group_libraries', user_email=user_email, group_name=group[1], library_ids=library_ids )}">${len( group[5] )}</a>
%else:
${len( group[5] )}
%endif
</td>
</tr>
<% ctr += 1 %>
%endfor
+1
View File
@@ -80,6 +80,7 @@
<div class="toolSectionBg">
<div class="toolTitle"><a href="${h.url_for( action='users' )}" target="galaxy_main">Manage users</a></div>
<div class="toolTitle"><a href="${h.url_for( action='groups' )}" target="galaxy_main">Manage groups</a></div>
<div class="toolTitle"><a href="${h.url_for( action='roles' )}" target="galaxy_main">Manage roles</a></div>
</div>
</div>
<div class="toolSectionPad"></div>
+4
View File
@@ -141,6 +141,7 @@
</ul>
<form name="update_multiple_datasets" action="${h.url_for( action='datasets' )}" onSubmit="javascript:return checkForm();" method="post">
<ul>
%if libraries:
%for library in libraries:
##%if trans.app.security_agent.check_folder_contents( trans.user, library ):
<li class="libraryRow libraryOrFolderRow" id="libraryRow"><div class="rowTitle"><table cellspacing="0" cellpadding="0" border="0" width="100%" class="libraryTitle"><tr>
@@ -177,3 +178,6 @@
<input type="submit" class="primary-button" name="with-selected" id="with-selected-submit" value="go"/>
</div>
</form>
%else:
There are no libraries.
%endif
+63 -104
View File
@@ -1,3 +1,33 @@
<%def name="render_select( dataset, action_key, action )">
<%
in_roles = []
for a in dataset.actions:
if a.action == action.action:
for role_id in a.role_ids:
in_roles.append( trans.app.model.Role.get( role_id ) )
out_roles = filter( lambda x: x not in in_roles, trans.app.model.Role.select() )
%>
<p><label>${action.description}</label></p>
<div style="float: left; margin-right: 10px;">
Roles associated:<br/>
<select name="${action_key}_in" id="${action_key}_in_select" class="in_select" style="min-width: 250px;" multiple>
%for role in in_roles:
<option value="${role.id}">${role.name}</option>
%endfor
</select> <br/>
<input type="submit" id="${action_key}_remove_button" class="role_remove_button" value=">>"/>
</div>
<div>
Roles not associated:<br/>
<select name="${action_key}_out" id="${action_key}_out_select" style="min-width: 250px;" multiple>
%for role in out_roles:
<option value="${role.id}">${role.name}</option>
%endfor
</select> <br/>
<input type="submit" id="${action_key}_add_button" class="role_add_button" value="<<"/>
</div>
</%def>
<%def name="render_permissions_forms( data_obj )">
<script type="text/javascript">
var q = jQuery.noConflict();
@@ -14,6 +44,23 @@
q("div#group_" + this.value).slideUp("fast");
}
});
q('.role_add_button').click(function() {
var action = this.id.substring( 0, this.id.lastIndexOf( '_add_button' ) )
var in_select = '#' + action + '_in_select';
var out_select = '#' + action + '_out_select';
return !q(out_select + ' option:selected').remove().appendTo(in_select);
});
q('.role_remove_button').click(function() {
var action = this.id.substring( 0, this.id.lastIndexOf( '_remove_button' ) )
var in_select = '#' + action + '_in_select';
var out_select = '#' + action + '_out_select';
return !q(in_select + ' option:selected').remove().appendTo(out_select);
});
q('form#edit_role_associations').submit(function() {
q('.in_select option').each(function(i) {
q(this).attr("selected", "selected");
});
});
});
</script>
@@ -32,118 +79,30 @@
trans.show_error_message( "Unknown object passed to render_permissions_forms" )
if id is None:
id = dataset.id
%>
<div class="toolForm">
<div class="toolFormTitle">Change Existing Permissions</div>
<div class="toolFormTitle">Associate with roles and set permissions</div>
<div class="toolFormBody">
<form name="edit_group_associations" action="${h.url_for( controller='admin', action='dataset_permissions' )}" method="post">
<form name="edit_role_associations" id="edit_role_associations" action="${h.url_for( action='dataset' )}" method="post">
<input type="hidden" name="id" value="${id}">
%if redirect:
<input type="hidden" name="${redirect[0]}" value="${redirect[1]}">
%endif
<div class="form-row">
<%
user_permissions = [ p for p in trans.app.security_agent.get_dataset_permissions( dataset ) if p[0].name.endswith( ' private group' ) ]
real_permissions = [ p for p in trans.app.security_agent.get_dataset_permissions( dataset ) if not p[0].name.endswith( ' private group' ) and p[0].name != 'public' ]
%>
<div class="toolParamHelp" style="clear: both;">
Update the permissions for the users and groups currently associated with this dataset.
</div>
<br/>
%if trans.app.security_agent.dataset_has_group( dataset.id, trans.app.security_agent.get_public_group().id ):
<label>Allow public access:</label><br/>
<input type="checkbox" name="public" value="Yes" checked> This dataset can be accessed by anyone (it is public).<br/>
%endif
<br/>
<label>Users with permissions on this dataset:</label><br/>
%if not len( user_permissions ):
&nbsp;&nbsp;None<br/>
%endif
%for p in user_permissions:
<input type="checkbox" name="users" class="groupCheckbox" value="${p[0].id}" checked/> ${p[0].name.replace( ' private group', '' )} <br/>
<div class="permissionContainer" id="group_${p[0].id}">
%for k, v in trans.app.security_agent.permitted_actions.items():
<input type="checkbox" name="actions" value="${p[0].id},${k}"
%if v in p[1]:
checked
%endif
/> ${trans.app.security_agent.get_permitted_action_description(k)} <br/>
%endfor
</div>
%endfor
<br/>
<label>Groups with permissions on this dataset:</label><br/>
%if not len( real_permissions ):
&nbsp;&nbsp;None<br/>
%endif
%for p in real_permissions:
<input type="checkbox" name="groups" class="groupCheckbox" value="${p[0].id}" checked/> ${p[0].name} <br/>
<div class="permissionContainer" id="group_${p[0].id}">
%for k, v in trans.app.security_agent.permitted_actions.items():
<input type="checkbox" name="actions" value="${p[0].id},${k}"
%if v in p[1]:
checked
%endif
/> ${trans.app.security_agent.get_permitted_action_description(k)} <br/>
%endfor
</div>
%endfor
<br/>
<label>
Choose which roles are required to perform each action on this dataset.
</label>
</div>
<div class="form-row"><input type="submit" name="change_permitted_actions" value="Save"></div>
</form>
</div>
</div>
<p/>
<div class="toolForm">
<div class="toolFormTitle">Add New Users and Groups</div>
<div class="toolFormBody">
<form name="make_new_group_associations" action="${h.url_for( controller='admin', action='dataset_permissions' )}" method="post">
<input type="hidden" name="id" value="${id}">
%if redirect:
<input type="hidden" name="${redirect[0]}" value="${redirect[1]}">
%endif
%for k, v in trans.app.model.Dataset.permitted_actions.items():
<div class="form-row">
${render_select( dataset, k, v )}
</div>
%endfor
<div class="form-row">
<div class="toolParamHelp" style="clear: both;">
Select new users and groups to associate with this dataset (you can assign permissions after creating the association).
</div>
## Don't display the public access checkbox if it's already public (it's shown above instead)
%if not trans.app.security_agent.dataset_has_group( dataset.id, trans.app.security_agent.get_public_group().id ):
<label>Allow public access:</label><br/>
<input type="checkbox" name="public" value="Yes"> This dataset can be accessed by anyone (make it public).<br/>
%endif
<br/>
<%
all_groups = trans.app.model.Group.select()
user_groups = [ g for g in all_groups if g.name.endswith( ' private group' ) ]
real_groups = [ g for g in all_groups if not g.name.endswith( ' private group' ) and g.name != 'public' ]
%>
<div style="float: left; width: 250px; margin-right: 10px;">
<label>Associate with users:</label>
<select name="users" multiple="true" size="5">
%for group in user_groups:
%if not trans.app.security_agent.dataset_has_group( dataset.id, group.id ):
<option value="${group.id}">${group.name.replace( ' private group', '' )}</option>
%endif
%endfor
</select>
<p/>
<label>Associate with groups:</label>
<select name="groups" multiple="true" size="5">
%for group in real_groups:
%if dataset and not trans.app.security_agent.dataset_has_group( dataset.id, group.id ):
<option value="${group.id}">${group.name}</option>
%endif
%endfor
</select>
</div>
<div class="toolParamHelp" style="clear: both;">
To select multiple users or groups, hold ctrl or command while clicking.
</div>
<input type="submit" name="update_roles" value="Save"/>
</div>
<div class="form-row"><input type="submit" name="create_group_associations" value="Save"></div>
</form>
</div>
</div>
@@ -164,11 +123,11 @@
else:
data_state = data.state
%>
%if not trans.app.security_agent.allow_action( trans.user, data.permitted_actions.DATASET_ACCESS, dataset = data.dataset ):
<div class="historyItemWrapper historyItem historyItem-${data_state} historyItem-noPermission" id="historyItem-${data.id}">
%else:
##%if not trans.app.security_agent.allow_action( trans.user, data.permitted_actions.DATASET_ACCESS, dataset = data.dataset ):
## <div class="historyItemWrapper historyItem historyItem-${data_state} historyItem-noPermission" id="historyItem-${data.id}">
##%else:
<div class="historyItemWrapper historyItem historyItem-${data_state}" id="historyItem-${data.id}">
%endif
##%endif
## Header row for history items (name, state, action buttons)
+5 -19
View File
@@ -80,30 +80,16 @@
<div style="clear: both"></div>
</div>
<div class="form-row">
<label>Allow public access:</label>
<input type="checkbox" name="public" value="Yes"> This dataset can be accessed by anyone (make it public).<br/>
<p/>
<% user_groups = [ g for g in groups if g[1].endswith( ' private group' ) ] %>
<% real_groups = [ g for g in groups if not g[1].endswith( ' private group' ) and g[1] != 'public' ] %>
<div style="float: left; width: 250px; margin-right: 10px;">
<label>Associate with users:</label>
<select name="users" multiple="true" size="5">
%for group in user_groups:
<option value="${group[0]}">${group[1].replace( ' private group', '' )}</option>
<label>Restrict dataset access to specific roles:</label>
<select name="roles" multiple="true" size="5">
%for role in roles:
<option value="${role.id}">${role.name}</option>
%endfor
</select>
%if len( real_groups ):
<p/>
<label>Associate with groups:</label>
<select name="groups" multiple="true" size="5">
%for group in real_groups:
<option value="${group[0]}">${group[1]}</option>
%endfor
</select>
%endif
</div>
<div class="toolParamHelp" style="clear: both;">
To select multiple users or groups, hold ctrl or command while clicking.
To select multiple roles, hold ctrl or command while clicking. More permissions can be set after the upload is complete. Selecting no roles makes a dataset public.
</div>
</div>
<div style="clear: both"></div>
-87
View File
@@ -165,90 +165,3 @@ elif isinstance( data, trans.app.model.LibraryFolderDatasetAssociation ):
<p />
%endif
%if trans.user and ( trans.app.security_agent.allow_action( trans.user, data.permitted_actions.DATASET_MANAGE_PERMISSIONS, dataset = data ) ):
<script type="text/javascript">
var q = jQuery.noConflict();
q( document ).ready( function () {
// initialize state
q("input.groupCheckbox").each( function() {
if ( ! q(this).is(":checked") ) q("div#" + this.name).hide();
});
// handle events
q("input.groupCheckbox").click( function() {
if ( q(this).is(":checked") ) {
q("div#" + this.name).slideDown("fast");
} else {
q("div#" + this.name).slideUp("fast");
}
});
});
</script>
<div class="toolForm">
<div class="toolFormTitle">Change Dataset Access Permissions</div>
<div class="toolFormBody">
<form name="change_permission_form" action="${h.url_for( action='edit' )}" method="post">
<input type="hidden" name="${id_name}" value="${data.id}">
<div class="form-row">
<% user_groups = [ assoc.group for assoc in trans.user.groups ] %>
<% dataset_group_ids = [ assoc.group.id for assoc in data.dataset.groups ] %>
<div class="toolParamHelp" style="clear: both;">
Check each group which should have access to this dataset.
</div>
%for group in user_groups:
%if group.id in dataset_group_ids:
<% assoc = filter( lambda x: x.group_id == group.id, data.dataset.groups )[0] %>
%else:
<% assoc = None %>
%endif
<input type="checkbox" name="group_${group.id}" class="groupCheckbox"
%if assoc is not None:
checked
%endif
/> ${group.name} <br/>
<div class="permissionContainer" id="group_${group.id}">
%for k, v in trans.app.security_agent.permitted_actions.items():
<input type="checkbox" name="group_${group.id}_${k}"
%if assoc is not None and v in assoc.permitted_actions:
checked
%endif
/> ${trans.app.security_agent.get_permitted_action_description(k)} <br/>
%endfor
</div>
%endfor
<input type="submit" name="change_permission" value="Save">
</div>
</form>
</div>
</div>
%elif trans.user and ( trans.app.security_agent.allow_action( trans.user, data.permitted_actions.DATASET_ACCESS, dataset = data ) ):
<div class="toolForm">
<div class="toolFormTitle">Dataset Access Permissions</div>
<div class="toolFormBody">
<div class="form-row">
<label>The following groups may perform the actions listed on this dataset:</label>
<br/>
%for assoc in data.dataset.groups:
${assoc.group.name}
<ul>
%for action in assoc.permitted_actions:
<li>${trans.app.security_agent.get_permitted_action_description(action)}</li>
%endfor
</ul>
%endfor
<div class="toolParamHelp" style="clear: both;">
You are not allowed to change the permitted actions on this dataset.
</div>
</div>
</div>
</div>
%endif
<p>
<div class="toolForm">
<div class="toolFormTitle">Copy History Item</div>
<div class="toolFormBody">
Click <a href="${h.url_for( controller='dataset', action='copy_datasets', source_dataset_ids=data.id, target_history_ids=data.history_id )}" target="galaxy_main">here</a> to make a copy of this history item.
</div>
</div>
</p>
+2 -3
View File
@@ -530,20 +530,19 @@ class TwillTestCase( unittest.TestCase ):
self.assertNotEqual(count, maxiter)
# Dataset Security stuff
def create_group( self, name='New Test Group', priority='10' ):
def create_group( self, name='New Test Group' ):
"""Create a new group with 1 member"""
self.visit_url( "%s/admin/create_group" % self.url )
form = tc.show()
self.check_page_for_string( "Create Group" )
try:
tc.fv( "1", "name", name )
tc.fv( "1", "priority", priority )
# twill version 0.9 still does not allow for easily testing forms that contain
# multiple fields with the same name ( e.g., check boxes ). We could attempt to determine
# the number of the field on the form and use it instead of the name of the field, but we
# would be forced to drop and recreate the database every time we test in order to ensure
# the fields will be the same on the form ( since it is dynamically rendered from the database ).
tc.fv( "1", "3", "1" ) # 1-based form field 3 is the 1st check box named 'members', user id 1 is test@bx.psu.edu
tc.fv( "1", "2", "1" ) # 1-based form field 3 is the 1st check box named 'members', user id 1 is test@bx.psu.edu
tc.submit( "create_group_button" )
except AssertionError, err:
errmsg = 'Exception caught attempting to create group: %s' % str( err )
@@ -53,7 +53,7 @@ class TestHistory( TwillTestCase ):
self.visit_page( "admin/groups" )
# the following should have been created when account was created.
self.check_page_for_string( 'test@bx.psu.edu private group' )
self.create_group( name='New Test Group', priority='10' )
self.create_group( name='New Test Group' )
self.visit_page( "admin/groups" )
self.check_page_for_string( "group_name=New+Test+Group" )
# twill version 0.9 still does not allow for the following test
+1 -1
View File
@@ -38,7 +38,7 @@ def exec_after_process(app, inp_data, out_data, param_dict, tool, stdout, stderr
newdata.extension = file_type
newdata.name = basic_name + " (" + description + ")"
history.add_dataset( newdata )
app.security_agent.set_dataset_permissions( newdata.dataset, base_dataset.dataset.groups )
app.security_agent.copy_dataset_permissions( base_dataset.dataset, newdata.dataset )
app.model.flush()
try:
copyfile(filepath,newdata.file_name)
+1 -1
View File
@@ -129,7 +129,7 @@ def exec_after_process(app, inp_data, out_data, param_dict, tool, stdout, stderr
newdata.extension = file_type
newdata.name = basic_name + " (" + microbe_info[kingdom][org]['chrs'][chr]['data'][description]['feature'] +" for "+microbe_info[kingdom][org]['name']+":"+chr + ")"
newdata.flush()
app.security_agent.set_dataset_permissions( newdata.dataset, base_dataset.dataset.groups )
app.security_agent.copy_dataset_permissions( base_dataset.dataset, newdata.dataset )
history.add_dataset( newdata )
app.model.flush()
try:
+1 -1
View File
@@ -32,7 +32,7 @@ def exec_after_process(app, inp_data, out_data, param_dict, tool, stdout, stderr
newdata.name = basic_name + " (" + dbkey + ")"
newdata.flush()
history.add_dataset( newdata )
app.security_agent.set_dataset_permissions( newdata.dataset, output_data.dataset.groups )
app.security_agent.copy_dataset_permissions( output_data.dataset, newdata.dataset )
newdata.flush()
history.flush()
app.model.flush()