details: http://www.bx.psu.edu/hg/galaxy/rev/c37de7a983e7 changeset: 3682:c37de7a983e7 user: rc date: Thu Apr 22 21:11:17 2010 -0400 description: lims: added request_types permissions diffstat: lib/galaxy/model/__init__.py | 19 ++ lib/galaxy/model/mapping.py | 15 + lib/galaxy/model/migrate/versions/0045_request_type_permissions_table.py | 34 +++ lib/galaxy/security/__init__.py | 38 ++++- lib/galaxy/web/controllers/requests.py | 3 +- lib/galaxy/web/controllers/requests_admin.py | 59 +++++- templates/admin/requests/request_type_permissions.mako | 92 ++++++++++ templates/webapps/galaxy/base_panels.mako | 2 +- 8 files changed, 253 insertions(+), 9 deletions(-) diffs (392 lines): diff -r 1b30f5fa152b -r c37de7a983e7 lib/galaxy/model/__init__.py --- a/lib/galaxy/model/__init__.py Thu Apr 22 16:05:08 2010 -0400 +++ b/lib/galaxy/model/__init__.py Thu Apr 22 21:11:17 2010 -0400 @@ -73,6 +73,18 @@ if can_show: libraries[ library ] = hidden_folder_ids return libraries + def accessible_request_types(self, trans): + # get all permitted libraries for this user + all_rt_list = trans.sa_session.query( trans.app.model.RequestType ) \ + .filter( trans.app.model.RequestType.table.c.deleted == False ) \ + .order_by( trans.app.model.RequestType.name ) + roles = self.all_roles() + rt_list = [] + for rt in all_rt_list: + for permission in rt.actions: + if permission.role.id in [r.id for r in roles]: + rt_list.append(rt) + return list(set(rt_list)) class Job( object ): """ @@ -1445,6 +1457,7 @@ self.comment = comment class RequestType( object ): + permitted_actions = get_permitted_actions( filter='REQUEST_TYPE' ) def __init__(self, name=None, desc=None, request_form=None, sample_form=None, datatx_info=None): self.name = name @@ -1452,6 +1465,12 @@ self.request_form = request_form self.sample_form = sample_form self.datatx_info = datatx_info + +class RequestTypePermissions( object ): + def __init__( self, action, request_type, role ): + self.action = action + self.request_type = request_type + self.role = role class Sample( object ): transfer_status = Bunch( NOT_STARTED = 'Not started', diff -r 1b30f5fa152b -r c37de7a983e7 lib/galaxy/model/mapping.py --- a/lib/galaxy/model/mapping.py Thu Apr 22 16:05:08 2010 -0400 +++ b/lib/galaxy/model/mapping.py Thu Apr 22 21:11:17 2010 -0400 @@ -628,6 +628,14 @@ Column( "datatx_info", JSONType() ), Column( "deleted", Boolean, index=True, default=False ) ) +RequestTypePermissions.table = Table( "request_type_permissions", metadata, + Column( "id", Integer, primary_key=True ), + Column( "create_time", DateTime, default=now ), + Column( "update_time", DateTime, default=now, onupdate=now ), + Column( "action", TEXT ), + Column( "request_type_id", Integer, ForeignKey( "request_type.id" ), nullable=True, index=True ), + Column( "role_id", Integer, ForeignKey( "role.id" ), index=True ) ) + FormValues.table = Table('form_values', metadata, Column( "id", Integer, primary_key=True), Column( "create_time", DateTime, default=now ), @@ -923,6 +931,13 @@ primaryjoin=( RequestType.table.c.sample_form_id == FormDefinition.table.c.id ) ), ) ) +assign_mapper( context, RequestTypePermissions, RequestTypePermissions.table, + properties=dict( + request_type=relation( RequestType, backref="actions" ), + role=relation( Role, backref="request_type_actions" ) + ) +) + assign_mapper( context, FormDefinition, FormDefinition.table, properties=dict( current=relation( FormDefinitionCurrent, primaryjoin=( FormDefinition.table.c.form_definition_current_id == FormDefinitionCurrent.table.c.id ) ) diff -r 1b30f5fa152b -r c37de7a983e7 lib/galaxy/model/migrate/versions/0045_request_type_permissions_table.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/galaxy/model/migrate/versions/0045_request_type_permissions_table.py Thu Apr 22 21:11:17 2010 -0400 @@ -0,0 +1,34 @@ +""" +Migration script to add the request_type_permissions table. +""" + +from sqlalchemy import * +from migrate import * +from migrate.changeset import * + +import datetime +now = datetime.datetime.utcnow + +import logging +log = logging.getLogger( __name__ ) + +metadata = MetaData( migrate_engine ) + +RequestTypePermissions_table = Table( "request_type_permissions", metadata, + Column( "id", Integer, primary_key=True ), + Column( "create_time", DateTime, default=now ), + Column( "update_time", DateTime, default=now, onupdate=now ), + Column( "action", TEXT ), + Column( "request_type_id", Integer, ForeignKey( "request_type.id" ), nullable=True, index=True ), + Column( "role_id", Integer, ForeignKey( "role.id" ), index=True ) ) + +def upgrade(): + print __doc__ + metadata.reflect() + try: + RequestTypePermissions_table.create() + except Exception, e: + log.debug( "Creating request_type_permissions table failed: %s" % str( e ) ) + +def downgrade(): + pass \ No newline at end of file diff -r 1b30f5fa152b -r c37de7a983e7 lib/galaxy/security/__init__.py --- a/lib/galaxy/security/__init__.py Thu Apr 22 16:05:08 2010 -0400 +++ b/lib/galaxy/security/__init__.py Thu Apr 22 21:11:17 2010 -0400 @@ -24,7 +24,10 @@ LIBRARY_ACCESS = Action( "access library", "Restrict access to this library to only role members", "restrict" ), LIBRARY_ADD = Action( "add library item", "Role members can add library items to this library item", "grant" ), LIBRARY_MODIFY = Action( "modify library item", "Role members can modify this library item", "grant" ), - LIBRARY_MANAGE = Action( "manage library permissions", "Role members can manage roles associated with permissions on this library item", "grant" ) + LIBRARY_MANAGE = Action( "manage library permissions", "Role members can manage roles associated with permissions on this library item", "grant" ), + # Request type permissions + REQUEST_TYPE_ACCESS = Action( "access request_type", "Restrict access to this request_type to only role members", "restrict" ) + ) def get_action( self, name, default=None ): """Get a permitted action by its dict key or action name""" @@ -754,6 +757,39 @@ else: hidden_folder_ids = '%d' % sub_folder.id return False, hidden_folder_ids + # + # RequestType Permissions + # + def can_access_request_type( self, roles, request_type ): + action = self.permitted_actions.REQUEST_TYPE_ACCESS + request_type_actions = [] + for permission in request_type.actions: + if permission.action == action.action: + request_type_actions.append(permission) + if not request_type_actions: + return action.model == 'restrict' + ret_val = False + for item_action in item_actions: + if item_action.role in roles: + ret_val = True + break + return ret_val + def set_request_type_permissions( self, request_type, permissions={} ): + # Set new permissions on request_type, eliminating all current permissions + for role_assoc in request_type.actions: + self.sa_session.delete( role_assoc ) + # Add the new permissions on request_type + item_class = self.model.RequestType + permission_class = self.model.RequestTypePermissions + for action, roles in permissions.items(): + if isinstance( action, Action ): + action = action.action + for role_assoc in [ permission_class( action, request_type, role ) for role in roles ]: + self.sa_session.add( role_assoc ) + self.sa_session.flush() + + + class HostAgent( RBACAgent ): """ diff -r 1b30f5fa152b -r c37de7a983e7 lib/galaxy/web/controllers/requests.py --- a/lib/galaxy/web/controllers/requests.py Thu Apr 22 16:05:08 2010 -0400 +++ b/lib/galaxy/web/controllers/requests.py Thu Apr 22 21:11:17 2010 -0400 @@ -623,8 +623,7 @@ details=details, edit_mode=edit_mode) def __select_request_type(self, trans, rtid): - requesttype_list = trans.sa_session.query( trans.app.model.RequestType )\ - .order_by( trans.app.model.RequestType.name.asc() ) + requesttype_list = trans.user.accessible_request_types(trans) rt_ids = ['none'] for rt in requesttype_list: if not rt.deleted: diff -r 1b30f5fa152b -r c37de7a983e7 lib/galaxy/web/controllers/requests_admin.py --- a/lib/galaxy/web/controllers/requests_admin.py Thu Apr 22 16:05:08 2010 -0400 +++ b/lib/galaxy/web/controllers/requests_admin.py Thu Apr 22 21:11:17 2010 -0400 @@ -195,7 +195,8 @@ visible=False, filterable="standard" ) ) operations = [ - #grids.GridOperation( "Update", allow_multiple=False, condition=( lambda item: not item.deleted ) ), + grids.GridOperation( "Permissions", allow_multiple=False, condition=( lambda item: not item.deleted ) ), + #grids.GridOperation( "Clone", allow_multiple=False, condition=( lambda item: not item.deleted ) ), grids.GridOperation( "Delete", allow_multiple=True, condition=( lambda item: not item.deleted ) ), grids.GridOperation( "Undelete", condition=( lambda item: item.deleted ) ), ] @@ -258,6 +259,7 @@ ''' List all request made by the current user ''' + #self.__sample_datasets(trans, **kwd) if 'operation' in kwd: operation = kwd['operation'].lower() if not kwd.get( 'id', None ): @@ -534,8 +536,7 @@ #---- Request Creation ---------------------------------------------------------- # def __select_request_type(self, trans, rtid): - requesttype_list = trans.sa_session.query( trans.app.model.RequestType )\ - .order_by( trans.app.model.RequestType.name.asc() ) + requesttype_list = trans.user.accessible_request_types(trans) rt_ids = ['none'] for rt in requesttype_list: if not rt.deleted: @@ -1771,6 +1772,25 @@ dataset_index=dataset_index, message=message, status=status) + +# def __sample_datasets(self, trans, **kwd): +# samples = trans.sa_session.query( trans.app.model.Sample ).all() +# for s in samples: +# if s.dataset_files: +# newdf = [] +# for df in s.dataset_files: +# if type(s.dataset_files[0]) == type([1,2]): +# filepath = df[0] +# status = df[1] +# newdf.append(dict(filepath=filepath, +# status=status, +# name=filepath.split('/')[-1], +# error_msg='', +# size='Unknown')) +# s.dataset_files = newdf +# trans.sa_session.add( s ) +# trans.sa_session.flush() +# ## #### Request Type Stuff ################################################### ## @@ -1792,8 +1812,10 @@ return self.__delete_request_type( trans, **kwd ) elif operation == "undelete": return self.__undelete_request_type( trans, **kwd ) -# elif operation == "update": -# return self.__edit_request( trans, **kwd ) + elif operation == "clone": + return self.__clone_request_type( trans, **kwd ) + elif operation == "permissions": + return self.__show_request_type_permissions( trans, **kwd ) # Render the grid view return self.requesttype_grid( trans, **kwd ) def __view_request_type(self, trans, **kwd): @@ -1992,3 +2014,30 @@ action='manage_request_types', message='%i request type(s) has been undeleted' % len(id_list), status='done') ) + def __show_request_type_permissions(self, trans, **kwd ): + params = util.Params( kwd ) + message = util.restore_text( params.get( 'message', '' ) ) + status = params.get( 'status', 'done' ) + try: + rt = trans.sa_session.query( trans.app.model.RequestType ).get( trans.security.decode_id(kwd['id']) ) + except: + return trans.response.send_redirect( web.url_for( controller='requests_admin', + action='manage_request_types', + status='error', + message="Invalid requesttype ID") ) + roles = 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 ) + if params.get( 'update_roles_button', False ): + permissions = {} + for k, v in trans.app.model.RequestType.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_request_type_permissions( rt, permissions ) + trans.sa_session.refresh( rt ) + message = "Permissions updated for request type '%s'" % rt.name + return trans.fill_template( '/admin/requests/request_type_permissions.mako', + request_type=rt, + roles=roles, + status=status, + message=message) diff -r 1b30f5fa152b -r c37de7a983e7 templates/admin/requests/request_type_permissions.mako --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/templates/admin/requests/request_type_permissions.mako Thu Apr 22 21:11:17 2010 -0400 @@ -0,0 +1,92 @@ +<%inherit file="/base.mako"/> +<%namespace file="/message.mako" import="render_msg" /> + + +%if message: + ${render_msg( message, status )} +%endif + +<script type="text/javascript"> + $( document ).ready( function () { + $( '.role_add_button' ).click( function() { + var action = this.id.substring( 0, this.id.lastIndexOf( '_add_button' ) ) + var in_select = '#' + action + '_in_select'; + var out_select = '#' + action + '_out_select'; + return !$( out_select + ' option:selected' ).remove().appendTo( in_select ); + }); + $( '.role_remove_button' ).click( function() { + var action = this.id.substring( 0, this.id.lastIndexOf( '_remove_button' ) ) + var in_select = '#' + action + '_in_select'; + var out_select = '#' + action + '_out_select'; + return !$( in_select + ' option:selected' ).remove().appendTo( out_select ); + }); + $( 'form#edit_role_associations' ).submit( function() { + $( '.in_select option' ).each(function( i ) { + $( this ).attr( "selected", "selected" ); + }); + }); + }); +</script> + + +<div class="toolForm"> + <div class="toolFormTitle">Manage permissions on "${request_type.name}"</div> + <div class="toolFormBody"> + <form name="request_type_permissions" id="request_type_permissions" action="${h.url_for( controller='requests_admin', action='manage_request_types', operation="permissions", id=trans.security.encode_id(request_type.id))}" method="post"> + <div class="form-row"> +## %for k, v in permitted_actions: +## %if k not in do_not_render: +## <div class="form-row"> +## ${render_select( current_actions, k, v, all_roles )} +## </div> +## %endif +## %endfor +## <%def name="render_select( current_actions, action_key, action, all_roles )"> + <% + obj_name = request_type.name + current_actions = request_type.actions + permitted_actions = trans.app.model.RequestType.permitted_actions.items() + action = trans.app.model.RequestType.permitted_actions.REQUEST_TYPE_ACCESS + obj_str = 'request_type %s' % obj_name + obj_type = 'request_type' + all_roles = roles + action_key = 'REQUEST_TYPE_ACCESS' + + import sets + in_roles = sets.Set() + for a in current_actions: + if a.action == action.action: + in_roles.add( a.role ) + out_roles = filter( lambda x: x not in in_roles, all_roles ) + %> + <p> + <b>${action.action}:</b> ${action.description} + </p> + <div style="width: 100%; white-space: nowrap;"> + <div style="float: left; width: 50%;"> + Roles associated:<br/> + <select name="${action_key}_in" id="${action_key}_in_select" class="in_select" style="max-width: 98%; width: 98%; height: 150px; font-size: 100%;" multiple> + %for role in in_roles: + <option value="${role.id}">${role.name}</option> + %endfor + </select> <br/> + <div style="width: 98%; text-align: right"><input type="submit" id="${action_key}_remove_button" class="role_remove_button" value=">>"/></div> + </div> + <div style="width: 50%;"> + Roles not associated:<br/> + <select name="${action_key}_out" id="${action_key}_out_select" style="max-width: 98%; width: 98%; height: 150px; font-size: 100%;" multiple> + %for role in out_roles: + <option value="${role.id}">${role.name}</option> + %endfor + </select> <br/> + <input type="submit" id="${action_key}_add_button" class="role_add_button" value="<<"/> + </div> + </div> +## </%def> + </div> + <div class="form-row"> + <input type="submit" name="update_roles_button" value="Save"/> + </div> + </form> + </div> +</div> \ No newline at end of file diff -r 1b30f5fa152b -r c37de7a983e7 templates/webapps/galaxy/base_panels.mako --- a/templates/webapps/galaxy/base_panels.mako Thu Apr 22 16:05:08 2010 -0400 +++ b/templates/webapps/galaxy/base_panels.mako Thu Apr 22 21:11:17 2010 -0400 @@ -36,7 +36,7 @@ ${tab( "libraries", "Data Libraries", h.url_for( controller='/library', action='index' ))} %endif - %if trans.user and trans.request_types(): + %if trans.user and trans.user.accessible_request_types(trans): <td class="tab"> <a>Lab</a> <div class="submenu">