1 new changeset in galaxy-central: http://bitbucket.org/galaxy/galaxy-central/changeset/8731db1b2bfb/ changeset: 8731db1b2bfb user: greg date: 2011-06-13 16:47:51 summary: No longer allow Datasets to have DatasetPermissions set such that no roles are associated with the DATASET_MANAGE_PERMISSION. Include the automatic creation of a new DatasetPermission for an hda where the DATASET_MANAGE_PERMISSION permission is associated with the hda.history.user's private role if the hda has no roles associated with the DATASET_MANAGE_PERMISSION permission. The creation of the DatasetPermission occurs when the user clicks the pencil icon for the hda. affected #: 10 files (5.2 KB) --- a/lib/galaxy/jobs/deferred/data_transfer.py Mon Jun 13 09:51:56 2011 -0400 +++ b/lib/galaxy/jobs/deferred/data_transfer.py Mon Jun 13 10:47:51 2011 -0400 @@ -108,7 +108,7 @@ ld = self.app.model.LibraryDataset( folder=sample.folder, name=library_dataset_name ) self.sa_session.add( ld ) self.sa_session.flush() - self.app.security_agent.copy_library_permissions( sample.folder, ld ) + self.app.security_agent.copy_library_permissions( FakeTrans( self.app ), sample.folder, ld ) ldda = self.app.model.LibraryDatasetDatasetAssociation( name = library_dataset_name, extension = extension, dbkey = '?', --- a/lib/galaxy/model/__init__.py Mon Jun 13 09:51:56 2011 -0400 +++ b/lib/galaxy/model/__init__.py Mon Jun 13 10:47:51 2011 -0400 @@ -594,6 +594,17 @@ if dp.action == trans.app.security_agent.permitted_actions.DATASET_ACCESS.action: roles.append( dp.role ) return roles + def get_manage_permissions_roles( self, trans ): + roles = [] + for dp in self.actions: + if dp.action == trans.app.security_agent.permitted_actions.DATASET_MANAGE_PERMISSIONS.action: + roles.append( dp.role ) + return roles + def has_manage_permissions_roles( self, trans ): + for dp in self.actions: + if dp.action == trans.app.security_agent.permitted_actions.DATASET_MANAGE_PERMISSIONS.action: + return True + return False class DatasetInstance( object ): """A base class for all 'dataset instances', HDAs, LDAs, etc""" @@ -1265,6 +1276,10 @@ return def get_access_roles( self, trans ): return self.dataset.get_access_roles( trans ) + def get_manage_permissions_roles( self, trans ): + return self.dataset.get_manage_permissions_roles( trans ) + def has_manage_permissions_roles( self, trans ): + return self.dataset.has_manage_permissions_roles( trans ) def get_info_association( self, restrict=False, inherited=False ): # If restrict is True, we will return this ldda's info_association whether it # exists or not ( in which case None will be returned ). If restrict is False, --- a/lib/galaxy/security/__init__.py Mon Jun 13 09:51:56 2011 -0400 +++ b/lib/galaxy/security/__init__.py Mon Jun 13 10:47:51 2011 -0400 @@ -70,7 +70,7 @@ raise "Unimplemented Method" def set_dataset_permission( self, dataset, permission ): raise "Unimplemented Method" - def set_all_library_permissions( self, dataset, permissions ): + def set_all_library_permissions( self, trans, dataset, permissions ): raise "Unimplemented Method" def library_is_public( self, library ): raise "Unimplemented Method" @@ -479,6 +479,19 @@ Set new permissions on a dataset, eliminating all current permissions permissions looks like: { Action : [ Role, Role ] } """ + for action, roles in permissions.items(): + # Make sure that DATASET_MANAGE_PERMISSIONS is associated with at least 1 role + has_dataset_manage_permissions = False + for action, roles in permissions.items(): + if isinstance( action, Action ): + if action == self.permitted_actions.DATASET_MANAGE_PERMISSIONS and roles: + has_dataset_manage_permissions = True + break + elif action == self.permitted_actions.DATASET_MANAGE_PERMISSIONS.action and roles: + has_dataset_manage_permissions = True + break + if not has_dataset_manage_permissions: + return "At least 1 role must be associated with the <b>manage permissions</b> permission on this dataset." flush_needed = False # Delete all of the current permissions on the dataset for dp in dataset.actions: @@ -493,6 +506,7 @@ flush_needed = True if flush_needed: self.sa_session.flush() + return "" def set_dataset_permission( self, dataset, permission={} ): """ Set a specific permission on a dataset, leaving all other current permissions on the dataset alone @@ -580,7 +594,7 @@ for user in users: self.associate_components( user=user, role=sharing_role ) self.set_dataset_permission( dataset, { self.permitted_actions.DATASET_ACCESS : [ sharing_role ] } ) - def set_all_library_permissions( self, library_item, permissions={} ): + def set_all_library_permissions( self, trans, library_item, permissions={} ): # Set new permissions on library_item, eliminating all current permissions flush_needed = False for role_assoc in library_item.actions: @@ -595,14 +609,21 @@ for role_assoc in [ permission_class( action, library_item, role ) for role in roles ]: self.sa_session.add( role_assoc ) flush_needed = True - if isinstance( library_item, self.model.LibraryDatasetDatasetAssociation ) and \ - action == self.permitted_actions.LIBRARY_MANAGE.action: - # Handle the special case when we are setting the LIBRARY_MANAGE_PERMISSION on a - # library_dataset_dataset_association since the roles need to be applied to the - # DATASET_MANAGE_PERMISSIONS permission on the associated dataset - permissions = {} - permissions[ self.permitted_actions.DATASET_MANAGE_PERMISSIONS ] = roles - self.set_dataset_permission( library_item.dataset, permissions ) + if isinstance( library_item, self.model.LibraryDatasetDatasetAssociation ): + # Permission setting related to DATASET_MANAGE_PERMISSIONS was broken for a period of time, + # so it is possible that some Datasets have no roles associated with the DATASET_MANAGE_PERMISSIONS + # permission. In this case, we'll reset this permission to the library_item user's private role. + if not library_item.dataset.has_manage_permissions_roles( trans ): + permission = {} + permissions[ self.permitted_actions.DATASET_MANAGE_PERMISSIONS ] = [ trans.app.security_agent.get_private_user_role( library_item.user ) ] + self.set_dataset_permission( library_item.dataset, permissions ) + if action == self.permitted_actions.LIBRARY_MANAGE.action and roles: + # Handle the special case when we are setting the LIBRARY_MANAGE_PERMISSION on a + # library_dataset_dataset_association since the roles need to be applied to the + # DATASET_MANAGE_PERMISSIONS permission on the associated dataset. + permissions = {} + permissions[ self.permitted_actions.DATASET_MANAGE_PERMISSIONS ] = roles + self.set_dataset_permission( library_item.dataset, permissions ) if flush_needed: self.sa_session.flush() def library_is_public( self, library, contents=False ): @@ -752,7 +773,7 @@ else: permissions[ self.get_action( v.action ) ] = in_roles return permissions, in_roles, error, msg - def copy_library_permissions( self, source_library_item, target_library_item, user=None ): + def copy_library_permissions( self, trans, source_library_item, target_library_item, user=None ): # Copy all relevant permissions from source. permissions = {} for role_assoc in source_library_item.actions: @@ -762,7 +783,7 @@ permissions[role_assoc.action].append( role_assoc.role ) else: permissions[role_assoc.action] = [ role_assoc.role ] - self.set_all_library_permissions( target_library_item, permissions ) + self.set_all_library_permissions( trans, target_library_item, permissions ) if user: item_class = None for item_class, permission_class in self.library_item_assocs: --- a/lib/galaxy/tools/actions/upload_common.py Mon Jun 13 09:51:56 2011 -0400 +++ b/lib/galaxy/tools/actions/upload_common.py Mon Jun 13 10:47:51 2011 -0400 @@ -143,7 +143,7 @@ folder.add_folder( new_folder ) trans.sa_session.add( new_folder ) trans.sa_session.flush() - trans.app.security_agent.copy_library_permissions( folder, new_folder ) + trans.app.security_agent.copy_library_permissions( trans, folder, new_folder ) folder = new_folder if library_bunch.replace_dataset: ld = library_bunch.replace_dataset @@ -151,7 +151,7 @@ ld = trans.app.model.LibraryDataset( folder=folder, name=uploaded_dataset.name ) trans.sa_session.add( ld ) trans.sa_session.flush() - trans.app.security_agent.copy_library_permissions( folder, ld ) + trans.app.security_agent.copy_library_permissions( trans, folder, ld ) ldda = trans.app.model.LibraryDatasetDatasetAssociation( name = uploaded_dataset.name, extension = uploaded_dataset.file_type, dbkey = uploaded_dataset.dbkey, @@ -167,7 +167,7 @@ ldda.message = library_bunch.message trans.sa_session.flush() # Permissions must be the same on the LibraryDatasetDatasetAssociation and the associated LibraryDataset - trans.app.security_agent.copy_library_permissions( ld, ldda ) + trans.app.security_agent.copy_library_permissions( trans, ld, ldda ) if library_bunch.replace_dataset: # Copy the Dataset level permissions from replace_dataset to the new LibraryDatasetDatasetAssociation.dataset trans.app.security_agent.copy_dataset_permissions( library_bunch.replace_dataset.library_dataset_dataset_association.dataset, ldda.dataset ) --- a/lib/galaxy/web/controllers/library_common.py Mon Jun 13 09:51:56 2011 -0400 +++ b/lib/galaxy/web/controllers/library_common.py Mon Jun 13 10:47:51 2011 -0400 @@ -232,10 +232,10 @@ for k, v in trans.app.model.Library.permitted_actions.items(): in_roles = [ trans.sa_session.query( trans.app.model.Role ).get( x ) for x in util.listify( params.get( k + '_in', [] ) ) ] permissions[ trans.app.security_agent.get_action( v.action ) ] = in_roles - trans.app.security_agent.set_all_library_permissions( library, permissions ) + trans.app.security_agent.set_all_library_permissions( trans, library, permissions ) trans.sa_session.refresh( library ) # Copy the permissions to the root folder - trans.app.security_agent.copy_library_permissions( library, library.root_folder ) + trans.app.security_agent.copy_library_permissions( trans, library, library.root_folder ) message = "Permissions updated for library '%s'." % library.name return trans.response.send_redirect( web.url_for( controller='library_common', action='library_permissions', @@ -285,7 +285,7 @@ trans.sa_session.add( new_folder ) trans.sa_session.flush() # New folders default to having the same permissions as their parent folder - trans.app.security_agent.copy_library_permissions( parent_folder, new_folder ) + trans.app.security_agent.copy_library_permissions( trans, parent_folder, new_folder ) # If we're creating in the API, we're done if cntrller == 'api': return 200, dict( created=new_folder ) @@ -411,7 +411,7 @@ # and it is not inherited. in_roles = [ trans.sa_session.query( trans.app.model.Role ).get( int( x ) ) for x in util.listify( params.get( k + '_in', [] ) ) ] permissions[ trans.app.security_agent.get_action( v.action ) ] = in_roles - trans.app.security_agent.set_all_library_permissions( folder, permissions ) + trans.app.security_agent.set_all_library_permissions( trans, folder, permissions ) trans.sa_session.refresh( folder ) message = "Permissions updated for folder '%s'." % folder.name return trans.response.send_redirect( web.url_for( controller='library_common', @@ -625,33 +625,50 @@ else: roles = trans.app.security_agent.get_legitimate_roles( trans, ldda.dataset, cntrller ) if params.get( 'update_roles_button', False ): - a = trans.app.security_agent.get_action( trans.app.security_agent.permitted_actions.DATASET_ACCESS.action ) + # Dataset permissions + access_action = trans.app.security_agent.get_action( trans.app.security_agent.permitted_actions.DATASET_ACCESS.action ) + manage_permissions_action = trans.app.security_agent.get_action( trans.app.security_agent.permitted_actions.DATASET_MANAGE_PERMISSIONS.action ) permissions, in_roles, error, message = \ trans.app.security_agent.derive_roles_from_access( trans, trans.app.security.decode_id( library_id ), cntrller, library=True, **kwd ) + # Keep roles for DATASET_MANAGE_PERMISSIONS on the dataset + if not ldda.has_manage_permissions_roles( trans ): + # Permission setting related to DATASET_MANAGE_PERMISSIONS was broken for a period of time, + # so it is possible that some Datasets have no roles associated with the DATASET_MANAGE_PERMISSIONS + # permission. In this case, we'll reset this permission to the ldda user's private role. + #dataset_manage_permissions_roles = [ trans.app.security_agent.get_private_user_role( ldda.user ) ] + permissions[ manage_permissions_action ] = [ trans.app.security_agent.get_private_user_role( ldda.user ) ] + else: + permissions[ manage_permissions_action ] = ldda.get_manage_permissions_roles( trans ) for ldda in lddas: # Set the DATASET permissions on the Dataset. if error: # Keep the original role associations for the DATASET_ACCESS permission on the ldda. - permissions[ a ] = ldda.get_access_roles( trans ) - trans.app.security_agent.set_all_dataset_permissions( ldda.dataset, permissions ) - trans.sa_session.refresh( ldda.dataset ) - # Set the LIBRARY permissions on the LibraryDataset. The LibraryDataset and - # LibraryDatasetDatasetAssociation will be set with the same permissions. - permissions = {} - for k, v in trans.app.model.Library.permitted_actions.items(): - if k != 'LIBRARY_ACCESS': - # LIBRARY_ACCESS is a special permission set only at the library level and it is not inherited. - in_roles = [ trans.sa_session.query( trans.app.model.Role ).get( x ) for x in util.listify( kwd.get( k + '_in', [] ) ) ] - permissions[ trans.app.security_agent.get_action( v.action ) ] = in_roles - for ldda in lddas: - trans.app.security_agent.set_all_library_permissions( ldda.library_dataset, permissions ) - trans.sa_session.refresh( ldda.library_dataset ) - # Set the LIBRARY permissions on the LibraryDatasetDatasetAssociation - trans.app.security_agent.set_all_library_permissions( ldda, permissions ) - trans.sa_session.refresh( ldda ) - if error: - status = 'error' - else: + permissions[ access_action ] = ldda.get_access_roles( trans ) + status = 'error' + else: + error = trans.app.security_agent.set_all_dataset_permissions( ldda.dataset, permissions ) + if error: + message += error + status = 'error' + trans.sa_session.refresh( ldda.dataset ) + if not error: + # Set the LIBRARY permissions on the LibraryDataset. The LibraryDataset and + # LibraryDatasetDatasetAssociation will be set with the same permissions. + permissions = {} + for k, v in trans.app.model.Library.permitted_actions.items(): + if k != 'LIBRARY_ACCESS': + # LIBRARY_ACCESS is a special permission set only at the library level and it is not inherited. + in_roles = [ trans.sa_session.query( trans.app.model.Role ).get( x ) for x in util.listify( kwd.get( k + '_in', [] ) ) ] + permissions[ trans.app.security_agent.get_action( v.action ) ] = in_roles + for ldda in lddas: + error = trans.app.security_agent.set_all_library_permissions( trans, ldda.library_dataset, permissions ) + trans.sa_session.refresh( ldda.library_dataset ) + if error: + message = error + else: + # Set the LIBRARY permissions on the LibraryDatasetDatasetAssociation + trans.app.security_agent.set_all_library_permissions( trans, ldda, permissions ) + trans.sa_session.refresh( ldda ) if len( lddas ) == 1: message = "Permissions updated for dataset '%s'." % ldda.name else: @@ -1211,8 +1228,8 @@ if not replace_dataset: # If replace_dataset is None, the Library level permissions will be taken from the folder and applied to the new # LDDA and LibraryDataset. - trans.app.security_agent.copy_library_permissions( folder, ldda ) - trans.app.security_agent.copy_library_permissions( folder, ldda.library_dataset ) + trans.app.security_agent.copy_library_permissions( trans, folder, ldda ) + trans.app.security_agent.copy_library_permissions( trans, folder, ldda.library_dataset ) # Make sure to apply any defined dataset permissions, allowing the permissions inherited from the folder to # over-ride the same permissions on the dataset, if they exist. dataset_permissions_dict = trans.app.security_agent.get_permissions( hda.dataset ) @@ -1235,7 +1252,7 @@ if flush_needed: trans.sa_session.flush() # Permissions must be the same on the LibraryDatasetDatasetAssociation and the associated LibraryDataset - trans.app.security_agent.copy_library_permissions( ldda.library_dataset, ldda ) + trans.app.security_agent.copy_library_permissions( trans, ldda.library_dataset, ldda ) if created_ldda_ids: created_ldda_ids = created_ldda_ids.lstrip( ',' ) ldda_id_list = created_ldda_ids.split( ',' ) @@ -1478,13 +1495,17 @@ permissions[ trans.app.security_agent.get_action( v.action ) ] = in_roles # Set the LIBRARY permissions on the LibraryDataset # NOTE: the LibraryDataset and LibraryDatasetDatasetAssociation will be set with the same permissions - trans.app.security_agent.set_all_library_permissions( library_dataset, permissions ) + error = trans.app.security_agent.set_all_library_permissions( trans, library_dataset, permissions ) trans.sa_session.refresh( library_dataset ) - # Set the LIBRARY permissions on the LibraryDatasetDatasetAssociation - trans.app.security_agent.set_all_library_permissions( library_dataset.library_dataset_dataset_association, permissions ) - trans.sa_session.refresh( library_dataset.library_dataset_dataset_association ) - message = "Permisisons updated for library dataset '%s'." % library_dataset.name - status = 'done' + if error: + message = error + status = 'error' + else: + # Set the LIBRARY permissions on the LibraryDatasetDatasetAssociation + trans.app.security_agent.set_all_library_permissions( trans, library_dataset.library_dataset_dataset_association, permissions ) + trans.sa_session.refresh( library_dataset.library_dataset_dataset_association ) + message = "Permisisons updated for library dataset '%s'." % library_dataset.name + status = 'done' roles = trans.app.security_agent.get_legitimate_roles( trans, library_dataset, cntrller ) return trans.fill_template( '/library/common/library_dataset_permissions.mako', cntrller=cntrller, --- a/lib/galaxy/web/controllers/requests_common.py Mon Jun 13 09:51:56 2011 -0400 +++ b/lib/galaxy/web/controllers/requests_common.py Mon Jun 13 10:47:51 2011 -0400 @@ -1050,7 +1050,7 @@ def __import_samples( self, trans, cntrller, request, displayable_sample_widgets, libraries, workflows, **kwd ): """ Reads the samples csv file and imports all the samples. The format of the csv file is: - SampleName,DataLibraryName,DataLibraryFolderName,HistoryName,WorkflowName,FieldValue1,FieldValue2... + SampleName,DataLibraryName,DataLibraryFolderName,HistoryName,WorkflowName,Field1Name,Field2Name... """ params = util.Params( kwd ) current_user_roles = trans.get_current_user_roles() --- a/lib/galaxy/web/controllers/root.py Mon Jun 13 09:51:56 2011 -0400 +++ b/lib/galaxy/web/controllers/root.py Mon Jun 13 10:47:51 2011 -0400 @@ -316,6 +316,13 @@ if id is not None and data.history.user is not None and data.history.user != trans.user: return trans.show_error_message( "This instance of a dataset (%s) in a history does not belong to you." % ( data.id ) ) current_user_roles = trans.get_current_user_roles() + if not data.dataset.has_manage_permissions_roles( trans ): + # Permission setting related to DATASET_MANAGE_PERMISSIONS was broken for a period of time, + # so it is possible that some Datasets have no roles associated with the DATASET_MANAGE_PERMISSIONS + # permission. In this case, we'll reset this permission to the hda user's private role. + manage_permissions_action = trans.app.security_agent.get_action( trans.app.security_agent.permitted_actions.DATASET_MANAGE_PERMISSIONS.action ) + permissions = { manage_permissions_action : [ trans.app.security_agent.get_private_user_role( data.history.user ) ] } + trans.app.security_agent.set_dataset_permission( data.dataset, permissions ) if trans.app.security_agent.can_access_dataset( current_user_roles, data.dataset ): if data.state == trans.model.Dataset.states.UPLOAD: return trans.show_error_message( "Please wait until this dataset finishes uploading before attempting to edit its metadata." ) @@ -394,18 +401,24 @@ if not trans.user: return trans.show_error_message( "You must be logged in if you want to change permissions." ) if trans.app.security_agent.can_manage_dataset( current_user_roles, data.dataset ): + access_action = trans.app.security_agent.get_action( trans.app.security_agent.permitted_actions.DATASET_ACCESS.action ) + manage_permissions_action = trans.app.security_agent.get_action( trans.app.security_agent.permitted_actions.DATASET_MANAGE_PERMISSIONS.action ) # The user associated the DATASET_ACCESS permission on the dataset with 1 or more roles. We # need to ensure that they did not associate roles that would cause accessibility problems. permissions, in_roles, error, message = \ trans.app.security_agent.derive_roles_from_access( trans, data.dataset.id, 'root', **kwd ) - a = trans.app.security_agent.get_action( trans.app.security_agent.permitted_actions.DATASET_ACCESS.action ) if error: # Keep the original role associations for the DATASET_ACCESS permission on the dataset. - permissions[ a ] = data.dataset.get_access_roles( trans ) - trans.app.security_agent.set_all_dataset_permissions( data.dataset, permissions ) + permissions[ access_action ] = data.dataset.get_access_roles( trans ) + status = 'error' + else: + error = trans.app.security_agent.set_all_dataset_permissions( data.dataset, permissions ) + if error: + message += error + status = 'error' + else: + message = 'Your changes completed successfully.' trans.sa_session.refresh( data.dataset ) - if not message: - message = 'Your changes completed successfully.' else: return trans.show_error_message( "You are not authorized to change this dataset's permissions" ) if "dbkey" in data.datatype.metadata_spec and not data.metadata.dbkey: --- a/templates/requests/common/add_samples.mako Mon Jun 13 09:51:56 2011 -0400 +++ b/templates/requests/common/add_samples.mako Mon Jun 13 10:47:51 2011 -0400 @@ -118,7 +118,7 @@ <input type="submit" name="import_samples_button" value="Import samples"/><div class="toolParamHelp" style="clear: both;"> The csv file must be in the following format:<br/> - SampleName,DataLibraryName,DataLibraryFolderName,HistoryName,WorkflowName,FieldValue1,FieldValue2... + SampleName,DataLibraryName,DataLibraryFolderName,HistoryName,WorkflowName,Field1Name,Field2Name... </div></div></form> --- a/test/functional/test_library_security.py Mon Jun 13 09:51:56 2011 -0400 +++ b/test/functional/test_library_security.py Mon Jun 13 10:47:51 2011 -0400 @@ -420,11 +420,10 @@ str( role1.id ), permissions_in=[ 'DATASET_ACCESS' ], strings_displayed=[ 'Permissions updated for 3 datasets.' ] ) + # Even though we've eliminated the roles associated with the LIBRARY_MANAGE_PERMISSIONS permission, + # none of the roles associated with the DATASET_MANAGE permission sould have been changed. check_edit_page( latest_3_lddas, - strings_displayed=[ 'View Permissions' ], - strings_not_displayed=[ 'Manage dataset permissions on', - 'can manage roles associated with permissions on this library item', - 'can import this dataset into their history for analysis' ] ) + strings_displayed=[ 'manage permissions' ] ) def test_060_restrict_access_to_library2( self ): """Testing restricting access to library2""" # Logged in as admin_user --- a/test/functional/test_sample_tracking.py Mon Jun 13 09:51:56 2011 -0400 +++ b/test/functional/test_sample_tracking.py Mon Jun 13 10:47:51 2011 -0400 @@ -869,7 +869,6 @@ sample1_dataset.transfer_status.NOT_STARTED ] self.view_sample_dataset( sample_dataset_id=self.security.encode_id( sample1_dataset.id ), strings_displayed=strings_displayed ) - ''' def test_999_reset_data_for_later_test_runs( self ): """Reseting data to enable later test runs to pass""" # Logged in as admin_user @@ -935,4 +934,3 @@ # Manually delete the group from the database refresh( group ) delete_obj( group ) - ''' Repository URL: https://bitbucket.org/galaxy/galaxy-central/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email.