details: http://www.bx.psu.edu/hg/galaxy/rev/4a3a4af6be53 changeset: 3273:4a3a4af6be53 user: Greg Von Kuster <greg@bx.psu.edu> date: Tue Jan 26 16:16:03 2010 -0500 description: If a dataset is not public, populate the permissions forms for it with legitimate roles only. diffstat: lib/galaxy/model/__init__.py | 42 +----------------- lib/galaxy/security/__init__.py | 58 +++++++++++++++++++++++-- lib/galaxy/web/controllers/library_common.py | 12 ++-- lib/galaxy/web/controllers/root.py | 5 +- templates/dataset/edit_attributes.mako | 2 +- templates/dataset/security_common.mako | 2 +- templates/mobile/manage_library.mako | 2 +- test/functional/test_security_and_libraries.py | 4 +- 8 files changed, 70 insertions(+), 57 deletions(-) diffs (301 lines): diff -r 3ca480a2666b -r 4a3a4af6be53 lib/galaxy/model/__init__.py --- a/lib/galaxy/model/__init__.py Tue Jan 26 14:03:48 2010 -0500 +++ b/lib/galaxy/model/__init__.py Tue Jan 26 16:16:03 2010 -0500 @@ -5,7 +5,7 @@ the relationship cardinalities are obvious (e.g. prefer Dataset to Data) """ -import os.path, os, errno, sys, codecs, operator +import os.path, os, errno, sys, codecs import galaxy.datatypes from galaxy.util.bunch import Bunch from galaxy import util @@ -719,7 +719,8 @@ if isinstance(hda_name, str): hda_name = unicode(hda_name, 'utf-8') return hda_name - + def get_access_roles( self, trans ): + return self.dataset.get_access_roles( trans ) class HistoryDatasetAssociationDisplayAtAuthorization( object ): def __init__( self, hda=None, user=None, site=None ): @@ -759,43 +760,6 @@ if lp.action == trans.app.security_agent.permitted_actions.LIBRARY_ACCESS.action: roles.append( lp.role ) return roles - def get_legitimate_roles( self, trans ): - if trans.app.security_agent.library_is_public( self ): - return trans.sa_session.query( trans.app.model.Role ) \ - .filter( trans.app.model.Role.table.c.deleted==False ) \ - .order_by( trans.app.model.Role.table.c.name ) - def sort_by_attr( seq, attr ): - """ - Sort the sequence of objects by object's attribute - Arguments: - seq - the list or any sequence (including immutable one) of objects to sort. - attr - the name of attribute to sort by - """ - # Use the "Schwartzian transform" - # Create the auxiliary list of tuples where every i-th tuple has form - # (seq[i].attr, i, seq[i]) and sort it. The second item of tuple is needed not - # only to provide stable sorting, but mainly to eliminate comparison of objects - # (which can be expensive or prohibited) in case of equal attribute values. - intermed = map( None, map( getattr, seq, ( attr, ) * len( seq ) ), xrange( len( seq ) ), seq ) - intermed.sort() - return map( operator.getitem, intermed, ( -1, ) * len( intermed ) ) - roles = set() - # If a library has roles associated with the LIBRARY_ACCESS permission, we need to start with them. - access_roles = self.get_access_roles( trans ) - for role in access_roles: - roles.add( role ) - # Each role potentially has users. We need to find all roles that each of those users have. - for ura in role.users: - roles.add( ura.role ) - # Each role also potentially has groups which, in turn, have members ( users ). We need to - # find all roles that each group's members have. - for gra in role.groups: - group = gra.group - for uga in group.users: - user = uga.user - for ura in user.roles: - roles.add( ura.role ) - return sort_by_attr( [ role for role in roles ], 'name' ) def get_display_name( self ): # Library name can be either a string or a unicode object. If string, # convert to unicode object assuming 'utf-8' format. diff -r 3ca480a2666b -r 4a3a4af6be53 lib/galaxy/security/__init__.py --- a/lib/galaxy/security/__init__.py Tue Jan 26 14:03:48 2010 -0500 +++ b/lib/galaxy/security/__init__.py Tue Jan 26 16:16:03 2010 -0500 @@ -2,7 +2,7 @@ Galaxy Security """ -import logging, socket +import logging, socket, operator from datetime import datetime, timedelta from galaxy.util.bunch import Bunch from galaxy.util import listify @@ -79,6 +79,8 @@ raise "Unimplemented Method" def get_permissions( self, library_dataset ): raise "Unimplemented Method" + def get_legitimate_roles( self, trans, item ): + raise "Unimplemented Method" def check_library_dataset_access( self, trans, library_id, **kwd ): raise "Unimplemented Method" def get_component_associations( self, **kwd ): @@ -107,6 +109,51 @@ def sa_session( self ): """Returns a SQLAlchemy session""" return self.model.context + def get_legitimate_roles( self, trans, item ): + """ + Return a sorted list of legitimate roles that can be associated with a permission on + item where item is a Library or a Dataset. If item is public, all roles are legitimate. + If item is restricted, legitimate roles are derived from the users and groups associated + with each role that is associated with the access permission ( i.e., DATASET_MANAGE_PERMISSIONS + or LIBRARY_MANAGE ) on item. + """ + if ( isinstance( item, self.model.Library ) and self.library_is_public( item ) ) or \ + ( isinstance( item, self.model.Dataset ) and self.dataset_is_public( item ) ): + return trans.sa_session.query( trans.app.model.Role ) \ + .filter( trans.app.model.Role.table.c.deleted==False ) \ + .order_by( trans.app.model.Role.table.c.name ) + def sort_by_attr( seq, attr ): + """ + Sort the sequence of objects by object's attribute + Arguments: + seq - the list or any sequence (including immutable one) of objects to sort. + attr - the name of attribute to sort by + """ + # Use the "Schwartzian transform" + # Create the auxiliary list of tuples where every i-th tuple has form + # (seq[i].attr, i, seq[i]) and sort it. The second item of tuple is needed not + # only to provide stable sorting, but mainly to eliminate comparison of objects + # (which can be expensive or prohibited) in case of equal attribute values. + intermed = map( None, map( getattr, seq, ( attr, ) * len( seq ) ), xrange( len( seq ) ), seq ) + intermed.sort() + return map( operator.getitem, intermed, ( -1, ) * len( intermed ) ) + roles = set() + # If a library has roles associated with the LIBRARY_ACCESS permission, we need to start with them. + access_roles = item.get_access_roles( trans ) + for role in access_roles: + roles.add( role ) + # Each role potentially has users. We need to find all roles that each of those users have. + for ura in role.users: + roles.add( ura.role ) + # Each role also potentially has groups which, in turn, have members ( users ). We need to + # find all roles that each group's members have. + for gra in role.groups: + group = gra.group + for uga in group.users: + user = uga.user + for ura in user.roles: + roles.add( ura.role ) + return sort_by_attr( [ role for role in roles ], 'name' ) def allow_action( self, roles, action, item ): """ Method for checking a permission for the current user ( based on roles ) to perform a @@ -328,8 +375,9 @@ self.sa_session.flush() def get_permissions( self, item ): """ - Return a dictionary containing the actions and associated roles on item. - The dictionary looks like: { Action : [ Role, Role ] } + Return a dictionary containing the actions and associated roles on item + where item is one of Library, LibraryFolder, LibraryDatasetDatasetAssociation, + LibraryDataset, Dataset. The dictionary looks like: { Action : [ Role, Role ] }. """ permissions = {} for item_permission in item.actions: @@ -409,7 +457,7 @@ # accessible will be True only if at least 1 user has every role in DATASET_ACCESS_in accessible = False # legitimate will be True only if all roles in DATASET_ACCESS_in are in the set - # of roles returned from library.get_legitimate_roles() + # of roles returned from self.get_legitimate_roles() legitimate = False error = None for k, v in get_permitted_actions( filter='DATASET' ).items(): @@ -422,7 +470,7 @@ # not public. This will keep ill-legitimate roles from being associated with the DATASET_ACCESS # permission on the dataset (i.e., if Role1 is associated with LIBRARY_ACCESS, then only those users # that have Role1 should be associated with DATASET_ACCESS. - legitimate_roles = library.get_legitimate_roles( trans ) + legitimate_roles = self.get_legitimate_roles( trans, library ) ill_legitimate_roles = [] for role in in_roles: if role not in legitimate_roles: diff -r 3ca480a2666b -r 4a3a4af6be53 lib/galaxy/web/controllers/library_common.py --- a/lib/galaxy/web/controllers/library_common.py Tue Jan 26 14:03:48 2010 -0500 +++ b/lib/galaxy/web/controllers/library_common.py Tue Jan 26 16:16:03 2010 -0500 @@ -183,7 +183,7 @@ id=trans.security.encode_id( library.id ), msg=util.sanitize_text( msg ), messagetype='done' ) ) - roles = library.get_legitimate_roles( trans ) + roles = trans.app.security_agent.get_legitimate_roles( trans, library ) return trans.fill_template( '/library/common/library_permissions.mako', cntrller=cntrller, library=library, @@ -307,7 +307,7 @@ # If the library is public all roles are legitimate, but if the library is restricted, only those # roles associated with the LIBRARY_ACCESS permission are legitimate. library = trans.sa_session.query( trans.app.model.Library ).get( trans.security.decode_id( library_id ) ) - roles = library.get_legitimate_roles( trans ) + roles = trans.app.security_agent.get_legitimate_roles( trans, library ) return trans.fill_template( '/library/common/folder_permissions.mako', cntrller=cntrller, folder=folder, @@ -492,7 +492,7 @@ # If the library is public all roles are legitimate, but if the library is restricted, only those # roles associated with the LIBRARY_ACCESS permission are legitimate. library = trans.sa_session.query( trans.app.model.Library ).get( trans.security.decode_id( library_id ) ) - roles = library.get_legitimate_roles( trans ) + roles = trans.app.security_agent.get_legitimate_roles( trans, library ) if params.get( 'update_roles_button', False ): current_user_roles = trans.get_current_user_roles() if cntrller=='library_admin' or ( trans.app.security_agent.can_manage_library_item( current_user_roles, ldda ) and \ @@ -694,7 +694,7 @@ # If the library is public, all active roles are legitimate. If the library is restricted by the # LIBRARY_ACCESS permission, only those roles associated with that permission are legitimate. library = trans.sa_session.query( trans.app.model.Library ).get( trans.security.decode_id( library_id ) ) - roles = library.get_legitimate_roles( trans ) + roles = trans.app.security_agent.get_legitimate_roles( trans, library ) # Send the current history to the form to enable importing datasets from history to library history = trans.get_history() trans.sa_session.refresh( history ) @@ -977,7 +977,7 @@ dbkeys = get_dbkey_options( last_used_build ) # Send list of legitimate roles to the form so the dataset can be associated with 1 or more of them. library = trans.sa_session.query( trans.app.model.Library ).get( trans.security.decode_id( library_id ) ) - roles = library.get_legitimate_roles( trans ) + roles = trans.app.security_agent.get_legitimate_roles( trans, library ) return trans.fill_template( "/library/common/upload.mako", upload_option=upload_option, library_id=library_id, @@ -1101,7 +1101,7 @@ msg = "You are not authorized to managed the permissions of this dataset" messagetype = "error" library = trans.sa_session.query( trans.app.model.Library ).get( trans.security.decode_id( library_id ) ) - roles = library.get_legitimate_roles( trans ) + roles = trans.app.security_agent.get_legitimate_roles( trans, library ) return trans.fill_template( '/library/common/library_dataset_permissions.mako', cntrller=cntrller, library_dataset=library_dataset, diff -r 3ca480a2666b -r 4a3a4af6be53 lib/galaxy/web/controllers/root.py --- a/lib/galaxy/web/controllers/root.py Tue Jan 26 14:03:48 2010 -0500 +++ b/lib/galaxy/web/controllers/root.py Tue Jan 26 16:16:03 2010 -0500 @@ -357,11 +357,12 @@ # the built-in 'id' is overwritten in lots of places as well ldatatypes = [ dtype_name for dtype_name, dtype_value in trans.app.datatypes_registry.datatypes_by_extension.iteritems() if dtype_value.allow_datatype_change ] ldatatypes.sort() - trans.log_event( "Opened edit view on dataset %s" % str(id) ) + all_roles = trans.app.security_agent.get_legitimate_roles( trans, data.dataset ) return trans.fill_template( "/dataset/edit_attributes.mako", data=data, datatypes=ldatatypes, - current_user_roles=current_user_roles ) + current_user_roles=current_user_roles, + all_roles=all_roles ) else: return trans.show_error_message( "You do not have permission to edit this dataset's ( id: %s ) information." % str( id ) ) diff -r 3ca480a2666b -r 4a3a4af6be53 templates/dataset/edit_attributes.mako --- a/templates/dataset/edit_attributes.mako Tue Jan 26 14:03:48 2010 -0500 +++ b/templates/dataset/edit_attributes.mako Tue Jan 26 16:16:03 2010 -0500 @@ -164,7 +164,7 @@ %if trans.app.security_agent.can_manage_dataset( current_user_roles, data.dataset ): <%namespace file="/dataset/security_common.mako" import="render_permission_form" /> - ${render_permission_form( data.dataset, data.get_display_name(), h.url_for( controller='root', action='edit', id=data.id ), current_user_roles )} + ${render_permission_form( data.dataset, data.get_display_name(), h.url_for( controller='root', action='edit', id=data.id ), all_roles )} %elif trans.user: <div class="toolForm"> <div class="toolFormTitle">View Permissions</div> diff -r 3ca480a2666b -r 4a3a4af6be53 templates/dataset/security_common.mako --- a/templates/dataset/security_common.mako Tue Jan 26 14:03:48 2010 -0500 +++ b/templates/dataset/security_common.mako Tue Jan 26 16:16:03 2010 -0500 @@ -102,7 +102,7 @@ }); </script> <div class="toolForm"> - <div class="toolFormTitle">Manage ${obj_type} permissions and role associations of ${obj_str}</div> + <div class="toolFormTitle">Manage ${obj_type} permissions on ${obj_str}</div> <div class="toolFormBody"> <form name="edit_role_associations" id="edit_role_associations" action="${form_url}" method="post"> <div class="form-row"></div> diff -r 3ca480a2666b -r 4a3a4af6be53 templates/mobile/manage_library.mako --- a/templates/mobile/manage_library.mako Tue Jan 26 14:03:48 2010 -0500 +++ b/templates/mobile/manage_library.mako Tue Jan 26 16:16:03 2010 -0500 @@ -50,7 +50,7 @@ </div> %endif %if trans.app.security_agent.can_manage_library_item( current_user_roles, library ): - <% roles = library.get_legitimate_roles( trans ) %> + <% roles = trans.app.security_agent.get_legitimate_roles( trans, library ) %> ${render_permission_form( library, library.name, h.url_for( controller='library_common', cntrller='mobile', action='library_permissions', id=trans.security.encode_id( library.id ) ), roles )} %endif diff -r 3ca480a2666b -r 4a3a4af6be53 test/functional/test_security_and_libraries.py --- a/test/functional/test_security_and_libraries.py Tue Jan 26 14:03:48 2010 -0500 +++ b/test/functional/test_security_and_libraries.py Tue Jan 26 16:16:03 2010 -0500 @@ -1457,7 +1457,7 @@ self.visit_url( '%s/root/edit?id=%s' % ( self.url, str( last_hda_created.id ) ) ) self.check_page_for_string( 'Edit Attributes' ) self.check_page_for_string( last_hda_created.name ) - check_str = 'Manage dataset permissions and role associations of %s' % last_hda_created.name + check_str = 'Manage dataset permissions on %s' % last_hda_created.name self.check_page_for_string( check_str ) self.check_page_for_string( 'Role members can manage the roles associated with permissions on this dataset' ) self.check_page_for_string( 'Role members can import this dataset into their history for analysis' ) @@ -1510,7 +1510,7 @@ self.check_page_for_string( last_hda_created.name ) try: # This should no longer be possible - check_str = 'Manage dataset permissions and role associations of %s' % last_hda_created.name + check_str = 'Manage dataset permissions on %s' % last_hda_created.name self.check_page_for_string( check_str ) raise AssertionError( '%s incorrectly has DATASET_MANAGE_PERMISSIONS on datasets imported from a library' % admin_user.email ) except: