mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-09-24 16:30:27 +08:00
User interface to permissions, and a bunch of changes to ensure that
both permitted_actions and groups are set appropriately on datasets. Somewhat tested and mostly working, but needs more testing. Greg: If a userless session has a history, if that user logs in, the DefaultHistoryGroupAssociation for the current history as well as the datasets in that history are supposed to be updated with the permissions in DefaultUserGroupAssociation, but this isn't happening. Possibly because the default permissions on userless histories create datasets in the public group with only the DATASET_ACCESS permission (or possibly for another reason). What are the implications of giving public users DATASET_MANAGE_PERMISSIONS?
This commit is contained in:
@@ -619,7 +619,7 @@ def init( file_path, url, engine_options={}, create_tables=False ):
|
||||
orphans = result.Dataset.get_by( history_id = None )
|
||||
if orphans:
|
||||
for dataset in orphans:
|
||||
result.security_agent.set_dataset_groups( dataset, [ public_group ] )
|
||||
result.security_agent.set_dataset_permissions( dataset, [ ( public_group, result.security_agent.permitted_actions.DATASET_ACCESS ) ] )
|
||||
else:
|
||||
result.security_agent.guess_public_group()
|
||||
log.debug( "Public Group identified as id = %s." % ( Group.public_id ) )
|
||||
|
||||
@@ -7,10 +7,6 @@ from galaxy.util.bunch import Bunch
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
# TODO, Nate: Think about whether the following permitted actions are appropriate for the dataset and
|
||||
# group objects. What should be the default "public" permitted actions? Make sure that the public group
|
||||
# and public datasets are set with the correct permitted actions. Also make sure that "private" settings
|
||||
# are correct when an authenticated user creates things inside their "private" environment.
|
||||
class RBACAgent:
|
||||
"""Class that handles galaxy security"""
|
||||
permitted_actions = Bunch(
|
||||
@@ -23,9 +19,14 @@ class RBACAgent:
|
||||
# use in a job, etc).
|
||||
DATASET_ACCESS = 'dataset_access'
|
||||
)
|
||||
permitted_action_descriptions = Bunch(
|
||||
DATASET_EDIT_METADATA = "Edit this dataset's metadata in the library",
|
||||
DATASET_MANAGE_PERMISSIONS = "Manage the groups associated with this dataset (and those groups' permissions on the dataset)",
|
||||
DATASET_ACCESS = "View, import, and perform analyses on this dataset"
|
||||
)
|
||||
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_groups_permitted_actions_for_datasets( self, datasets = [] ):
|
||||
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
|
||||
@@ -35,12 +36,12 @@ class RBACAgent:
|
||||
raise 'No valid method of creating group with %s' % ( kwd )
|
||||
def create_private_user_group( self, user ):
|
||||
raise "Unimplemented Method"
|
||||
def user_set_default_access( self, user, groups = None, history = False, dataset = False ):
|
||||
def user_set_default_access( 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, groups=None, dataset=False ):
|
||||
def history_set_default_access( self, history, permissions=None, dataset=False ):
|
||||
raise "Unimplemented Method"
|
||||
def set_public_group( self, group ):
|
||||
raise "Unimplemented Method"
|
||||
@@ -48,7 +49,7 @@ class RBACAgent:
|
||||
raise "Unimplemented Method"
|
||||
def guess_public_group( self ):
|
||||
raise "Unimplemented Method"
|
||||
def set_dataset_groups( self, dataset, groups ):
|
||||
def set_dataset_permissions( self, dataset, permissions ):
|
||||
raise "Unimplemented Method"
|
||||
def set_dataset_permitted_actions( self, dataset ):
|
||||
raise "Unimplemented Method"
|
||||
@@ -56,6 +57,24 @@ class RBACAgent:
|
||||
raise "Unimplemented Method"
|
||||
def components_are_associated( self, **kwd ):
|
||||
return bool( self.get_component_associations( **kwd ) )
|
||||
def convert_permitted_action_strings( self, permitted_action_strings ):
|
||||
"""
|
||||
When getting permitted actions from an untrusted source like a
|
||||
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 ):
|
||||
@@ -83,35 +102,41 @@ class GalaxyRBACAgent( RBACAgent ):
|
||||
if action in group_dataset_assoc.permitted_actions:
|
||||
return True
|
||||
return False # No user and dataset not in public group, or user lacks permission
|
||||
def guess_derived_groups_for_datasets( self, datasets=[] ):
|
||||
# TODO, Nate: Make sure this method is functionally correct.
|
||||
"""Returns a list of groups for the output dataset based upon itself and provided datasets"""
|
||||
access_groups = None
|
||||
priority_access_group = None
|
||||
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
|
||||
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 ):
|
||||
dataset = dataset.dataset
|
||||
groups = [ data_group_assoc.group for data_group_assoc in dataset.groups ]
|
||||
for group in groups:
|
||||
if priority_access_group is None or priority_access_group.priority < group.priority:
|
||||
priority_access_group = group
|
||||
if access_groups is None:
|
||||
access_groups = set( groups )
|
||||
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:
|
||||
access_groups.intersection_update( set( groups ) )
|
||||
# Complete lists for output dataset access
|
||||
if access_groups:
|
||||
access_groups = list( access_groups)
|
||||
else:
|
||||
access_groups = []
|
||||
# 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 access_groups:
|
||||
if priority_access_group:
|
||||
access_groups = [ priority_access_group ]
|
||||
return access_groups
|
||||
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 %s' % ( id )
|
||||
@@ -125,29 +150,18 @@ class GalaxyRBACAgent( RBACAgent ):
|
||||
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:
|
||||
if 'group' in kwd:
|
||||
return self.associate_user_group( kwd['user'], kwd['group'] )
|
||||
raise 'No valid method of associating provided components: %s' % kwd
|
||||
def disassociate_components( self, **kwd ):
|
||||
assert len( kwd ) == 2, 'You must specify exactly 2 Galaxy security components to disassociate.'
|
||||
if 'dataset' in kwd:
|
||||
if 'group' in kwd:
|
||||
return self.disassociate_group_dataset( kwd['group'], kwd['dataset'] )
|
||||
raise 'No valid method of associating provided components: %s' % kwd
|
||||
def associate_group_dataset( self, group, dataset, permitted_actions=[] ):
|
||||
if not permitted_actions:
|
||||
if isinstance( dataset.permitted_actions, Bunch ):
|
||||
permitted_actions = dataset.permitted_actions.__dict__.values()
|
||||
else:
|
||||
permitted_actions = dataset.permitted_actions
|
||||
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
|
||||
log.debug("In associate_permissions_dataset, dataset: %d, group: %d, permitted_actions: %s" % ( dataset.id, group.id, permitted_actions ) )
|
||||
assoc = self.model.GroupDatasetAssociation( group, dataset, permitted_actions )
|
||||
assoc.flush()
|
||||
return assoc
|
||||
def disassociate_group_dataset( self, group, dataset ):
|
||||
assoc = self.model.GroupDatasetAssociation.selectone_by( group_id = group.id, dataset_id = dataset.id )
|
||||
assoc.delete()
|
||||
assoc.flush()
|
||||
def associate_user_group( self, user, group ):
|
||||
assoc = self.model.UserGroupAssociation( user, group )
|
||||
assoc.flush()
|
||||
@@ -161,66 +175,69 @@ class GalaxyRBACAgent( RBACAgent ):
|
||||
self.associate_components( group=group, user=user )
|
||||
group.flush()
|
||||
return group
|
||||
def user_set_default_access( self, user, groups = None, history = False, dataset = False ):
|
||||
# TODO, Nate: Make sure this method is functionally correct with permitted actions set appropriately.
|
||||
if groups is None:
|
||||
groups = [ self.create_private_user_group( user ) ]
|
||||
if groups is not None:
|
||||
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:
|
||||
for assoc in user.default_groups: #this is the association not the actual group
|
||||
assoc.delete()
|
||||
assoc.flush()
|
||||
for group in groups:
|
||||
if isinstance( group, self.model.Group ):
|
||||
permitted_actions = group.permitted_actions.__dict__.values()
|
||||
else:
|
||||
permitted_actions = group.permitted_actions
|
||||
for group, permitted_actions in permissions:
|
||||
log.debug("In user_set_default_access, user: %s, group: %s, permitted_actions: %s" % (user.email, group.name, str( permitted_actions)))
|
||||
assoc = self.model.DefaultUserGroupAssociation( user, group, permitted_actions )
|
||||
assoc.flush()
|
||||
if history:
|
||||
for history in user.histories:
|
||||
self.history_set_default_access( history, groups=groups, dataset=dataset )
|
||||
def history_set_default_access( self, history, groups=None, dataset=False ):
|
||||
# TODO, Nate: Make sure this method is functionally correct with permitted actions set appropriately.
|
||||
if groups is None:
|
||||
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:
|
||||
groups = [ assoc.group for assoc in history.user.default_groups ]
|
||||
permissions = self.user_get_default_access( history.user )
|
||||
else:
|
||||
groups = [ self.get_public_group() ]
|
||||
if groups is not None:
|
||||
# FIXME: should this be all permissions?
|
||||
permissions = [ ( self.get_public_group(), [ self.permitted_actions.DATASET_ACCESS ] ) ]
|
||||
if permissions is not None:
|
||||
for assoc in history.default_groups: #this is the association not the actual group
|
||||
assoc.delete()
|
||||
assoc.flush()
|
||||
for group in groups:
|
||||
if isinstance( group, self.model.Group ):
|
||||
permitted_actions = group.permitted_actions.__dict__.values()
|
||||
else:
|
||||
permitted_actions = group.permitted_actions
|
||||
for group, permitted_actions in permissions:
|
||||
log.debug("In history_set_default_access, history: %s, group: %s, permitted_actions: %s" % (history.id, group.name, str( permitted_actions)))
|
||||
assoc = self.model.DefaultHistoryGroupAssociation( history, group, permitted_actions )
|
||||
assoc.flush()
|
||||
if dataset:
|
||||
for data in history.datasets:
|
||||
for hda in data.dataset.history_associations:
|
||||
if history.user and hda.history not in history.user.histories:
|
||||
self.set_dataset_groups( data.dataset, [ self.get_public_group() ] )
|
||||
self.set_dataset_permissions( data.dataset, [ ( self.get_public_group(), [ self.permitted_actions.DATASET_ACCESS ] ) ] )
|
||||
break
|
||||
else:
|
||||
self.set_dataset_groups( data.dataset, groups )
|
||||
self.set_dataset_permissions( data.dataset, permissions )
|
||||
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_groups( self, dataset, groups ):
|
||||
def set_dataset_permissions( self, dataset, permissions ):
|
||||
"""
|
||||
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
|
||||
for group_dataset_assoc in dataset.groups:
|
||||
group_dataset_assoc.delete()
|
||||
group_dataset_assoc.flush()
|
||||
for group in groups:
|
||||
if not isinstance( group, self.model.Group ):
|
||||
group = group.group
|
||||
self.associate_components( dataset=dataset, group=group )
|
||||
if isinstance( permissions[0], self.model.GroupDatasetAssociation ):
|
||||
permissions = [ ( gda.group, gda.permitted_actions ) for gda in permissions ]
|
||||
for ptuple in permissions:
|
||||
log.debug("In set_dataset_permissions, before elf.associate_components, dataset: %s, group: %s, permitted_actions: %s" % ( str(dataset.id), str(ptuple[0].id), str(ptuple[1])))
|
||||
self.associate_components( dataset=dataset, permissions=ptuple )
|
||||
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.'
|
||||
|
||||
@@ -1085,8 +1085,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 )
|
||||
# TODO, Nate: Make sure the following is functionally correct.
|
||||
self.app.security_agent.set_dataset_groups( child_dataset.dataset, outdata.dataset.groups )
|
||||
self.app.security_agent.set_dataset_permissions( child_dataset.dataset, outdata.dataset.groups )
|
||||
# Move data from temp location to dataset location
|
||||
shutil.move( filename, child_dataset.file_name )
|
||||
child_dataset.flush()
|
||||
@@ -1123,8 +1122,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 )
|
||||
# TODO, Nate: Make sure the following is functionally correct.
|
||||
self.app.security_agent.set_dataset_groups( primary_data.dataset, outdata.dataset.groups )
|
||||
self.app.security_agent.set_dataset_permissions( primary_data.dataset, outdata.dataset.groups )
|
||||
primary_data.flush()
|
||||
# Move data from temp location to dataset location
|
||||
shutil.move( filename, primary_data.file_name )
|
||||
|
||||
@@ -86,11 +86,10 @@ class DefaultToolAction( object ):
|
||||
# Determine output dataset permitted_actions list
|
||||
existing_datasets = [ inp for inp in inp_data.values() if inp ]
|
||||
if existing_datasets:
|
||||
output_access_groups = trans.app.security_agent.guess_derived_groups_for_datasets( 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_access_groups = [ group.group for group in trans.history.default_groups ]
|
||||
|
||||
output_permissions = trans.app.security_agent.history_get_default_access( trans.history )
|
||||
# Build name for output datasets based on tool name and input names
|
||||
if len( input_names ) == 1:
|
||||
on_text = input_names[0]
|
||||
@@ -132,7 +131,7 @@ class DefaultToolAction( object ):
|
||||
data = trans.app.model.HistoryDatasetAssociation( extension=ext, create_dataset=True )
|
||||
# Commit the dataset immediately so it gets database assigned unique id
|
||||
data.flush()
|
||||
trans.app.security_agent.set_dataset_groups( data.dataset, output_access_groups )
|
||||
trans.app.security_agent.set_dataset_permissions( data.dataset, output_permissions )
|
||||
# Create an empty file immediately
|
||||
open( data.file_name, "w" ).close()
|
||||
# This may not be neccesary with the new parent/child associations
|
||||
|
||||
@@ -66,8 +66,7 @@ class UploadToolAction( object ):
|
||||
|
||||
def upload_empty(self, trans, err_code, err_msg):
|
||||
data = trans.app.model.HistoryDatasetAssociation( create_dataset=True )
|
||||
# TODO, Nate: Make sure the following is appropriate.
|
||||
trans.app.security_agent.set_dataset_groups( data.dataset, trans.history.default_groups )
|
||||
trans.app.security_agent.set_dataset_permissions( data.dataset, trans.app.security_agent.history_get_default_access( trans.history ) )
|
||||
data.name = err_code
|
||||
data.extension = "txt"
|
||||
data.dbkey = "?"
|
||||
@@ -161,8 +160,7 @@ class UploadToolAction( object ):
|
||||
info = 'uploaded %s file' %data_type
|
||||
|
||||
data = trans.app.model.HistoryDatasetAssociation( history = trans.history, extension = ext, create_dataset = True )
|
||||
# TODO, Nate: Make sure the following is appropriate.
|
||||
trans.app.security_agent.set_dataset_groups( data.dataset, trans.history.default_groups )
|
||||
trans.app.security_agent.set_dataset_permissions( data.dataset, trans.app.security_agent.history_get_default_access( trans.history ) )
|
||||
data.name = file_name
|
||||
data.dbkey = dbkey
|
||||
data.info = info
|
||||
|
||||
@@ -992,15 +992,15 @@ class DataToolParameter( ToolParameter ):
|
||||
>>> group.flush()
|
||||
>>> Group.public_id = group.id
|
||||
>>> dataset1 = HistoryDatasetAssociation( id=1, extension='txt', create_dataset=True )
|
||||
>>> security_agent.set_dataset_groups( dataset1, [ group ] )
|
||||
>>> security_agent.set_dataset_permissions( dataset1, [ ( group, security_agent.permitted_actions.__dict__.values() ) ] )
|
||||
>>> dataset2 = HistoryDatasetAssociation( id=2, extension='bed', create_dataset=True )
|
||||
>>> security_agent.set_dataset_groups( dataset2, [ group ] )
|
||||
>>> security_agent.set_dataset_permissions( dataset2, [ ( group, security_agent.permitted_actions.__dict__.values() ) ] )
|
||||
>>> dataset3 = HistoryDatasetAssociation( id=3, extension='fasta', create_dataset=True )
|
||||
>>> security_agent.set_dataset_groups( dataset3, [ group ] )
|
||||
>>> security_agent.set_dataset_permissions( dataset3, [ ( group, security_agent.permitted_actions.__dict__.values() ) ] )
|
||||
>>> dataset4 = HistoryDatasetAssociation( id=4, extension='png', create_dataset=True )
|
||||
>>> security_agent.set_dataset_groups( dataset4, [ group ] )
|
||||
>>> security_agent.set_dataset_permissions( dataset4, [ ( group, security_agent.permitted_actions.__dict__.values() ) ] )
|
||||
>>> dataset5 = HistoryDatasetAssociation( id=5, extension='interval', create_dataset=True )
|
||||
>>> security_agent.set_dataset_groups( dataset5, [ group ] )
|
||||
>>> 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 )
|
||||
|
||||
@@ -104,8 +104,7 @@ class ASync( BaseController ):
|
||||
#history.datasets.add_dataset( data )
|
||||
|
||||
data = trans.app.model.HistoryDatasetAssociation( create_dataset = True, extension = GALAXY_TYPE )
|
||||
# TODO, Nate: Make sure the following is functionally correct.
|
||||
trans.app.security_agent.set_dataset_groups( data.dataset, trans.history.default_groups )
|
||||
trans.app.security_agent.set_dataset_permissions( data.dataset, trans.app.security_agent.history_get_default_access( trans.history ) )
|
||||
data.name = GALAXY_NAME
|
||||
data.dbkey = GALAXY_BUILD
|
||||
data.info = GALAXY_INFO
|
||||
|
||||
@@ -126,4 +126,4 @@ class DatasetInterface( BaseController ):
|
||||
except:
|
||||
raise paste.httpexceptions.HTTPNotFound( "File Not Found (%s)." % ( filename ) )
|
||||
else:
|
||||
raise paste.httpexceptions.HTTPForbidden( "You are not permitted to access this dataset." )
|
||||
return trans.show_error_message( "You are not privileged to access this dataset." )
|
||||
|
||||
@@ -14,8 +14,6 @@ import urllib
|
||||
log = logging.getLogger( __name__ )
|
||||
|
||||
class RootController( BaseController ):
|
||||
# TODO, Nate: This is where a lot of the new dataset security stuff is managed.
|
||||
# Make sure it is is functionally correct.
|
||||
|
||||
@web.expose
|
||||
def default(self, trans, target1=None, target2=None, **kwd):
|
||||
@@ -266,23 +264,18 @@ class RootController( BaseController ):
|
||||
"""The user clicked the change_permission button on the 'Change permissions' form"""
|
||||
if not trans.user:
|
||||
return trans.show_error_message( "You must be logged in if you want to change dataset permitted actions." )
|
||||
private_dataset = 'private_dataset'
|
||||
public_group = trans.app.security_agent.get_public_group()
|
||||
if private_dataset in kwd and trans.app.security_agent.dataset_has_group( data.dataset.id, public_group.id ):
|
||||
#check user has permission and then remove public group
|
||||
if trans.app.security_agent.allow_action( trans.user, data.dataset.permitted_actions.DATASET_MANAGE_PERMISSIONS, dataset = data.dataset ):
|
||||
trans.app.security_agent.disassociate_components( dataset = data, group = public_group )
|
||||
else:
|
||||
return trans.show_error_message( "You are not authorized to change this dataset's permitted actions." )
|
||||
elif private_dataset not in kwd and not trans.app.security_agent.dataset_has_group( data.dataset.id, public_group.id ):
|
||||
#check user has permission and then add public group
|
||||
if trans.app.security_agent.allow_action( trans.user, data.dataset.permitted_actions.DATASET_MANAGE_PERMISSIONS, dataset = data.dataset ):
|
||||
trans.app.security_agent.associate_components( dataset = data, group = public_group)
|
||||
else:
|
||||
return trans.show_error_message( "You are not authorized to change this dataset's permitted actions." )
|
||||
if trans.app.security_agent.allow_action( trans.user, data.dataset.permitted_actions.DATASET_MANAGE_PERMISSIONS, dataset = data.dataset ):
|
||||
group_args = [ k.replace('group_', '', 1) for k in kwd if k.startswith('group_') ]
|
||||
group_ids_checked = filter( lambda x: not x.count('_'), group_args )
|
||||
permissions = []
|
||||
for group_id in group_ids_checked:
|
||||
action_strings = [ action.replace(group_id + '_', '', 1) for action in group_args if action.startswith(group_id + '_') ]
|
||||
actions = trans.app.security_agent.convert_permitted_action_strings( action_strings )
|
||||
permissions.append( ( trans.app.security_agent.get_group( group_id ), actions ) )
|
||||
trans.app.security_agent.set_dataset_permissions( data.dataset, permissions )
|
||||
return trans.show_ok_message( "Dataset permissions have been set.", refresh_frames=['history'] )
|
||||
else:
|
||||
return trans.show_error_message( "You have not specified a valid change of permitted actions." )
|
||||
return trans.show_ok_message( 'Permitted actions have been changed.', refresh_frames=['history'] )
|
||||
return trans.show_error_message( "You are not authorized to change this dataset's permitted actions." )
|
||||
|
||||
data.datatype.before_edit( data )
|
||||
|
||||
@@ -596,12 +589,12 @@ class RootController( BaseController ):
|
||||
"""Adds a POSTed file to a History"""
|
||||
try:
|
||||
history = trans.app.model.History.get( history_id )
|
||||
groups = history.default_groups
|
||||
groups = trans.app.security_agent.history_get_default_access( history )
|
||||
if copy_access_from:
|
||||
copy_access_from = trans.app.model.HistoryDatasetAssociation.get( copy_access_from )
|
||||
groups = copy_access_from.dataset.groups
|
||||
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_groups( data.dataset, groups )
|
||||
trans.app.security_agent.set_dataset_permissions( data.dataset, group_dataset_associations )
|
||||
data.flush()
|
||||
data_file = open( data.file_name, "wb" )
|
||||
file_data.file.seek( 0 )
|
||||
@@ -629,34 +622,24 @@ class RootController( BaseController ):
|
||||
if 'set_permitted_actions' in kwd:
|
||||
"""The user clicked the set_permitted_actions button on the set_permitted_actions form"""
|
||||
history = trans.get_history()
|
||||
group_in = []
|
||||
group_out = []
|
||||
# Collect groups as entered by user
|
||||
for name, value in kwd.items():
|
||||
if name.startswith( "group_" ):
|
||||
group = trans.app.security_agent.get_group( name.replace( "group_", "", 1 ) )
|
||||
if not group:
|
||||
return trans.show_error_message( 'You have specified an invalid group.' )
|
||||
if value == 'in':
|
||||
group_in.append( group )
|
||||
else:
|
||||
group_out.append( group )
|
||||
if not group_in:
|
||||
group_args = [ k.replace('group_', '', 1) for k in kwd if k.startswith('group_') ]
|
||||
group_ids_checked = filter( lambda x: not x.count('_'), group_args )
|
||||
if not group_ids_checked:
|
||||
return trans.show_error_message( "You must specify at least one default group." )
|
||||
cur_groups = [ assoc.group for assoc in history.default_groups ]
|
||||
group_in.sort()
|
||||
cur_groups.sort()
|
||||
if cur_groups != group_in:
|
||||
trans.app.security_agent.history_set_default_access( history, groups=group_in )
|
||||
return trans.show_ok_message( 'Default history permitted actions have been changed.' )
|
||||
else:
|
||||
return trans.show_error_message( "You did not specify any changes to this history's default permitted actions." )
|
||||
permissions = []
|
||||
for group_id in group_ids_checked:
|
||||
group = trans.app.security_agent.get_group( group_id )
|
||||
if not group:
|
||||
return trans.show_error_message( 'You have specified an invalid group.' )
|
||||
action_strings = [ action.replace(group_id + '_', '', 1) for action in group_args if action.startswith(group_id + '_') ]
|
||||
permissions.append( ( group, trans.app.security_agent.convert_permitted_action_strings( action_strings ) ) )
|
||||
trans.app.security_agent.history_set_default_access( history, permissions = permissions )
|
||||
return trans.show_ok_message( 'Default history permitted actions have been changed.' )
|
||||
return trans.fill_template( 'history/permissions.mako' )
|
||||
else:
|
||||
#user not logged in, history group must be only public
|
||||
return trans.show_error_message( "You must be logged in to change a history's default permitted actions." )
|
||||
|
||||
|
||||
@web.expose
|
||||
def dataset_make_primary( self, trans, id=None):
|
||||
"""Copies a dataset and makes primary"""
|
||||
|
||||
@@ -170,33 +170,23 @@ class User( BaseController ):
|
||||
|
||||
@web.expose
|
||||
def set_default_permitted_actions( self, trans, **kwd ):
|
||||
# TODO, Nate: Make sure this method is functionally correct.
|
||||
"""Sets the user's default permitted actions for the new histories"""
|
||||
if trans.user:
|
||||
if 'set_permitted_actions' in kwd:
|
||||
"""The user clicked the set_permitted_actions button on the set_permitted_actions form"""
|
||||
group_in = []
|
||||
group_out = []
|
||||
# Collect groups as entered by user
|
||||
for name, value in kwd.items():
|
||||
if name.startswith( "group_" ):
|
||||
group = trans.app.security_agent.get_group( name.replace( "group_", "", 1 ) )
|
||||
if not group:
|
||||
return trans.show_error_message( 'You have specified an invalid group.' )
|
||||
if value == 'in':
|
||||
group_in.append( group )
|
||||
else:
|
||||
group_out.append( group )
|
||||
if not group_in:
|
||||
return trans.show_error_message( "You must specify at least one default group." )
|
||||
cur_groups = [ assoc.group for assoc in trans.user.default_groups ]
|
||||
group_in.sort()
|
||||
cur_groups.sort()
|
||||
if cur_groups != group_in:
|
||||
trans.app.security_agent.user_set_default_access( trans.user, groups = group_in )
|
||||
return trans.show_ok_message( 'Default new history permitted actions have been changed.' )
|
||||
else:
|
||||
return trans.show_error_message( "You did not specify any changes to new history's default permitted actions." )
|
||||
group_args = [ k.replace('group_', '', 1) for k in kwd if k.startswith('group_') ]
|
||||
group_ids_checked = filter( lambda x: not x.count('_'), group_args )
|
||||
if not group_ids_checked:
|
||||
return trans.show_error_message( "You must specify at least one default group." )
|
||||
permissions = []
|
||||
for group_id in group_ids_checked:
|
||||
group = trans.app.security_agent.get_group( group_id )
|
||||
if not group:
|
||||
return trans.show_error_message( 'You have specified an invalid group.' )
|
||||
action_strings = [ action.replace(group_id + '_', '', 1) for action in group_args if action.startswith(group_id + '_') ]
|
||||
permissions.append( ( group, trans.app.security_agent.convert_permitted_action_strings( action_strings ) ) )
|
||||
trans.app.security_agent.user_set_default_access( trans.user, permissions )
|
||||
return trans.show_ok_message( 'Default new history permitted actions have been changed.' )
|
||||
return trans.fill_template( 'user/permissions.mako' )
|
||||
else:
|
||||
# User not logged in, history group must be only public
|
||||
|
||||
@@ -433,10 +433,9 @@ class UniverseWebTransaction( base.DefaultWebTransaction ):
|
||||
galaxy_session.flush()
|
||||
self.__galaxy_session = galaxy_session
|
||||
if history is not None and user is not None:
|
||||
# TODO, Nate: Make sure the following is functionally correct
|
||||
if not history.user:
|
||||
# This user will now acquire previously non-owned history, so set permitted actions to user's default
|
||||
self.app.security_agent.history_set_default_access( history, groups=user.default_groups, dataset=True )
|
||||
self.app.security_agent.history_set_default_access( history, dataset=True )
|
||||
history.user_id = user.id
|
||||
history.flush()
|
||||
self.__history = history
|
||||
|
||||
@@ -414,4 +414,8 @@ div.popupmenu-item:hover {
|
||||
|
||||
.popup-arrow:hover {
|
||||
color: black;
|
||||
}
|
||||
}
|
||||
|
||||
div.permissionContainer {
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
@@ -133,33 +133,80 @@
|
||||
|
||||
<p />
|
||||
|
||||
%if trans.app.config.enable_beta_features and trans.user and ( trans.app.security_agent.allow_action( trans.user, data.permitted_actions.DATASET_MANAGE_PERMISSIONS, dataset = data ) ):
|
||||
%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 Permitted Actions</div>
|
||||
<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" value="${data.id}">
|
||||
<div class="form-row">
|
||||
<label>
|
||||
Private Dataset:
|
||||
</label>
|
||||
<% checked = "" %>
|
||||
%if not trans.app.security_agent.dataset_has_group( data.id, trans.app.model.Group.get_public_group().id ):
|
||||
<% checked = " checked" %>
|
||||
%endif
|
||||
<div style="float: left; width: 250px; margin-right: 10px;">
|
||||
<input type="checkbox" name="private_dataset"${checked}>
|
||||
</div>
|
||||
<div style="clear: both"></div>
|
||||
<div class="toolParamHelp" style="clear: both;">
|
||||
This will prevent other users from viewing or utilizing this dataset, even if you share your history with them.
|
||||
</div>
|
||||
<div style="clear: both"></div>
|
||||
</div>
|
||||
<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 do not have permission to edit this dataset's permissions.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
%endif
|
||||
|
||||
@@ -2,6 +2,23 @@
|
||||
<%def name="title()">Change Default History Permitted Actions</%def>
|
||||
|
||||
%if trans.user:
|
||||
<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 Default History Permitted Actions</div>
|
||||
<div class="toolFormBody">
|
||||
@@ -9,27 +26,34 @@
|
||||
<div class="form-row">
|
||||
<% user_groups = [ assoc.group for assoc in trans.user.groups ] %>
|
||||
<% cur_groups = [ assoc.group for assoc in trans.get_history().default_groups ] %>
|
||||
<div style="float: left; width: 250px; margin-right: 10px;">
|
||||
<table>
|
||||
<tr><th>Group</th><th>In</th><th>Out</th></tr>
|
||||
%for group in user_groups:
|
||||
<tr><td>${group.name}</td><td><input type="radio" name="group_${group.id}" value="in"
|
||||
%if group in cur_groups:
|
||||
checked
|
||||
%endif
|
||||
></td><td><input type="radio" name="group_${group.id}" value="out"
|
||||
%if group not in cur_groups:
|
||||
checked
|
||||
%endif
|
||||
></td></tr>
|
||||
<input type="checkbox" name="group_${group.id}" class="groupCheckbox"
|
||||
%if group in cur_groups:
|
||||
<% assoc = filter( lambda x: x.group_id == group.id, trans.get_history().default_groups )[0] %>
|
||||
checked
|
||||
%else:
|
||||
<% assoc = None %>
|
||||
%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
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div style="clear: both"></div>
|
||||
|
||||
<div class="toolParamHelp" style="clear: both;">
|
||||
This will change the default permitted actions assigned to new datasets for your current history.
|
||||
This will change the default permitted actions assigned
|
||||
to new datasets in your current history. You may also
|
||||
specify the defaults for new histories via the
|
||||
<a href="${h.url_for(controller='user')}">user options</a>.
|
||||
|
||||
</div>
|
||||
<div style="clear: both"></div>
|
||||
</div>
|
||||
@@ -39,4 +63,4 @@
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
%endif
|
||||
%endif
|
||||
|
||||
@@ -2,6 +2,23 @@
|
||||
<%def name="title()">Change Default History Permitted Actions</%def>
|
||||
|
||||
%if trans.user:
|
||||
<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 Default Permitted Actions for new Histories </div>
|
||||
<div class="toolFormBody">
|
||||
@@ -9,27 +26,33 @@
|
||||
<div class="form-row">
|
||||
<% user_groups = [ assoc.group for assoc in trans.user.groups ] %>
|
||||
<% cur_groups = [ assoc.group for assoc in trans.user.default_groups ] %>
|
||||
<div style="float: left; width: 250px; margin-right: 10px;">
|
||||
<table>
|
||||
<tr><th>Group</th><th>In</th><th>Out</th></tr>
|
||||
%for group in user_groups:
|
||||
<tr><td>${group.name}</td><td><input type="radio" name="group_${group.id}" value="in"
|
||||
%if group in cur_groups:
|
||||
checked
|
||||
%endif
|
||||
></td><td><input type="radio" name="group_${group.id}" value="out"
|
||||
%if group not in cur_groups:
|
||||
checked
|
||||
%endif
|
||||
></td></tr>
|
||||
<input type="checkbox" name="group_${group.id}" class="groupCheckbox"
|
||||
%if group in cur_groups:
|
||||
<% assoc = filter( lambda x: x.group_id == group.id, trans.user.default_groups )[0] %>
|
||||
checked
|
||||
%else:
|
||||
<% assoc = None %>
|
||||
%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
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div style="clear: both"></div>
|
||||
|
||||
<div class="toolParamHelp" style="clear: both;">
|
||||
This will change the default permitted actions assigned to new datasets for new histories.
|
||||
This will change the default permitted actions assigned
|
||||
to new datasets in new histories. You may also specify
|
||||
per-history defaults via the
|
||||
<a href="${h.url_for(controller='root', action='history_options')}">history options</a>.
|
||||
</div>
|
||||
<div style="clear: both"></div>
|
||||
</div>
|
||||
@@ -39,4 +62,4 @@
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
%endif
|
||||
%endif
|
||||
|
||||
@@ -38,8 +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 )
|
||||
#TODO, Nate: Make sure the following is functionally correct
|
||||
app.security_agent.set_dataset_groups( newdata.dataset, base_dataset.dataset.groups )
|
||||
app.security_agent.set_dataset_permissions( newdata.dataset, base_dataset.dataset.groups )
|
||||
app.model.flush()
|
||||
try:
|
||||
copyfile(filepath,newdata.file_name)
|
||||
|
||||
@@ -129,8 +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()
|
||||
#TODO, Nate: Make sure the following is functionally correct
|
||||
app.security_agent.set_dataset_groups( newdata.dataset, base_dataset.dataset.groups )
|
||||
app.security_agent.set_dataset_permissions( newdata.dataset, base_dataset.dataset.groups )
|
||||
history.add_dataset( newdata )
|
||||
app.model.flush()
|
||||
try:
|
||||
|
||||
@@ -32,8 +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 )
|
||||
#TODO, Nate: Make sure the following is functionally correct
|
||||
app.security_agent.set_dataset_groups( newdata.dataset, output_data.dataset.groups )
|
||||
app.security_agent.set_dataset_permissions( newdata.dataset, output_data.dataset.groups )
|
||||
newdata.flush()
|
||||
history.flush()
|
||||
app.model.flush()
|
||||
|
||||
Reference in New Issue
Block a user