Simplified security model
Hello, Continuing the security discussion, here's a suggestion for a simplified security model: 1. no anonymous users (login required) 2. no roles, or groups. 3. no public data. 4. if a user has the dataset in one of his histories, he can do anything with it (including sharing it). 5. if a user doesn't have the dataset in one of his histories, he can't access it. 6. Administrators can access everything. This model fits a small lab (I hope), and assumes the following: 1. no security administration what-so-ever. 2. each user can access only his own data. nothing is public. 3. users are reasonably responsible: if they decide to share their files with somebody, they know what they are doing. 4. If users share history with somebody else, they don't have control over it any more (the other user can share it with more users) - so they should exercise caution before sharing. note: 'libraries' are not really incorporated into this model. Below is the patch with the new code. A new class is added to ./lib/galaxy/security/__init__.py, which does the permission checking. The added 'app' parameter is needed to access the config object and find out who the administrators are. Comments are welcomed, -Gordon. ================================== diff -r 46bd94b12a0c lib/galaxy/app.py --- a/lib/galaxy/app.py Thu Jul 16 15:35:58 2009 -0400 +++ b/lib/galaxy/app.py Wed Jul 22 20:40:56 2009 -0400 @@ -28,7 +28,8 @@ create_or_verify_database( db_url, self.config.database_engine_options ) # Setup the database engine and ORM from galaxy.model import mapping - self.model = mapping.init( self.config.file_path, + self.model = mapping.init( self, + self.config.file_path, db_url, self.config.database_engine_options ) # Security helper diff -r 46bd94b12a0c lib/galaxy/model/mapping.py --- a/lib/galaxy/model/mapping.py Thu Jul 16 15:35:58 2009 -0400 +++ b/lib/galaxy/model/mapping.py Wed Jul 22 20:40:56 2009 -0400 @@ -14,6 +14,7 @@ from galaxy.model.custom_types import * from galaxy.util.bunch import Bunch from galaxy.security import GalaxyRBACAgent +from galaxy.security import GalaxyRBACAgent_Gordon metadata = MetaData() context = Session = scoped_session( sessionmaker( autoflush=False, transactional=False ) ) @@ -1046,7 +1047,7 @@ # Let this go, it could possibly work with db's we don't support log.error( "database_connection contains an unknown SQLAlchemy database dialect: %s" % dialect ) -def init( file_path, url, engine_options={}, create_tables=False ): +def init( app, file_path, url, engine_options={}, create_tables=False ): """Connect mappings to the database""" # Connect dataset to the file path Dataset.file_path = file_path @@ -1072,7 +1073,7 @@ result.context = Session result.create_tables = create_tables #load local galaxy security policy - result.security_agent = GalaxyRBACAgent( result ) + result.security_agent = GalaxyRBACAgent_Gordon( app, result ) return result def get_suite(): diff -r 46bd94b12a0c lib/galaxy/security/__init__.py --- a/lib/galaxy/security/__init__.py Thu Jul 16 15:35:58 2009 -0400 +++ b/lib/galaxy/security/__init__.py Wed Jul 22 20:40:56 2009 -0400 @@ -5,6 +5,11 @@ import logging from galaxy.util.bunch import Bunch from galaxy.model.orm import * +# gordon: these are needed for new security model +import sys +import sqlalchemy as sa +import galaxy.model +import galaxy.web log = logging.getLogger(__name__) @@ -484,6 +489,122 @@ else: raise 'Passed an illegal object to check_folder_contents: %s' % type( entry ) + +#Hack by gordon@cshl.edu: +# new, simplified security model +# +# There are no public datasets. +# If the user has a dataset in one of his histories, he can do everything to it (including sharing) +# If the user doesn't have the dataset in onw of his histories, the dataset is blocked. +# +# roles and groups are ignored. +class GalaxyRBACAgent_Gordon( RBACAgent ): + def __init__( self, app, model, permitted_actions=None ): + self.model = model + self.app = app + if permitted_actions: + self.permitted_actions = permitted_actions + def allow_action( self, user, action, **kwd ): + if 'dataset' in kwd: + return self.allow_dataset_action( user, action, kwd[ 'dataset' ] ) + raise 'No valid method of checking action (%s) for user %s using kwd %s' % ( action, str( user ), str( kwd ) ) + def allow_dataset_action( self, user, action, dataset ): + """Returns true when user has permission to perform an action""" + # This security model requires user to be logged on. no user - no access! + if not user: + return False + # Administrators can access everything (that's why they're administators) + if self.app.config.is_admin_user(user): + return True + if not isinstance( dataset, self.model.Dataset ): + dataset = dataset.dataset + """ + Perform the equivalent of the following SQL query: + select 'yes' from + history_dataset_association, + history + where + history_dataset_association.dataset_id = [dataset.id] + and + history_dataset_association.history_id = history.id + and + history.user_id = [user.id] + If we get a 'yes' - then this user has this dataset in one of his histories, + and access is allowed. + """ + q = sa.select ( ( galaxy.model.HistoryDatasetAssociation.table.c.id, galaxy.model.History.c.user_id ), + from_obj = [ galaxy.model.HistoryDatasetAssociation.table.join ( + galaxy.model.History.table, + galaxy.model.HistoryDatasetAssociation.history_id == galaxy.model.History.id ) ], + whereclause = ( and_( + galaxy.model.HistoryDatasetAssociation.dataset_id == dataset.id, + galaxy.model.History.user_id == user.id )) + ) + #Surely there's a better way to check if we got results or not... + found = False + for row in q.execute(): + found = True + break + #sys.stderr.write( "!!! user_id = %d\nhda_id = %d\n" % (user.id, dataset.id) ) + #sys.stderr.write( "!!! Query = %s\nFound = %s\n" % (str(q), str(found)) ) + # if the user has this dataset in one of his histories - allow him access + if found: + return True + return False + def allow_library_item_action( self, user, action, library_item ): + return False + def guess_derived_permissions_for_datasets( self, datasets=[] ): + perms = {} + return perms + def associate_components( self, **kwd ): + raise 'No valid method of associating provided components: %s' % kwd + def create_private_user_role( self, user ): + return None + def get_private_user_role( self, user, auto_create=False ): + return None + def user_set_default_permissions( self, user, permissions={}, history=False, dataset=False, bypass_manage_permission=False ): + return None + def user_get_default_permissions( self, user ): + permissions = {} + return permissions + def history_set_default_permissions( self, history, permissions={}, dataset=False, bypass_manage_permission=False ): + return + def history_get_default_permissions( self, history ): + permissions = {} + return permissions + def set_all_dataset_permissions( self, dataset, permissions={} ): + return + def set_dataset_permission( self, dataset, permission={} ): + return + def dataset_is_public( self, dataset ): + return True + def make_dataset_public( self, dataset ): + return + def get_dataset_permissions( self, dataset ): + permissions = {} + return permissions + def copy_dataset_permissions( self, src, dst ): + return + def privately_share_dataset( self, dataset, users = [] ): + return + def set_all_library_permissions( self, library_item, permissions={} ): + return + def get_library_dataset_permissions( self, library_dataset ): + permissions = {} + return permissions + def copy_library_permissions( self, source_library_item, target_library_item, user=None ): + return + def show_library_item( self, user, library_item ): + return True + def set_entity_user_associations( self, users=[], roles=[], groups=[], delete_existing_assocs=True ): + return + def set_entity_group_associations( self, groups=[], users=[], roles=[], delete_existing_assocs=True ): + return + def set_entity_role_associations( self, roles=[], users=[], groups=[], delete_existing_assocs=True ): + return + def check_folder_contents( self, user, entry ): + return True + def get_permitted_actions( filter=None ): '''Utility method to return a subset of RBACAgent's permitted actions''' if filter is None:
participants (1)
-
Assaf Gordon