diff --git a/lib/galaxy/model/mapping.py b/lib/galaxy/model/mapping.py index cd4aa9c3ee9..4922b4fd7d5 100644 --- a/lib/galaxy/model/mapping.py +++ b/lib/galaxy/model/mapping.py @@ -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 ) ) diff --git a/lib/galaxy/security/__init__.py b/lib/galaxy/security/__init__.py index 0eaba298d10..9c497437120 100644 --- a/lib/galaxy/security/__init__.py +++ b/lib/galaxy/security/__init__.py @@ -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.' diff --git a/lib/galaxy/tools/__init__.py b/lib/galaxy/tools/__init__.py index 7b5c4b0d305..4030cadd47e 100644 --- a/lib/galaxy/tools/__init__.py +++ b/lib/galaxy/tools/__init__.py @@ -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 ) diff --git a/lib/galaxy/tools/actions/__init__.py b/lib/galaxy/tools/actions/__init__.py index bd74d188ee6..efc0ce99893 100644 --- a/lib/galaxy/tools/actions/__init__.py +++ b/lib/galaxy/tools/actions/__init__.py @@ -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 diff --git a/lib/galaxy/tools/actions/upload.py b/lib/galaxy/tools/actions/upload.py index a9623ae40e0..9fba4fe467d 100644 --- a/lib/galaxy/tools/actions/upload.py +++ b/lib/galaxy/tools/actions/upload.py @@ -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 diff --git a/lib/galaxy/tools/parameters/basic.py b/lib/galaxy/tools/parameters/basic.py index b626e665472..4167629927b 100644 --- a/lib/galaxy/tools/parameters/basic.py +++ b/lib/galaxy/tools/parameters/basic.py @@ -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 ) diff --git a/lib/galaxy/web/controllers/async.py b/lib/galaxy/web/controllers/async.py index dcb6a0c1be5..f0e4ec01e7f 100644 --- a/lib/galaxy/web/controllers/async.py +++ b/lib/galaxy/web/controllers/async.py @@ -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 diff --git a/lib/galaxy/web/controllers/dataset.py b/lib/galaxy/web/controllers/dataset.py index 3d43b5bef5e..8e7a09bfc9d 100644 --- a/lib/galaxy/web/controllers/dataset.py +++ b/lib/galaxy/web/controllers/dataset.py @@ -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." ) diff --git a/lib/galaxy/web/controllers/root.py b/lib/galaxy/web/controllers/root.py index 6850b069cb7..83ab538e87f 100644 --- a/lib/galaxy/web/controllers/root.py +++ b/lib/galaxy/web/controllers/root.py @@ -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""" diff --git a/lib/galaxy/web/controllers/user.py b/lib/galaxy/web/controllers/user.py index d46abb922c5..5b00894d1f9 100644 --- a/lib/galaxy/web/controllers/user.py +++ b/lib/galaxy/web/controllers/user.py @@ -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 diff --git a/lib/galaxy/web/framework/__init__.py b/lib/galaxy/web/framework/__init__.py index 87f099a681d..bd3741a939d 100644 --- a/lib/galaxy/web/framework/__init__.py +++ b/lib/galaxy/web/framework/__init__.py @@ -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 diff --git a/static/june_2007_style/blue/base.css b/static/june_2007_style/blue/base.css index e0ac97ea56b..2cf80e80722 100644 --- a/static/june_2007_style/blue/base.css +++ b/static/june_2007_style/blue/base.css @@ -414,4 +414,8 @@ div.popupmenu-item:hover { .popup-arrow:hover { color: black; -} \ No newline at end of file +} + +div.permissionContainer { + padding-left: 20px; +} diff --git a/templates/dataset/edit_attributes.mako b/templates/dataset/edit_attributes.mako index c54f6eb0d1a..712b2eccfc5 100644 --- a/templates/dataset/edit_attributes.mako +++ b/templates/dataset/edit_attributes.mako @@ -133,33 +133,80 @@

-%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 ) ): +

-
Change Permitted Actions
+
Change Dataset Access Permissions
- - <% checked = "" %> - %if not trans.app.security_agent.dataset_has_group( data.id, trans.app.model.Group.get_public_group().id ): - <% checked = " checked" %> - %endif -
- -
-
-
- This will prevent other users from viewing or utilizing this dataset, even if you share your history with them. -
-
-
-
+ <% user_groups = [ assoc.group for assoc in trans.user.groups ] %> + <% dataset_group_ids = [ assoc.group.id for assoc in data.dataset.groups ] %> +
+ Check each group which should have access to this dataset. +
+ %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 + ${group.name}
+
+ %for k, v in trans.app.security_agent.permitted_actions.items(): + ${trans.app.security_agent.get_permitted_action_description(k)}
+ %endfor +
+ %endfor
+%elif trans.user and ( trans.app.security_agent.allow_action( trans.user, data.permitted_actions.DATASET_ACCESS, dataset = data ) ): +
+
Dataset Access Permissions
+
+
+ +
+ %for assoc in data.dataset.groups: + ${assoc.group.name} +
    + %for action in assoc.permitted_actions: +
  • ${trans.app.security_agent.get_permitted_action_description(action)}
  • + %endfor +
+ %endfor +
+ You do not have permission to edit this dataset's permissions. +
+
+
+
%endif diff --git a/templates/history/permissions.mako b/templates/history/permissions.mako index 6607f002c41..013481d9351 100644 --- a/templates/history/permissions.mako +++ b/templates/history/permissions.mako @@ -2,6 +2,23 @@ <%def name="title()">Change Default History Permitted Actions %if trans.user: +
Change Default History Permitted Actions
@@ -9,27 +26,34 @@
<% user_groups = [ assoc.group for assoc in trans.user.groups ] %> <% cur_groups = [ assoc.group for assoc in trans.get_history().default_groups ] %> -
- - %for group in user_groups: - + + checked + %else: + <% assoc = None %> + %endif + /> ${group.name}
+
+ %for k, v in trans.app.security_agent.permitted_actions.items(): + ${trans.app.security_agent.get_permitted_action_description(k)}
+ %endfor +
%endfor -
GroupInOut
${group.name}
-
- 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 + user options. +
@@ -39,4 +63,4 @@
-%endif \ No newline at end of file +%endif diff --git a/templates/user/permissions.mako b/templates/user/permissions.mako index 7b6b90f976d..e451bc71c29 100644 --- a/templates/user/permissions.mako +++ b/templates/user/permissions.mako @@ -2,6 +2,23 @@ <%def name="title()">Change Default History Permitted Actions %if trans.user: +
Change Default Permitted Actions for new Histories
@@ -9,27 +26,33 @@
<% user_groups = [ assoc.group for assoc in trans.user.groups ] %> <% cur_groups = [ assoc.group for assoc in trans.user.default_groups ] %> -
- - %for group in user_groups: - + + checked + %else: + <% assoc = None %> + %endif + /> ${group.name}
+
+ %for k, v in trans.app.security_agent.permitted_actions.items(): + ${trans.app.security_agent.get_permitted_action_description(k)}
+ %endfor +
%endfor -
GroupInOut
${group.name}
-
- 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 + history options.
@@ -39,4 +62,4 @@
-%endif \ No newline at end of file +%endif diff --git a/tools/data_source/encode_import_code.py b/tools/data_source/encode_import_code.py index ddfbb0241e0..19fb17fef95 100644 --- a/tools/data_source/encode_import_code.py +++ b/tools/data_source/encode_import_code.py @@ -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) diff --git a/tools/data_source/microbial_import_code.py b/tools/data_source/microbial_import_code.py index e8816f093ff..b5ccbf11c3c 100644 --- a/tools/data_source/microbial_import_code.py +++ b/tools/data_source/microbial_import_code.py @@ -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: diff --git a/tools/maf/maf_to_bed_code.py b/tools/maf/maf_to_bed_code.py index 428486b3e37..d28c10b73ca 100644 --- a/tools/maf/maf_to_bed_code.py +++ b/tools/maf/maf_to_bed_code.py @@ -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()