From f50ee0fff3fcf803778f3a38f76852ac2c987365 Mon Sep 17 00:00:00 2001 From: Greg Von Kuster Date: Tue, 19 Aug 2008 10:04:30 -0400 Subject: [PATCH] Added security to libraries. Non-admin users can now only see libraries that contain datasets associated with the user's groups, and each library will only display those datasets that are associated with the user's groups. --- lib/galaxy/web/controllers/admin.py | 6 +- lib/galaxy/web/controllers/library.py | 81 ++++++++++++++++++++++++-- templates/admin/library/dataset.mako | 45 +++++++------- templates/library/libraries.mako | 6 +- templates/library/library.mako | 31 +++++++++- tools/data_source/access_libraries.xml | 14 ++--- 6 files changed, 142 insertions(+), 41 deletions(-) diff --git a/lib/galaxy/web/controllers/admin.py b/lib/galaxy/web/controllers/admin.py index cb8aad42c78..f0ccc2f4f07 100644 --- a/lib/galaxy/web/controllers/admin.py +++ b/lib/galaxy/web/controllers/admin.py @@ -703,6 +703,8 @@ class Admin( BaseController ): gdpa_dict[ group_id ].append( dpa ) else: gdpa_dict[ group_id ] = [ dpa ] + # Refresh the Dataset to ensure we have a valid set of DatasetGroupAssociations + dataset.dataset.refresh() # Check to see if we need to delete any GroupDatasetAssociations. This occurs if # the user unchecked all boxes for a group for group_dataset_assoc in dataset.dataset.groups: @@ -761,9 +763,11 @@ class Admin( BaseController ): dataset_actions.sort() # Get the permitted_actions of each GroupDatasetAssociation to send to the form gdas = [] - # Refresh the dataset to ensure we have a valid set of DatasetGroupAssociations + # Refresh the Dataset to ensure we have a valid set of GroupDatasetAssociations dataset.dataset.refresh() for group_dataset_assoc in dataset.dataset.groups: + # Refresh the GroupDatasetAssociation to ensure we have a valid set of permitted_actions + group_dataset_assoc.refresh() group = galaxy.model.Group.get( group_dataset_assoc.group_id ) gdas.append( ( group.id, group.name, group_dataset_assoc.permitted_actions ) ) if "dbkey" in dataset.datatype.metadata_spec and not dataset.metadata.dbkey: diff --git a/lib/galaxy/web/controllers/library.py b/lib/galaxy/web/controllers/library.py index 94e7b5731e0..43c45a58402 100644 --- a/lib/galaxy/web/controllers/library.py +++ b/lib/galaxy/web/controllers/library.py @@ -6,9 +6,12 @@ log = logging.getLogger( __name__ ) class Library( BaseController ): @web.expose - def index( self, trans, library_id = None, import_ids = [], **kwd ): - #use for importing an entry into your history + def index( self, trans, library_id=None, import_ids=[], **kwd ): + # Need user to get associated Groups and Datasets + user = trans.get_user() + libraries = [] if import_ids: + # Used for importing a dataset into a user's history if not isinstance( import_ids, list ): import_ids = [import_ids] history = trans.get_history() @@ -19,5 +22,75 @@ class Library( BaseController ): history.flush() return trans.show_ok_message( "%i datasets have been imported into your history" % len( import_ids ), refresh_frames=['history'] ) elif library_id: - return trans.fill_template( '/library/library.mako', library=trans.app.model.Library.get( library_id ) ) - return trans.fill_template( '/library/libraries.mako', libraries=trans.app.model.Library.select() ) + # Since permitted_actions are kept with the GroupDatasetAssociation, each accessible Library will only + # display the subset of [ it's complete set of ] datasets that the user has permission to access. We + # pass group_ids so this can be handled in the template. + if not user: + group_ids = [ trans.app.model.Group.select_by( name='public' )[0].id ] + else: + group_ids = [] + for user_group_assoc in user.groups: + group_ids.append( user_group_assoc.group_id ) + library = trans.app.model.Library.get( library_id ) + return trans.fill_template( '/library/library.mako', library=library, group_ids=group_ids ) + if user: + # Only display libraries that contain datasets associated with the user's groups + group_ids = [] + for user_group_assoc in user.groups: + group = trans.app.model.Group.get( user_group_assoc.group_id ) + group_ids.append( group.id ) + libs = trans.app.model.Library.select() + for library in libs: + user_can_access = False + # Check for public datasets in the Library's root folder + for library_folder_dataset_assoc in library.root_folder.datasets: + if user_can_access: + break + dataset = trans.app.model.Dataset.get( library_folder_dataset_assoc.dataset_id ) + for group_dataset_assoc in dataset.groups: + if group_dataset_assoc.group_id in group_ids: + libraries.append( library ) + user_can_access = True + break + for folder in library.root_folder.folders: + if user_can_access: + break + for library_folder_dataset_assoc in folder.datasets: + if user_can_access: + break + dataset = trans.app.model.Dataset.get( library_folder_dataset_assoc.dataset_id ) + for group_dataset_assoc in dataset.groups: + if group_dataset_assoc.group_id in group_ids: + libraries.append( library ) + user_can_access = True + break + else: + # Only display libraries that contain datasets associated with the public group + group_ids = [ trans.app.model.Group.select_by( name='public' )[0].id ] + libs = trans.app.model.Library.select() + for library in libs: + public_library = False + # Check for public datasets in the Library's root folder + for library_folder_dataset_assoc in library.root_folder.datasets: + if public_library: + break + dataset = trans.app.model.Dataset.get( library_folder_dataset_assoc.dataset_id ) + for group_dataset_assoc in dataset.groups: + if group_dataset_assoc.group_id in group_ids: + libraries.append( library ) + public_library = True + break + # Check for public datasets in the root folder's sub-folders + for folder in library.root_folder.folders: + if public_library: + break + for library_folder_dataset_assoc in folder.datasets: + if public_library: + break + dataset = trans.app.model.Dataset.get( library_folder_dataset_assoc.dataset_id ) + for group_dataset_assoc in dataset.groups: + if group_dataset_assoc.group_id in group_ids: + libraries.append( library ) + public_library = True + break + return trans.fill_template( '/library/libraries.mako', group_ids=group_ids, libraries=libraries ) diff --git a/templates/admin/library/dataset.mako b/templates/admin/library/dataset.mako index b522a05256d..a7877e54479 100644 --- a/templates/admin/library/dataset.mako +++ b/templates/admin/library/dataset.mako @@ -1,6 +1,7 @@ <%inherit file="/base.mako"/> <%def name="title()">Edit Dataset Attributes + <%def name="datatype( dataset, datatypes )"> + +<%def name="group_dataset_permitted_actions( dataset_actions, gda )"> + %for da in dataset_actions: + <% check = False %> + %for action in gda[2]: + %if action == da: + <% + check = True + break + %> + %endif + %endfor + %if check: + + %else: + + %endif + ${da}
+ %endfor + +
Group Associations
%for gda in gdas: -
- ${gda[1]} -
+
${gda[1]}
- %for da in dataset_actions: - <% check = False %> - %for action in gda[2]: - %if action == da: - <% - check = True - break - %> - %endif - %endfor - %if check: - - %else: - - %endif - ${da}
- %endfor -
+ ${group_dataset_permitted_actions( dataset_actions, gda )}
%endfor
diff --git a/templates/library/libraries.mako b/templates/library/libraries.mako index 247333f709c..1176bd2162a 100644 --- a/templates/library/libraries.mako +++ b/templates/library/libraries.mako @@ -1,12 +1,12 @@ <%inherit file="/base.mako"/> -<%def name="title()">View Libraries +<%def name="title()">Libraries You Can Access
-
View Library
+
Libraries You Can Access
%for library in libraries: %endfor
diff --git a/templates/library/library.mako b/templates/library/library.mako index 2706efcde25..373ff9c4118 100644 --- a/templates/library/library.mako +++ b/templates/library/library.mako @@ -3,17 +3,41 @@ <%def name="render_component( component )"> <% if isinstance( component, trans.app.model.LibraryFolder ): - return render_folder( component ) + render = False + # Check the folder's datasets to see what can be rendered + for library_folder_dataset_assoc in component.datasets: + if render: + break + dataset = trans.app.model.Dataset.get( library_folder_dataset_assoc.dataset_id ) + for group_dataset_assoc in dataset.groups: + if group_dataset_assoc.group_id in group_ids: + render = True + break + # TODO: Do we need to upgrade sqlalchemy? The following shouldn't be necessary if the mappers work correctly. + # Check the folder's sub-folders to see what can be rendered + for library_folder in component.folders: + render_component( library_folder ) + if render: + return render_folder( component ) elif isinstance( component, trans.app.model.LibraryFolderDatasetAssociation ): - return render_dataset( component ) + render = False + dataset = trans.app.model.Dataset.get( component.dataset_id ) + for group_dataset_assoc in dataset.groups: + if group_dataset_assoc.group_id in group_ids: + render = True + break + if render: + return render_dataset( component ) %> + ## Render the dataset `data` as history item, using `hid` as the displayed id <%def name="render_dataset( data )">
${data.name}
+ ## Render a folder <%def name="render_folder( this_folder )">
@@ -31,11 +55,12 @@
+ <%def name="title()">View Library: ${library.name}
Import from Library: ${library.name}
- + ${render_folder( library.root_folder )}
diff --git a/tools/data_source/access_libraries.xml b/tools/data_source/access_libraries.xml index ffd383c481a..e6dabfbf1b1 100644 --- a/tools/data_source/access_libraries.xml +++ b/tools/data_source/access_libraries.xml @@ -1,11 +1,7 @@ - - stored locally - - - - - - - + stored locally + + + + \ No newline at end of file