Merged local head with head from central

This commit is contained in:
Nate Coraor
2008-08-19 10:54:19 -04:00
49 changed files with 3301 additions and 432 deletions
+3
View File
@@ -4,6 +4,7 @@ from galaxy import config, jobs, util, tools, web
import galaxy.model
import galaxy.model.mapping
import galaxy.datatypes.registry
import galaxy.security
class UniverseApplication( object ):
"""Encapsulates the state of a Universe application"""
@@ -30,6 +31,8 @@ class UniverseApplication( object ):
self.toolbox = tools.ToolBox( self.config.tool_config, self.config.tool_path, self )
#Load datatype converters
self.datatypes_registry.load_datatype_converters( self.toolbox )
#Load security policy
self.security_agent = self.model.security_agent
# Start the job queue
job_dispatcher = jobs.DefaultJobDispatcher( self )
self.job_queue = jobs.JobQueue( self, job_dispatcher )
+1 -1
View File
@@ -45,7 +45,7 @@ class Configuration( object ):
self.job_scheduler_policy = kwargs.get("job_scheduler_policy", "FIFO")
self.job_queue_cleanup_interval = int( kwargs.get("job_queue_cleanup_interval", "5") )
self.job_working_directory = resolve_path( kwargs.get( "job_working_directory", "database/job_working_directory" ), self.root )
self.admin_pass = kwargs.get('admin_pass',"galaxy")
self.admin_users = kwargs.get( "admin_users", "" )
self.sendmail_path = kwargs.get('sendmail_path',"/usr/sbin/sendmail")
self.mailing_join_addr = kwargs.get('mailing_join_addr',"galaxy-user-join@bx.psu.edu")
self.error_email_to = kwargs.get( 'error_email_to', None )
+2 -2
View File
@@ -110,7 +110,7 @@ class Gmaj( data.Data ):
"nobutton": "false",
"urlpause" :"100",
"debug": "false",
"posturl": "history_add_to?%s" % urlencode( { 'history_id': dataset.history_id, 'ext': 'maf', 'name': 'GMAJ Output on data %s' % dataset.hid, 'info': 'Added by GMAJ', 'dbkey': dataset.dbkey } )
"posturl": "history_add_to?%s" % urlencode( { 'history_id': dataset.history_id, 'ext': 'maf', 'name': 'GMAJ Output on data %s' % dataset.hid, 'info': 'Added by GMAJ', 'dbkey': dataset.dbkey, 'copy_access_from': dataset.id } )
}
class_name = "edu.psu.bx.gmaj.MajApplet.class"
archive = "/static/gmaj/gmaj.jar"
@@ -180,7 +180,7 @@ class Laj( data.Text ):
"alignfile1": "display?id=%s" % dataset.id,
"buttonlabel": "Launch LAJ",
"title": "LAJ in Galaxy",
"posturl": "history_add_to?%s" % urlencode( { 'history_id': dataset.history_id, 'ext': 'lav', 'name': 'LAJ Output', 'info': 'Added by LAJ', 'dbkey': dataset.dbkey } ),
"posturl": "history_add_to?%s" % urlencode( { 'history_id': dataset.history_id, 'ext': 'lav', 'name': 'LAJ Output', 'info': 'Added by LAJ', 'dbkey': dataset.dbkey, 'copy_access_from': dataset.id } ),
"noseq": "true"
}
class_name = "edu.psu.cse.bio.laj.LajApplet.class"
+412 -244
View File
@@ -13,6 +13,7 @@ from galaxy import util
import tempfile
import galaxy.datatypes.registry
from galaxy.datatypes.metadata import MetadataCollection
from galaxy.security import RBACAgent
import logging
log = logging.getLogger( __name__ )
@@ -33,13 +34,14 @@ class User( object ):
self.external = False
# Relationships
self.histories = []
def set_password_cleartext( self, cleartext ):
"""Set 'self.password' to the digest of 'cleartext'."""
self.password = sha.new( cleartext ).hexdigest()
def check_password( self, cleartext ):
"""Check if 'cleartext' matches 'self.password' when hashed."""
return self.password == sha.new( cleartext ).hexdigest()
class Job( object ):
"""
A job represents a request to run a tool given input datasets, tool
@@ -101,252 +103,69 @@ class JobToOutputDatasetAssociation( object ):
self.name = name
self.dataset = dataset
class HistoryDatasetAssociation( object ):
def __init__( self, id=None, hid=None, name=None, info=None, blurb=None, peek=None, extension=None,
dbkey=None, metadata=None, history=None, dataset=None, deleted=False, designation=None,
parent_id=None, copied_from_history_dataset_association = None, validation_errors=None, visible=True, create_dataset = False ):
self.name = name or "Unnamed dataset"
self.id = id
self.hid = hid
self.info = info
self.blurb = blurb
self.peek = peek
self.extension = extension
self.dbkey = dbkey
self.designation = designation
self._metadata = metadata or dict()
self.deleted = deleted
self.visible = visible
# Relationships
self.history = history
if not dataset and create_dataset:
dataset = Dataset()
dataset.flush()
class GroupDatasetAssociation( object ):
def __init__( self, group, dataset, permitted_actions=[] ):
if isinstance( group, GroupDatasetAssociation ) or \
isinstance( group, DefaultUserGroupAssociation ) or \
isinstance( group, DefaultHistoryGroupAssociation ):
group = group.group
self.group = group
if isinstance( dataset, HistoryDatasetAssociation ):
dataset = dataset.dataset
self.dataset = dataset
self.parent_id = parent_id
self.validation_errors = validation_errors
self.copied_from_history_dataset_association = copied_from_history_dataset_association
@property
def ext( self ):
return self.extension
@property
def states( self ):
return self.dataset.states
def get_dataset_state( self ):
return self.dataset.state
def set_dataset_state ( self, state ):
self.dataset.state = state
self.dataset.flush() #flush here, because hda.flush() won't flush the Dataset object
state = property( get_dataset_state, set_dataset_state )
def get_file_name( self ):
return self.dataset.get_file_name()
def set_file_name (self, filename):
return self.dataset.set_file_name( filename )
file_name = property( get_file_name, set_file_name )
@property
def extra_files_path( self ):
return self.dataset.extra_files_path
@property
def datatype( self ):
return datatypes_registry.get_datatype_by_extension( self.extension )
self.permitted_actions = permitted_actions
def add_permitted_action( self, action ):
if action not in self.permitted_actions:
return self.permitted_actions.append( action )
raise 'action (%s) already exists in permitted actions list (%s: %s).' % ( action, str( self.id ), str( self.permitted_actions ) )
def remove_permitted_action( self, action ):
return self.permitted_actions.remove( action )
def get_metadata( self ):
if not self._metadata:
self._metadata = dict()
return MetadataCollection( self, self.datatype.metadata_spec )
def set_metadata( self, bunch ):
# Needs to accept a MetadataCollection, a bunch, or a dict
self._metadata = dict( bunch.items() )
metadata = property( get_metadata, set_metadata )
class Group( object ):
public_id = None
permitted_actions = galaxy.security.get_permitted_actions( 'GROUP' )
def __init__( self, name = None, priority = 0 ):
self.name = name
self.priority = priority
@classmethod
def get_public_group( cls ):
return Group.get( cls.public_id )
@classmethod
def set_public_group( cls, group ):
# We store the id instead of the object, because of alchemy sessions
if isinstance( group, Group ):
group = group.id
cls.public_id = group
@classmethod
def guess_public_group( cls ):
# Retrieve from database and store public group id
group = Group.select_by( name='public' )[0]
cls.set_public_group( group )
"""
This provide backwards compatibility with using the old dbkey
field in the database. That field now maps to "old_dbkey" (see mapping.py).
"""
def get_dbkey( self ):
dbkey = self.metadata.dbkey
if not isinstance(dbkey, list): dbkey = [dbkey]
#if dbkey in [["?"], [None], []]: dbkey = [self.old_dbkey]
if dbkey in [[None], []]: return "?"
return dbkey[0]
def set_dbkey( self, value ):
if "dbkey" in self.datatype.metadata_spec:
if not isinstance(value, list):
self.metadata.dbkey = [value]
else:
self.metadata.dbkey = value
#if isinstance(value, list):
# self.old_dbkey = value[0]
#else:
# self.old_dbkey = value
dbkey = property( get_dbkey, set_dbkey )
def change_datatype( self, new_ext ):
self.clear_associated_files()
datatypes_registry.change_datatype( self, new_ext )
def get_size( self ):
"""Returns the size of the data on disk"""
return self.dataset.get_size()
def set_size( self ):
"""Returns the size of the data on disk"""
return self.dataset.set_size()
def has_data( self ):
"""Detects whether there is any data"""
return self.dataset.has_data()
def get_raw_data( self ):
"""Returns the full data. To stream it open the file_name and read/write as needed"""
return self.datatype.get_raw_data( self )
def write_from_stream( self, stream ):
"""Writes data from a stream"""
self.datatype.write_from_stream(self, stream)
def set_raw_data( self, data ):
"""Saves the data on the disc"""
self.datatype.set_raw_data(self, data)
def get_mime( self ):
"""Returns the mime type of the data"""
return datatypes_registry.get_mimetype_by_extension( self.extension.lower() )
def set_peek( self ):
return self.datatype.set_peek( self )
def init_meta( self, copy_from=None ):
return self.datatype.init_meta( self, copy_from=copy_from )
def set_meta( self, **kwd ):
self.clear_associated_files( metadata_safe = True )
return self.datatype.set_meta( self, **kwd )
def set_readonly_meta( self, **kwd ):
return self.datatype.set_readonly_meta( self, **kwd )
def missing_meta( self ):
return self.datatype.missing_meta( self )
def as_display_type( self, type, **kwd ):
return self.datatype.as_display_type( self, type, **kwd )
def display_peek( self ):
return self.datatype.display_peek( self )
def display_name( self ):
return self.datatype.display_name( self )
def display_info( self ):
return self.datatype.display_info( self )
def get_converted_files_by_type( self, file_type ):
valid = []
for assoc in self.implicitly_converted_datasets:
if not assoc.deleted and assoc.type == file_type:
valid.append( assoc.dataset )
return valid
def clear_associated_files( self, metadata_safe = False, purge = False ):
#metadata_safe = True means to only clear when assoc.metadata_safe == False
for assoc in self.implicitly_converted_datasets:
if not metadata_safe or not assoc.metadata_safe:
assoc.clear( purge = purge )
def get_child_by_designation(self, designation):
for child in self.children:
if child.designation == designation:
return child
return None
def get_converter_types(self):
return self.datatype.get_converter_types( self, datatypes_registry)
def copy( self, copy_children = False, parent_id = None ):
des = HistoryDatasetAssociation( hid=self.hid, name=self.name, info=self.info, blurb=self.blurb, peek=self.peek, extension=self.extension, dbkey=self.dbkey, metadata=self._metadata, dataset = self.dataset, visible=self.visible, deleted=self.deleted, parent_id=parent_id, copied_from_history_dataset_association = self )
des.flush()
if copy_children:
for child in self.children:
child_copy = child.copy( copy_children = copy_children, parent_id = des.id )
des.set_peek() #in some instances peek relies on dataset_id, i.e. gmaj.zip for viewing MAFs
des.flush()
return des
def add_validation_error( self, validation_error ):
self.validation_errors.append( validation_error )
def extend_validation_errors( self, validation_errors ):
self.validation_errors.extend(validation_errors)
def mark_deleted( self, include_children=True ):
self.deleted = True
if include_children:
for child in self.children:
child.mark_deleted()
class History( object ):
def __init__( self, id=None, name=None, user=None ):
self.id = id
self.name = name or "Unnamed history"
self.deleted = False
self.purged = False
self.genome_build = None
# Relationships
class UserGroupAssociation( object ):
def __init__( self, user, group ):
self.user = user
self.datasets = []
self.galaxy_sessions = []
def _next_hid( self ):
# TODO: override this with something in the database that ensures
# better integrity
if len( self.datasets ) == 0:
return 1
else:
last_hid = 0
for dataset in self.datasets:
if dataset.hid > last_hid:
last_hid = dataset.hid
return last_hid + 1
self.group = group
def add_galaxy_session( self, galaxy_session, association=None ):
if association is None:
self.galaxy_sessions.append( GalaxySessionToHistoryAssociation( galaxy_session, self ) )
else:
self.galaxy_sessions.append( association )
class DefaultUserGroupAssociation( object ):
def __init__( self, user, group, permitted_actions ):
if isinstance( group, GroupDatasetAssociation ) or \
isinstance( group, DefaultUserGroupAssociation ) or \
isinstance( group, DefaultHistoryGroupAssociation ):
group = group.group
self.user = user
self.group = group
self.permitted_actions = permitted_actions
def add_dataset( self, dataset, parent_id=None, genome_build=None, set_hid = True ):
if isinstance( dataset, Dataset ):
dataset = HistoryDatasetAssociation( dataset = dataset )
dataset.flush()
elif not isinstance( dataset, HistoryDatasetAssociation ):
raise TypeError, "You can only add Dataset and HistoryDatasetAssociation instances to a history."
if parent_id:
for data in self.datasets:
if data.id == parent_id:
dataset.hid = data.hid
break
else:
if set_hid: dataset.hid = self._next_hid()
else:
if set_hid: dataset.hid = self._next_hid()
dataset.history = self
if genome_build not in [None, '?']:
self.genome_build = genome_build
self.datasets.append( dataset )
def copy(self):
des = History()
des.flush()
des.name = self.name
des.user_id = self.user_id
for data in self.datasets:
new_data = data.copy( copy_children = True )
des.add_dataset( new_data )
new_data.flush()
des.hid_counter = self.hid_counter
des.flush()
return des
# class Query( object ):
# def __init__( self, name=None, state=None, tool_parameters=None, history=None ):
# self.name = name or "Unnamed query"
# self.state = state
# self.tool_parameters = tool_parameters
# # Relationships
# self.history = history
# self.datasets = []
class DefaultHistoryGroupAssociation( object ):
def __init__( self, history, group, permitted_actions ):
if isinstance( group, GroupDatasetAssociation ) or \
isinstance( group, DefaultUserGroupAssociation ) or \
isinstance( group, DefaultHistoryGroupAssociation ):
group = group.group
self.history = history
self.group = group
self.permitted_actions = permitted_actions
class Dataset( object ):
states = Bunch( NEW = 'new',
@@ -356,6 +175,7 @@ class Dataset( object ):
EMPTY = 'empty',
ERROR = 'error',
DISCARDED = 'discarded' )
permitted_actions = galaxy.security.get_permitted_actions( 'DATASET' )
file_path = "/tmp/"
engine = None
def __init__( self, id=None, state=None, external_filename=None, extra_files_path=None, file_size=None, purgable=True ):
@@ -440,8 +260,347 @@ class Dataset( object ):
except OSError, e:
log.critical('%s delete error %s' % (self.__class__.__name__, e))
class Old_Dataset( Dataset ):
pass
class DatasetInstance( object ):
"""A base class for all 'dataset instances', HDAs, LDAs, etc"""
states = Dataset.states
permitted_actions = Dataset.permitted_actions
def __init__( self, id=None, hid=None, name=None, info=None, blurb=None, peek=None, extension=None,
dbkey=None, metadata=None, history=None, dataset=None, deleted=False, designation=None,
parent_id=None, validation_errors=None, visible=True, create_dataset = False ):
self.name = name or "Unnamed dataset"
self.id = id
self.info = info
self.blurb = blurb
self.peek = peek
self.extension = extension
self.dbkey = dbkey
self.designation = designation
self._metadata = metadata or dict()
self.deleted = deleted
self.visible = visible
# Relationships
if not dataset and create_dataset:
dataset = Dataset()
dataset.flush()
self.dataset = dataset
self.parent_id = parent_id
self.validation_errors = validation_errors
@property
def ext( self ):
return self.extension
def get_dataset_state( self ):
return self.dataset.state
def set_dataset_state ( self, state ):
self.dataset.state = state
self.dataset.flush() #flush here, because hda.flush() won't flush the Dataset object
state = property( get_dataset_state, set_dataset_state )
def get_file_name( self ):
return self.dataset.get_file_name()
def set_file_name (self, filename):
return self.dataset.set_file_name( filename )
file_name = property( get_file_name, set_file_name )
@property
def extra_files_path( self ):
return self.dataset.extra_files_path
@property
def datatype( self ):
return datatypes_registry.get_datatype_by_extension( self.extension )
def get_metadata( self ):
if not self._metadata:
self._metadata = dict()
return MetadataCollection( self, self.datatype.metadata_spec )
def set_metadata( self, bunch ):
# Needs to accept a MetadataCollection, a bunch, or a dict
self._metadata = dict( bunch.items() )
metadata = property( get_metadata, set_metadata )
# This provide backwards compatibility with using the old dbkey
# field in the database. That field now maps to "old_dbkey" (see mapping.py).
def get_dbkey( self ):
dbkey = self.metadata.dbkey
if not isinstance(dbkey, list): dbkey = [dbkey]
#if dbkey in [["?"], [None], []]: dbkey = [self.old_dbkey]
if dbkey in [[None], []]: return "?"
return dbkey[0]
def set_dbkey( self, value ):
if "dbkey" in self.datatype.metadata_spec:
if not isinstance(value, list):
self.metadata.dbkey = [value]
else:
self.metadata.dbkey = value
#if isinstance(value, list):
# self.old_dbkey = value[0]
#else:
# self.old_dbkey = value
dbkey = property( get_dbkey, set_dbkey )
def change_datatype( self, new_ext ):
self.clear_associated_files()
datatypes_registry.change_datatype( self, new_ext )
def get_size( self ):
"""Returns the size of the data on disk"""
return self.dataset.get_size()
def set_size( self ):
"""Returns the size of the data on disk"""
return self.dataset.set_size()
def has_data( self ):
"""Detects whether there is any data"""
return self.dataset.has_data()
def get_raw_data( self ):
"""Returns the full data. To stream it open the file_name and read/write as needed"""
return self.datatype.get_raw_data( self )
def write_from_stream( self, stream ):
"""Writes data from a stream"""
self.datatype.write_from_stream(self, stream)
def set_raw_data( self, data ):
"""Saves the data on the disc"""
self.datatype.set_raw_data(self, data)
def get_mime( self ):
"""Returns the mime type of the data"""
return datatypes_registry.get_mimetype_by_extension( self.extension.lower() )
def set_peek( self ):
return self.datatype.set_peek( self )
def init_meta( self, copy_from=None ):
return self.datatype.init_meta( self, copy_from=copy_from )
def set_meta( self, **kwd ):
self.clear_associated_files( metadata_safe = True )
return self.datatype.set_meta( self, **kwd )
def set_readonly_meta( self, **kwd ):
return self.datatype.set_readonly_meta( self, **kwd )
def missing_meta( self ):
return self.datatype.missing_meta( self )
def as_display_type( self, type, **kwd ):
return self.datatype.as_display_type( self, type, **kwd )
def display_peek( self ):
return self.datatype.display_peek( self )
def display_name( self ):
return self.datatype.display_name( self )
def display_info( self ):
return self.datatype.display_info( self )
def get_converted_files_by_type( self, file_type ):
valid = []
for assoc in self.implicitly_converted_datasets:
if not assoc.deleted and assoc.type == file_type:
valid.append( assoc.dataset )
return valid
def clear_associated_files( self, metadata_safe = False, purge = False ):
raise 'Unimplemented'
def get_child_by_designation(self, designation):
for child in self.children:
if child.designation == designation:
return child
return None
def get_converter_types(self):
return self.datatype.get_converter_types( self, datatypes_registry)
def add_validation_error( self, validation_error ):
self.validation_errors.append( validation_error )
def extend_validation_errors( self, validation_errors ):
self.validation_errors.extend(validation_errors)
def mark_deleted( self, include_children=True ):
self.deleted = True
if include_children:
for child in self.children:
child.mark_deleted()
class HistoryDatasetAssociation( DatasetInstance ):
def __init__( self,
hid = None,
history = None,
copied_from_history_dataset_association = None,
copied_from_library_folder_dataset_association = None,
**kwd ):
DatasetInstance.__init__( self, **kwd )
self.hid = hid
# Relationships
self.history = history
self.copied_from_history_dataset_association = copied_from_history_dataset_association
self.copied_from_library_folder_dataset_association = copied_from_library_folder_dataset_association
def copy( self, copy_children = False, parent_id = None ):
des = HistoryDatasetAssociation( hid=self.hid,
name=self.name,
info=self.info,
blurb=self.blurb,
peek=self.peek,
extension=self.extension,
dbkey=self.dbkey,
metadata=self._metadata,
dataset = self.dataset,
visible=self.visible,
deleted=self.deleted,
parent_id=parent_id,
copied_from_history_dataset_association=self )
des.flush()
if copy_children:
for child in self.children:
child_copy = child.copy( copy_children = copy_children, parent_id = des.id )
des.set_peek() #in some instances peek relies on dataset_id, i.e. gmaj.zip for viewing MAFs
des.flush()
return des
def clear_associated_files( self, metadata_safe = False, purge = False ):
#metadata_safe = True means to only clear when assoc.metadata_safe == False
for assoc in self.implicitly_converted_datasets:
if not metadata_safe or not assoc.metadata_safe:
assoc.clear( purge = purge )
class History( object ):
def __init__( self, id=None, name=None, user=None ):
self.id = id
self.name = name or "Unnamed history"
self.deleted = False
self.purged = False
self.genome_build = None
# Relationships
self.user = user
self.datasets = []
self.galaxy_sessions = []
def _next_hid( self ):
# TODO: override this with something in the database that ensures
# better integrity
if len( self.datasets ) == 0:
return 1
else:
last_hid = 0
for dataset in self.datasets:
if dataset.hid > last_hid:
last_hid = dataset.hid
return last_hid + 1
def add_galaxy_session( self, galaxy_session, association=None ):
if association is None:
self.galaxy_sessions.append( GalaxySessionToHistoryAssociation( galaxy_session, self ) )
else:
self.galaxy_sessions.append( association )
def add_dataset( self, dataset, parent_id=None, genome_build=None, set_hid = True ):
if isinstance( dataset, Dataset ):
dataset = HistoryDatasetAssociation( dataset = dataset )
dataset.flush()
elif not isinstance( dataset, HistoryDatasetAssociation ):
raise TypeError, "You can only add Dataset and HistoryDatasetAssociation instances to a history."
if parent_id:
for data in self.datasets:
if data.id == parent_id:
dataset.hid = data.hid
break
else:
if set_hid: dataset.hid = self._next_hid()
else:
if set_hid: dataset.hid = self._next_hid()
dataset.history = self
if genome_build not in [None, '?']:
self.genome_build = genome_build
self.datasets.append( dataset )
def copy( self, target_user = None ):
if not target_user:
target_user = self.user
des = History( user = target_user )
des.flush()
des.name = self.name
for data in self.datasets:
new_data = data.copy( copy_children = True )
des.add_dataset( new_data )
new_data.flush()
des.hid_counter = self.hid_counter
des.flush()
return des
class Library( object ):
def __init__( self, name = None, description = None, root_folder = None ):
self.name = name or "Unnamed library"
self.description = description
self.root_folder = root_folder
class LibraryFolder( object ):
def __init__( self, name = None, description = None, item_count = 0, order_id = None ):
self.name = name or "Unnamed folder"
self.description = description
self.item_count = item_count
self.order_id = order_id
self.genome_build = None
def add_dataset( self, dataset, genome_build=None ):
dataset.folder_id = self.id
dataset.order_id = self.item_count
self.item_count += 1
if genome_build not in [None, '?']:
self.genome_build = genome_build
def add_folder( self, folder ):
folder.parent_id = self.id
folder.order_id = self.item_count
self.item_count += 1
class LibraryFolderDatasetAssociation( DatasetInstance ):
def __init__( self,
folder = None,
order_id = None,
copied_from_history_dataset_association = None,
copied_from_library_folder_dataset_association = None,
**kwd ):
DatasetInstance.__init__( self, **kwd )
self.folder = folder
self.order_id = order_id
self.copied_from_history_dataset_association = copied_from_history_dataset_association
self.copied_from_library_folder_dataset_association = copied_from_library_folder_dataset_association
def to_history_dataset_association( self, parent_id = None ):
des = HistoryDatasetAssociation( name=self.name,
info=self.info,
blurb=self.blurb,
peek=self.peek,
extension=self.extension,
dbkey=self.dbkey,
metadata=self._metadata,
dataset = self.dataset,
visible=self.visible,
deleted=self.deleted,
parent_id=parent_id,
copied_from_library_folder_dataset_association = self )
des.flush()
for child in self.children:
child_copy = child.to_history_dataset_association( parent_id = des.id )
des.set_peek() #in some instances peek relies on dataset_id, i.e. gmaj.zip for viewing MAFs
des.flush()
return des
def copy( self, copy_children = False, parent_id = None ):
des = LibraryFolderDatasetAssociation( name=self.name,
info=self.info,
blurb=self.blurb,
peek=self.peek,
extension=self.extension,
dbkey=self.dbkey,
metadata=self._metadata,
dataset = self.dataset,
visible=self.visible,
deleted=self.deleted,
parent_id=parent_id,
copied_from_library_folder_dataset_association = self )
des.flush()
if copy_children:
for child in self.children:
child_copy = child.copy( copy_children = copy_children, parent_id = des.id )
des.set_peek() #in some instances peek relies on dataset_id, i.e. gmaj.zip for viewing MAFs
des.flush()
return des
def clear_associated_files( self, metadata_safe = False, purge = False ):
return
class LibraryTag( object ):
def __init__( self, tag ):
self.tag = tag
class LibraryTagFolderAssociation( object ):
def __init__( self, tag, folder ):
self.tag = tag
self.folder = folder
class LibraryTagDatasetAssociation( object ):
def __init__( self, tag, dataset ):
self.tag = tag
self.dataset = dataset
# class Query( object ):
# def __init__( self, name=None, state=None, tool_parameters=None, history=None ):
# self.name = name or "Unnamed query"
# self.state = state
# self.tool_parameters = tool_parameters
# # Relationships
# self.history = history
# self.datasets = []
class ValidationError( object ):
def __init__( self, message=None, err_type=None, attributes=None ):
@@ -483,7 +642,16 @@ class Event( object ):
self.message = message
class GalaxySession( object ):
def __init__( self, id=None, user=None, remote_host=None, remote_addr=None, referer=None, current_history_id=None, session_key=None, is_valid=False, prev_session_id=None ):
def __init__( self,
id=None,
user=None,
remote_host=None,
remote_addr=None,
referer=None,
current_history_id=None,
session_key=None,
is_valid=False,
prev_session_id=None ):
self.id = id
self.user = user
self.remote_host = remote_host
+214 -2
View File
@@ -19,6 +19,7 @@ from sqlalchemy import *
from galaxy.model import *
from galaxy.model.custom_types import *
from galaxy.util.bunch import Bunch
from galaxy.security import GalaxyRBACAgent
metadata = DynamicMetaData( threadlocal=False )
context = SessionContext( create_session )
@@ -65,7 +66,6 @@ History.table = Table( "history", metadata,
# Column( "state", String( 64 ) ),
# Column( "tool_parameters", Pickle() ) )
HistoryDatasetAssociation.table = Table( "history_dataset_association", metadata,
Column( "id", Integer, primary_key=True ),
Column( "history_id", Integer, ForeignKey( "history.id" ), index=True ),
@@ -73,6 +73,7 @@ HistoryDatasetAssociation.table = Table( "history_dataset_association", metadata
Column( "create_time", DateTime, default=now ),
Column( "update_time", DateTime, default=now, onupdate=now ),
Column( "copied_from_history_dataset_association_id", Integer, ForeignKey( "history_dataset_association.id" ), nullable=True ),
Column( "copied_from_library_folder_dataset_association_id", Integer, ForeignKey( "library_folder_dataset_association.id" ), nullable=True ),
Column( "hid", Integer ),
Column( "name", TrimmedString( 255 ) ),
Column( "info", TrimmedString( 255 ) ),
@@ -114,6 +115,107 @@ ValidationError.table = Table( "validation_error", metadata,
Column( "err_type", TrimmedString( 64 ) ),
Column( "attributes", TEXT ) )
Group.table = Table( "galaxy_group", metadata,
Column( "id", Integer, primary_key=True ),
Column( "create_time", DateTime, default=now ),
Column( "update_time", DateTime, default=now, onupdate=now ),
Column( "name", TEXT ),
Column( "priority", Integer ),
Column( "deleted", Boolean, index=True, default=False ) )
UserGroupAssociation.table = Table( "user_group_association", metadata,
Column( "id", Integer, primary_key=True ),
Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ),
Column( "group_id", Integer, ForeignKey( "galaxy_group.id" ), index=True ),
Column( "create_time", DateTime, default=now ),
Column( "update_time", DateTime, default=now, onupdate=now ) )
GroupDatasetAssociation.table = Table( "group_dataset_association", metadata,
Column( "id", Integer, primary_key=True ),
Column( "group_id", Integer, ForeignKey( "galaxy_group.id" ), index=True ),
Column( "dataset_id", Integer, ForeignKey( "dataset.id" ), index=True ),
Column( "create_time", DateTime, default=now ),
Column( "update_time", DateTime, default=now, onupdate=now ),
Column( "permitted_actions", JSONType(), default=[] ) )
# The following table stores the permissions that are considered the defaults for new histories when they are created by a user
DefaultUserGroupAssociation.table = Table( "default_user_group_association", metadata,
Column( "id", Integer, primary_key=True ),
Column( "group_id", Integer, ForeignKey( "galaxy_group.id" ), index=True ),
Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ),
Column( "create_time", DateTime, default=now ),
Column( "update_time", DateTime, default=now, onupdate=now ),
Column( "permitted_actions", JSONType(), default=[] ) )
# The following table stores the default permissions assigned to histories for datasets
# that need permissions ( dataset permissions that cannot be determined based on ancestor )
DefaultHistoryGroupAssociation.table = Table( "default_history_group_association", metadata,
Column( "id", Integer, primary_key=True ),
Column( "group_id", Integer, ForeignKey( "galaxy_group.id" ), index=True ),
Column( "history_id", Integer, ForeignKey( "history.id" ), index=True ),
Column( "create_time", DateTime, default=now ),
Column( "update_time", DateTime, default=now, onupdate=now ),
Column( "permitted_actions", JSONType(), default=[] ) )
LibraryFolderDatasetAssociation.table = Table( "library_folder_dataset_association", metadata,
Column( "id", Integer, primary_key=True ),
Column( "dataset_id", Integer, ForeignKey( "dataset.id" ), index=True ),
Column( "folder_id", Integer, ForeignKey( "library_folder.id" ), index=True ),
Column( "order_id", Integer ),
Column( "create_time", DateTime, default=now ),
Column( "update_time", DateTime, default=now, onupdate=now ),
Column( "copied_from_history_dataset_association_id", Integer, ForeignKey( "history_dataset_association.id", use_alter=True, name='history_dataset_association_dataset_id_fkey' ), nullable=True ),
Column( "copied_from_library_folder_dataset_association_id", Integer, ForeignKey( "library_folder_dataset_association.id", use_alter=True, name='library_folder_dataset_association_id_fkey' ), nullable=True ),
Column( "name", TrimmedString( 255 ) ),
Column( "info", TrimmedString( 255 ) ),
Column( "blurb", TrimmedString( 255 ) ),
Column( "peek" , TEXT ),
Column( "extension", TrimmedString( 64 ) ),
Column( "metadata", MetadataType(), key="_metadata" ),
Column( "parent_id", Integer, ForeignKey( "library_folder_dataset_association.id" ), nullable=True ),
Column( "designation", TrimmedString( 255 ) ),
Column( "deleted", Boolean, index=True, default=False ),
Column( "visible", Boolean ) )
Library.table = Table( "library", metadata,
Column( "id", Integer, primary_key=True ),
Column( "root_folder_id", Integer, ForeignKey( "library_folder.id" ), index=True ),
Column( "create_time", DateTime, default=now ),
Column( "update_time", DateTime, default=now, onupdate=now ),
Column( "name", TEXT ),
Column( "description", TEXT ) )
LibraryFolder.table = Table( "library_folder", metadata,
Column( "id", Integer, primary_key=True ),
Column( "parent_id", Integer, ForeignKey( "library_folder.id" ), nullable = True, index=True ),
Column( "create_time", DateTime, default=now ),
Column( "update_time", DateTime, default=now, onupdate=now ),
Column( "name", TEXT ),
Column( "description", TEXT ),
Column( "order_id", Integer ),
Column( "item_count", Integer ),
Column( "genome_build", TrimmedString( 40 ) ) )
LibraryTag.table = Table( "library_tag", metadata,
Column( "id", Integer, primary_key=True ),
Column( "create_time", DateTime, default=now ),
Column( "update_time", DateTime, default=now, onupdate=now ),
Column( "text", TEXT ) )
LibraryTagFolderAssociation.table = Table( "library_tag_folder_association", metadata,
Column( "id", Integer, primary_key=True ),
Column( "folder_id", Integer, ForeignKey( "library_folder.id" ), index=True ),
Column( "tag_id", Integer, ForeignKey( "library_tag.id" ), index=True ),
Column( "create_time", DateTime, default=now ),
Column( "update_time", DateTime, default=now, onupdate=now ) )
LibraryTagDatasetAssociation.table = Table( "library_tag_dataset_association", metadata,
Column( "id", Integer, primary_key=True ),
Column( "dataset_id", Integer, ForeignKey( "library_folder_dataset_association.id" ), index=True ),
Column( "tag_id", Integer, ForeignKey( "library_tag.id" ), index=True ),
Column( "create_time", DateTime, default=now ),
Column( "update_time", DateTime, default=now, onupdate=now ) )
Job.table = Table( "job", metadata,
Column( "id", Integer, primary_key=True ),
Column( "create_time", DateTime, default=now ),
@@ -255,6 +357,10 @@ assign_mapper( context, HistoryDatasetAssociation, HistoryDatasetAssociation.tab
HistoryDatasetAssociation,
primaryjoin=( HistoryDatasetAssociation.table.c.copied_from_history_dataset_association_id == HistoryDatasetAssociation.table.c.id ),
backref=backref( "copied_from_history_dataset_association", primaryjoin=( HistoryDatasetAssociation.table.c.copied_from_history_dataset_association_id == HistoryDatasetAssociation.table.c.id ), remote_side=[HistoryDatasetAssociation.table.c.id] ) ),
copied_to_library_folder_dataset_associations=relation(
LibraryFolderDatasetAssociation,
primaryjoin=( HistoryDatasetAssociation.table.c.copied_from_library_folder_dataset_association_id == LibraryFolderDatasetAssociation.table.c.id ),
backref=backref( "copied_from_history_dataset_association", primaryjoin=( HistoryDatasetAssociation.table.c.copied_from_library_folder_dataset_association_id == LibraryFolderDatasetAssociation.table.c.id ), remote_side=[LibraryFolderDatasetAssociation.table.c.id] ) ),
implicitly_converted_datasets=relation(
ImplicitlyConvertedDatasetAssociation,
primaryjoin=( ImplicitlyConvertedDatasetAssociation.table.c.hda_parent_id == HistoryDatasetAssociation.table.c.id ) ),
@@ -268,7 +374,10 @@ assign_mapper( context, Dataset, Dataset.table,
properties=dict(
history_associations=relation(
HistoryDatasetAssociation,
primaryjoin=( Dataset.table.c.id == HistoryDatasetAssociation.table.c.dataset_id ) )
primaryjoin=( Dataset.table.c.id == HistoryDatasetAssociation.table.c.dataset_id ) ),
library_associations=relation(
LibraryFolderDatasetAssociation,
primaryjoin=( Dataset.table.c.id == LibraryFolderDatasetAssociation.table.c.dataset_id ) )
) )
@@ -298,6 +407,74 @@ assign_mapper( context, User, User.table,
collection_class=ordering_list( 'order_index' ) )
) )
assign_mapper( context, Group, Group.table,
properties=dict( users=relation( UserGroupAssociation ),
datasets=relation( GroupDatasetAssociation ) ) )
assign_mapper( context, UserGroupAssociation, UserGroupAssociation.table,
properties=dict( user=relation( User, backref = "groups" ),
group=relation( Group, backref = "users" ) ) )
assign_mapper( context, GroupDatasetAssociation, GroupDatasetAssociation.table,
properties=dict( dataset=relation( Dataset, backref = "groups" ),
group=relation( Group, backref = "datasets" ) ) )
assign_mapper( context, DefaultUserGroupAssociation, DefaultUserGroupAssociation.table,
properties=dict( user=relation( User, backref = "default_groups" ),
group=relation( Group ) ) )
assign_mapper( context, DefaultHistoryGroupAssociation, DefaultHistoryGroupAssociation.table,
properties=dict( history=relation( History, backref = "default_groups" ),
group=relation( Group ) ) )
assign_mapper( context, Library, Library.table,
properties=dict(
root_folder=relation( LibraryFolder,
backref = backref( "library_root" ) )
) )
assign_mapper( context, LibraryFolder, LibraryFolder.table,
properties=dict(
folders=relation(
LibraryFolder,
primaryjoin=( LibraryFolder.table.c.parent_id == LibraryFolder.table.c.id ),
backref=backref( "parent", primaryjoin=( LibraryFolder.table.c.parent_id == LibraryFolder.table.c.id ), remote_side=[LibraryFolder.table.c.id] ) ),
tags=relation(
LibraryTagFolderAssociation,
primaryjoin=( LibraryFolder.table.c.id == LibraryTagFolderAssociation.table.c.folder_id ),
backref=backref( "folders" ) )
) )
assign_mapper( context, LibraryFolderDatasetAssociation, LibraryFolderDatasetAssociation.table,
properties=dict(
dataset=relation( Dataset ),
folder=relation(
LibraryFolder,
backref=backref( "datasets" ) ),
copied_to_library_folder_dataset_associations=relation(
LibraryFolderDatasetAssociation,
primaryjoin=( LibraryFolderDatasetAssociation.table.c.copied_from_library_folder_dataset_association_id == LibraryFolderDatasetAssociation.table.c.id ),
backref=backref( "copied_from_library_folder_dataset_association", primaryjoin=( LibraryFolderDatasetAssociation.table.c.copied_from_library_folder_dataset_association_id == LibraryFolderDatasetAssociation.table.c.id ), remote_side=[LibraryFolderDatasetAssociation.table.c.id] ) ),
children=relation(
LibraryFolderDatasetAssociation,
primaryjoin=( LibraryFolderDatasetAssociation.table.c.parent_id == LibraryFolderDatasetAssociation.table.c.id ),
backref=backref( "parent", primaryjoin=( LibraryFolderDatasetAssociation.table.c.parent_id == LibraryFolderDatasetAssociation.table.c.id ), remote_side=[LibraryFolderDatasetAssociation.table.c.id] ) ),
tags=relation(
LibraryTagDatasetAssociation,
primaryjoin=( LibraryFolderDatasetAssociation.table.c.id == LibraryTagDatasetAssociation.table.c.dataset_id ),
backref=backref( "datasets" ) )
) )
assign_mapper( context, LibraryTag, LibraryTag.table )
assign_mapper( context, LibraryTagFolderAssociation, LibraryTagFolderAssociation.table,
properties=dict( tag=relation( LibraryTag ),
folder=relation( LibraryFolder ) ) )
assign_mapper( context, LibraryTagDatasetAssociation, LibraryTagDatasetAssociation.table,
properties=dict( tag=relation( LibraryTag ),
dataset=relation( LibraryFolderDatasetAssociation ) ) )
assign_mapper( context, JobToInputDatasetAssociation, JobToInputDatasetAssociation.table,
properties=dict( job=relation( Job ), dataset=relation( HistoryDatasetAssociation ) ) )
@@ -411,6 +588,41 @@ def init( file_path, url, engine_options={}, create_tables=False ):
result.flush = lambda *args, **kwargs: context.current.flush( *args, **kwargs )
result.context = context
result.create_tables = create_tables
#load local galaxy security policy
result.security_agent = GalaxyRBACAgent( result )
# TODO, Nate: The following may not work for our Galaxy instances because there are too
# many rows that need updating ( I think ) even though we have eliminated all of the
# Role stuff. Maybe we can test this to see how long it takes for about 1000 datasets.
# If we decide to use this approach rather than SQL commands to populate the tables,
# then this needs to be thoroughly tested to ensure the data is populated as expected
# (i.e., make sure naything that is public gets the public security settings, etc).
#
# Set up default table entries here, only exist for group access because
# permitted actions are exclusively restricted to the association between a group
# and a dataset
if result.Group.count() == 0:
log.warning( "There were no groups located, setting up default (public) group." )
# Create public group
public_group = result.security_agent.create_group( name='public' )
# Store public group id
result.security_agent.set_public_group( public_group )
# Loop through all histories and set up rbac on users, histories and datasets
for history in result.History.select( result.History.table.c.purged == False ):
if history.user:
if not history.user.default_groups:
result.security_agent.setup_new_user( history.user )
history.user.flush()
else:
result.security_agent.history_set_default_access( history, dataset=True )
history.flush()
# Add all datasets which aren't in a history to the public group
orphans = result.Dataset.get_by( history_id = None )
if orphans:
for dataset in orphans:
result.security_agent.set_dataset_groups( dataset, [ public_group ] )
else:
result.security_agent.guess_public_group()
log.debug( "Public Group identified as id = %s." % ( Group.public_id ) )
return result
def get_suite():
+245
View File
@@ -0,0 +1,245 @@
"""
Galaxy Security
"""
import logging
from galaxy.util.bunch import Bunch
log = logging.getLogger(__name__)
# TODO, Nate: Think about whether the following permitted actions are appropriate for the dataset and
# group objects. What should be the default "public" permitted actions? Make sure that the public group
# and public datasets are set with the correct permitted actions. Also make sure that "private" settings
# are correct when an authenticated user creates things inside their "private" environment.
class RBACAgent:
"""Class that handles galaxy security"""
permitted_actions = Bunch(
# The ability to edit the metadata of the associated dataset
DATASET_EDIT_METADATA = 'dataset_edit_metadata',
# The ability to change the permissions of a dataset (so specifically, to add and modify
# group_dataset_association rows where the dataset is the dataset for which the permission is set).
DATASET_MANAGE_PERMISSIONS = 'dataset_manage_permissions',
# The ability to perform any read only operation on the dataset (view, display at external site,
# use in a job, etc).
DATASET_ACCESS = 'dataset_access'
)
def allow_action( self, user, action, **kwd ):
raise 'No valid method of checking action (%s) on %s for user %s.' % ( action, kwd, user )
def guess_derived_groups_permitted_actions_for_datasets( self, datasets = [] ):
raise "Unimplemented Method"
def associate_components( self, **kwd ):
raise 'No valid method of associating provided components: %s' % kwd
def get_group( self, id ):
raise 'No valid method of retrieving group %s' % ( id )
def create_group( self, **kwd ):
raise 'No valid method of creating group with %s' % ( kwd )
def create_private_user_group( self, user ):
raise "Unimplemented Method"
def user_set_default_access( self, user, groups = None, history = False, dataset = False ):
raise "Unimplemented Method"
def setup_new_user( self, user ):
self.user_set_default_access( user, history = True, dataset = True )
self.associate_components( user=user, group=self.get_public_group() )
def history_set_default_access( self, history, groups=None, dataset=False ):
raise "Unimplemented Method"
def set_public_group( self, group ):
raise "Unimplemented Method"
def get_public_group( self ):
raise "Unimplemented Method"
def guess_public_group( self ):
raise "Unimplemented Method"
def set_dataset_groups( self, dataset, groups ):
raise "Unimplemented Method"
def set_dataset_permitted_actions( self, dataset ):
raise "Unimplemented Method"
def get_component_associations( self, **kwd ):
raise "Unimplemented Method"
def components_are_associated( self, **kwd ):
return bool( self.get_component_associations( **kwd ) )
class GalaxyRBACAgent( RBACAgent ):
def __init__( self, model, permitted_actions=None ):
self.model = model
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) on %s for user %s.' % ( action, kwd, user )
def allow_dataset_action( self, user, action, dataset ):
"""Returns true when user has permission to perform an action"""
if not isinstance( dataset, self.model.Dataset ):
dataset = dataset.dataset
# If dataset is in public group, we always return true for viewing and using
# This may need to change when the ability to alter groups and permitted_actions is allowed
if action == self.permitted_actions.DATASET_ACCESS and \
self.components_are_associated( group = self.get_public_group(), dataset = dataset ):
return True
elif user is not None:
# Loop through permitted_actions and if allowed return true:
# Check permitted_actions associated with dataset through groups
for group_dataset_assoc in dataset.groups:
if self.components_are_associated( user = user, group = group_dataset_assoc.group ):
if action in group_dataset_assoc.permitted_actions:
return True
return False # No user and dataset not in public group, or user lacks permission
def guess_derived_groups_for_datasets( self, datasets=[] ):
# TODO, Nate: Make sure this method is functionally correct.
"""Returns a list of groups for the output dataset based upon itself and provided datasets"""
access_groups = None
priority_access_group = None
for dataset in datasets:
# Determine access groups for output datasets - these groups are the
# intersection across all inputs. If we end up with no intersection
# between inputs, then we rely on priorities
if isinstance( dataset, self.model.HistoryDatasetAssociation ):
dataset = dataset.dataset
groups = [ data_group_assoc.group for data_group_assoc in dataset.groups ]
for group in groups:
if priority_access_group is None or priority_access_group.priority < group.priority:
priority_access_group = group
if access_groups is None:
access_groups = set( groups )
else:
access_groups.intersection_update( set( groups ) )
# Complete lists for output dataset access
if access_groups:
access_groups = list( access_groups)
else:
access_groups = []
# If we have no groups left after intersection, take the highest priority group
if not access_groups:
if priority_access_group:
access_groups = [ priority_access_group ]
return access_groups
def get_group( self, id ):
return self.model.Group.get( id )
raise 'No valid method of retrieving requested group %s' % ( id )
def create_group( self, **kwd ):
rval = self.model.Group( **kwd )
rval.flush()
return rval
raise 'No valid method of creating group with %s' % ( kwd )
def associate_components( self, **kwd ):
assert len( kwd ) == 2, 'You must specify exactly 2 Galaxy security components to associate.'
if 'dataset' in kwd:
if 'group' in kwd:
return self.associate_group_dataset( kwd['group'], kwd['dataset'] )
elif 'user' in kwd:
if 'group' in kwd:
return self.associate_user_group( kwd['user'], kwd['group'] )
raise 'No valid method of associating provided components: %s' % kwd
def disassociate_components( self, **kwd ):
assert len( kwd ) == 2, 'You must specify exactly 2 Galaxy security components to disassociate.'
if 'dataset' in kwd:
if 'group' in kwd:
return self.disassociate_group_dataset( kwd['group'], kwd['dataset'] )
raise 'No valid method of associating provided components: %s' % kwd
def associate_group_dataset( self, group, dataset, permitted_actions=[] ):
if not permitted_actions:
if isinstance( dataset.permitted_actions, Bunch ):
permitted_actions = dataset.permitted_actions.__dict__.values()
else:
permitted_actions = dataset.permitted_actions
assoc = self.model.GroupDatasetAssociation( group, dataset, permitted_actions )
assoc.flush()
return assoc
def disassociate_group_dataset( self, group, dataset ):
assoc = self.model.GroupDatasetAssociation.selectone_by( group_id = group.id, dataset_id = dataset.id )
assoc.delete()
assoc.flush()
def associate_user_group( self, user, group ):
assoc = self.model.UserGroupAssociation( user, group )
assoc.flush()
return assoc
def create_private_user_group( self, user ):
# Create private group
group_name = "%s private group" % user.email
group = self.model.Group( name=group_name, priority=10 )
group.flush()
# Add user to group
self.associate_components( group=group, user=user )
group.flush()
return group
def user_set_default_access( self, user, groups = None, history = False, dataset = False ):
# TODO, Nate: Make sure this method is functionally correct with permitted actions set appropriately.
if groups is None:
groups = [ self.create_private_user_group( user ) ]
if groups is not None:
for assoc in user.default_groups: #this is the association not the actual group
assoc.delete()
assoc.flush()
for group in groups:
if isinstance( group, self.model.Group ):
permitted_actions = group.permitted_actions.__dict__.values()
else:
permitted_actions = group.permitted_actions
assoc = self.model.DefaultUserGroupAssociation( user, group, permitted_actions )
assoc.flush()
if history:
for history in user.histories:
self.history_set_default_access( history, groups=groups, dataset=dataset )
def history_set_default_access( self, history, groups=None, dataset=False ):
# TODO, Nate: Make sure this method is functionally correct with permitted actions set appropriately.
if groups is None:
if history.user:
groups = [ assoc.group for assoc in history.user.default_groups ]
else:
groups = [ self.get_public_group() ]
if groups is not None:
for assoc in history.default_groups: #this is the association not the actual group
assoc.delete()
assoc.flush()
for group in groups:
if isinstance( group, self.model.Group ):
permitted_actions = group.permitted_actions.__dict__.values()
else:
permitted_actions = group.permitted_actions
assoc = self.model.DefaultHistoryGroupAssociation( history, group, permitted_actions )
assoc.flush()
if dataset:
for data in history.datasets:
for hda in data.dataset.history_associations:
if history.user and hda.history not in history.user.histories:
self.set_dataset_groups( data.dataset, [ self.get_public_group() ] )
break
else:
self.set_dataset_groups( data.dataset, groups )
def get_public_group( self ):
return self.model.Group.get_public_group()
def set_public_group( self, group ):
return self.model.Group.set_public_group( group )
def guess_public_group( self ):
return self.model.Group.guess_public_group()
def set_dataset_groups( self, dataset, groups ):
if isinstance( dataset, self.model.HistoryDatasetAssociation ):
dataset = dataset.dataset
for group_dataset_assoc in dataset.groups:
group_dataset_assoc.delete()
group_dataset_assoc.flush()
for group in groups:
if not isinstance( group, self.model.Group ):
group = group.group
self.associate_components( dataset=dataset, group=group )
def get_component_associations( self, **kwd ):
# TODO, Nate: Make sure this method is functionally correct.
assert len( kwd ) == 2, 'You must specify exactly 2 Galaxy security components to check for associations.'
if 'dataset' in kwd:
if 'group' in kwd:
return self.model.GroupDatasetAssociation.get_by( group_id = kwd['group'].id, dataset_id = kwd['dataset'].id )
elif 'user' in kwd:
if 'group' in kwd:
return self.model.UserGroupAssociation.get_by( group_id = kwd['group'].id, user_id = kwd['user'].id )
raise 'No valid method of associating provided components: %s' % kwd
def dataset_has_group( self, dataset_id, group_id ):
return bool( self.model.GroupDatasetAssociation.get_by( group_id = group_id, dataset_id = dataset_id ) )
def get_permitted_actions( self, filter=None ):
'''Utility method to return a subset of RBACAgent's permitted actions'''
if filter is None:
return RBACAgent.permitted_actions
if not filter.endswith('_'):
filter += '_'
tmp_bunch = Bunch()
[tmp_bunch.__dict__.__setitem__(k, v) for k, v in RBACAgent.permitted_actions.items() if k.startswith(filter)]
return tmp_bunch
+4
View File
@@ -1085,6 +1085,8 @@ class Tool:
else: visible = False
ext = fields.pop(0).lower()
child_dataset = self.app.model.HistoryDatasetAssociation( extension=ext, parent_id=outdata.id, designation=designation, visible=visible, dbkey=outdata.dbkey, create_dataset=True )
# TODO, Nate: Make sure the following is functionally correct.
self.app.security_agent.set_dataset_groups( child_dataset.dataset, outdata.dataset.groups )
# Move data from temp location to dataset location
shutil.move( filename, child_dataset.file_name )
child_dataset.flush()
@@ -1121,6 +1123,8 @@ class Tool:
ext = fields.pop(0).lower()
# Create new primary dataset
primary_data = self.app.model.HistoryDatasetAssociation( extension=ext, designation=designation, visible=visible, dbkey=outdata.dbkey, create_dataset=True )
# TODO, Nate: Make sure the following is functionally correct.
self.app.security_agent.set_dataset_groups( primary_data.dataset, outdata.dataset.groups )
primary_data.flush()
# Move data from temp location to dataset location
shutil.move( filename, primary_data.file_name )
+16
View File
@@ -43,6 +43,9 @@ class DefaultToolAction( object ):
assoc.flush()
data = new_data
break
# TODO, Nate: Make sure the permitted actions here are appropriate.
if data and not trans.app.security_agent.allow_action( trans.user, data.permitted_actions.DATASET_ACCESS, dataset=data ):
raise "User does not have permission to use a dataset (%s) provided for input." % data.id
return data
if isinstance( input, DataToolParameter ):
if isinstance( value, list ):
@@ -79,6 +82,15 @@ class DefaultToolAction( object ):
data = NoneDataset( datatypes_registry = trans.app.datatypes_registry )
if data.dbkey not in [None, '?']:
input_dbkey = data.dbkey
# Determine output dataset permitted_actions list
existing_datasets = [ inp for inp in inp_data.values() if inp ]
if existing_datasets:
output_access_groups = trans.app.security_agent.guess_derived_groups_for_datasets( existing_datasets )
else:
# No valid inputs, we will use history defaults
output_access_groups = [ group.group for group in trans.history.default_groups ]
# Build name for output datasets based on tool name and input names
if len( input_names ) == 1:
on_text = input_names[0]
@@ -120,6 +132,7 @@ class DefaultToolAction( object ):
data = trans.app.model.HistoryDatasetAssociation( extension=ext, create_dataset=True )
# Commit the dataset immediately so it gets database assigned unique id
data.flush()
trans.app.security_agent.set_dataset_groups( data.dataset, output_access_groups )
# Create an empty file immediately
open( data.file_name, "w" ).close()
# This may not be neccesary with the new parent/child associations
@@ -183,6 +196,9 @@ class DefaultToolAction( object ):
job.add_parameter( name, value )
for name, dataset in inp_data.iteritems():
if dataset:
# TODO, Nate: Make sure the permitted actions here are appropriate.
if not trans.app.security_agent.allow_action( trans.user, dataset.permitted_actions.DATASET_ACCESS, dataset=dataset ):
raise "User does not have permission to use a dataset (%s) provided for input." % data.id
job.add_input_dataset( name, dataset )
else:
job.add_input_dataset( name, None )
+8 -4
View File
@@ -65,8 +65,10 @@ class UploadToolAction( object ):
return dict( output=data_list[0] )
def upload_empty(self, trans, err_code, err_msg):
data = trans.app.model.HistoryDatasetAssociation( create_dataset = True )
data.name = err_code
data = trans.app.model.HistoryDatasetAssociation( create_dataset=True )
# TODO, Nate: Make sure the following is appropriate.
trans.app.security_agent.set_dataset_groups( data.dataset, trans.history.default_groups )
data.name = err_code
data.extension = "txt"
data.dbkey = "?"
data.info = err_msg
@@ -85,12 +87,12 @@ class UploadToolAction( object ):
if not os.path.getsize( temp_name ) > 0:
raise BadFileException( "you attempted to upload an empty file." )
# See if we have a gzipped file, which, if it passes our restrictions, we'll decompress on the fly.
# See if we have a gzipped file, which, if it passes our restrictions, we'll uncompress on the fly.
is_gzipped, is_valid = self.check_gzip( temp_name )
if is_gzipped and not is_valid:
raise BadFileException( "you attempted to upload an inappropriate file." )
elif is_gzipped and is_valid:
#We need to decompress the temp_name file
# We need to uncompress the temp_name file
CHUNK_SIZE = 2**20 # 1Mb
fd, uncompressed = tempfile.mkstemp()
gzipped_file = gzip.GzipFile( temp_name )
@@ -159,6 +161,8 @@ class UploadToolAction( object ):
info = 'uploaded %s file' %data_type
data = trans.app.model.HistoryDatasetAssociation( history = trans.history, extension = ext, create_dataset = True )
# TODO, Nate: Make sure the following is appropriate.
trans.app.security_agent.set_dataset_groups( data.dataset, trans.history.default_groups )
data.name = file_name
data.dbkey = dbkey
data.info = info
+28 -8
View File
@@ -972,6 +972,8 @@ class DrillDownSelectToolParameter( ToolParameter ):
class DataToolParameter( ToolParameter ):
# TODO, Nate: Make sure the following unit tests appropriately test the dataset security
# components. Add as many additional tests as necessary.
"""
Parameter that takes on one (or many) or a specific set of values.
@@ -979,19 +981,35 @@ class DataToolParameter( ToolParameter ):
displayed as radio buttons and multiple selects as a set of checkboxes
>>> # Mock up a history (not connected to database)
>>> from galaxy.model import History, HistoryDatasetAssociation
>>> from galaxy.model import History, HistoryDatasetAssociation, User, Group
>>> from galaxy.util.bunch import Bunch
>>> from galaxy.security import GalaxyRBACAgent
>>> import galaxy.model
>>> security_agent = GalaxyRBACAgent( galaxy.model )
>>> hist = History()
>>> hist.flush()
>>> hist.add_dataset( HistoryDatasetAssociation( id=1, extension='txt', create_dataset=True ) )
>>> hist.add_dataset( HistoryDatasetAssociation( id=2, extension='bed', create_dataset=True ) )
>>> hist.add_dataset( HistoryDatasetAssociation( id=3, extension='fasta', create_dataset=True ) )
>>> hist.add_dataset( HistoryDatasetAssociation( id=4, extension='png', create_dataset=True ) )
>>> hist.add_dataset( HistoryDatasetAssociation( id=5, extension='interval', create_dataset=True ) )
>>> group = Group( 'test' )
>>> group.flush()
>>> Group.public_id = group.id
>>> dataset1 = HistoryDatasetAssociation( id=1, extension='txt', create_dataset=True )
>>> security_agent.set_dataset_groups( dataset1, [ group ] )
>>> dataset2 = HistoryDatasetAssociation( id=2, extension='bed', create_dataset=True )
>>> security_agent.set_dataset_groups( dataset2, [ group ] )
>>> dataset3 = HistoryDatasetAssociation( id=3, extension='fasta', create_dataset=True )
>>> security_agent.set_dataset_groups( dataset3, [ group ] )
>>> dataset4 = HistoryDatasetAssociation( id=4, extension='png', create_dataset=True )
>>> security_agent.set_dataset_groups( dataset4, [ group ] )
>>> dataset5 = HistoryDatasetAssociation( id=5, extension='interval', create_dataset=True )
>>> security_agent.set_dataset_groups( dataset5, [ group ] )
>>> hist.add_dataset( dataset1 )
>>> hist.add_dataset( dataset2 )
>>> hist.add_dataset( dataset3 )
>>> hist.add_dataset( dataset4 )
>>> hist.add_dataset( dataset5 )
>>> p = DataToolParameter( None, XML( '<param name="blah" type="data" format="interval"/>' ) )
>>> print p.name
blah
>>> print p.get_html( trans=Bunch( history=hist ) )
>>> print p.get_html( trans=Bunch( history=hist, user=None, app=Bunch( security_agent = security_agent ) ) )
<select name="blah">
<option value="2">2: Unnamed dataset</option>
<option value="5" selected>5: Unnamed dataset</option>
@@ -1047,7 +1065,7 @@ class DataToolParameter( ToolParameter ):
hid = "%s.%d" % ( parent_hid, i + 1 )
else:
hid = str( data.hid )
if not data.deleted and data.state not in [data.states.ERROR] and data.visible:
if not data.deleted and data.state not in [data.states.ERROR] and data.visible and trans.app.security_agent.allow_action( trans.user, data.permitted_actions.DATASET_ACCESS, dataset = data ):
if self.options and data.get_dbkey() != filter_value:
continue
if isinstance( data.datatype, self.formats):
@@ -1061,6 +1079,8 @@ class DataToolParameter( ToolParameter ):
data = datasets[0]
elif not self.converter_safe( other_values, trans ):
continue
if not trans.app.security_agent.allow_action( trans.user, data.permitted_actions.DATASET_ACCESS, dataset = data ):
continue
selected = ( value and ( data in value ) )
field.add_option( "%s: (as %s) %s" % ( hid, target_ext, data.name[:30] ), data.id, selected )
break #we only report the first valid converter, assume self.extensions is a priority list
+786 -14
View File
@@ -1,24 +1,796 @@
import shutil, StringIO, operator
from galaxy import util
from galaxy.web.base.controller import *
import logging, sets, time
from galaxy.datatypes import sniff
from galaxy.security import RBACAgent
import galaxy.model
from xml.sax.saxutils import escape, unescape
import pkg_resources
pkg_resources.require( "sqlalchemy>=0.3" )
import sqlalchemy as sa
import logging
log = logging.getLogger( __name__ )
entities = { '@': 'FuNkYaT' }
unentities = { 'FuNkYaT' : '@' }
no_privilege_msg = "You must have Galaxy administrator privileges to use this feature."
class Admin( BaseController ):
def user_is_admin( self, trans ):
admin_users = trans.app.config.get( "admin_users", "" ).split( "," )
if not admin_users:
return False
user = trans.get_user()
if not user:
return False
if not user.email in admin_users:
return False
return True
@web.expose
def index( self, trans, **kwd ):
msg = ''
if 'action' in kwd:
if kwd['action'] == "tool_reload":
msg = self.tool_reload( **kwd )
return trans.fill_template( 'admin_main.mako', toolbox=self.app.toolbox, msg=msg )
def tool_reload( self, tool_version=None, **kwd ):
if not self.user_is_admin( trans ):
return trans.show_error_message( no_privilege_msg )
params = util.Params( kwd )
if params.passwd==self.app.config.admin_pass:
tool_id = params.tool_id
self.app.toolbox.reload( tool_id )
msg = 'Reloaded tool: ' + tool_id
msg = params.msg
return trans.fill_template( '/admin/index.mako', msg=msg )
@web.expose
def reload_tool( self, trans, **kwd ):
if not self.user_is_admin( trans ):
return trans.show_error_message( no_privilege_msg )
params = util.Params( kwd )
msg = params.msg
return trans.fill_template( '/admin/reload_tool.mako', toolbox=self.app.toolbox, msg=msg )
@web.expose
def tool_reload( self, trans, tool_version=None, **kwd ):
if not self.user_is_admin( trans ):
return trans.show_error_message( no_privilege_msg )
params = util.Params( kwd )
tool_id = params.tool_id
self.app.toolbox.reload( tool_id )
msg = 'Reloaded tool: ' + tool_id
return trans.fill_template( '/admin/reload_tool.mako', toolbox=self.app.toolbox, msg=msg )
@web.expose
def dataset_security( self, trans, **kwd ):
if not self.user_is_admin( trans ):
return trans.show_error_message( no_privilege_msg )
params = util.Params( kwd )
msg = params.msg
return trans.fill_template( '/admin/dataset_security/index.mako', msg=msg )
# Galaxy Group Stuff
@web.expose
def groups( self, trans, **kwd ):
if not self.user_is_admin( trans ):
return trans.show_error_message( no_privilege_msg )
params = util.Params( kwd )
msg = params.msg
# This query retrieves groups that are not deleted and members of each group
q = sa.select( ( ( galaxy.model.Group.table.c.id ).label( 'group_id' ),
( galaxy.model.Group.table.c.name ).label( 'group_name' ),
( galaxy.model.Group.table.c.priority ).label( 'group_priority' ),
sa.func.count( galaxy.model.User.table.c.id ).label( 'total_members' ) ),
whereclause = galaxy.model.Group.table.c.deleted == False,
from_obj = [ sa.outerjoin( galaxy.model.Group.table,
galaxy.model.UserGroupAssociation.table
).outerjoin( galaxy.model.User.table ) ],
group_by = [ galaxy.model.Group.table.c.id,
galaxy.model.Group.table.c.name,
galaxy.model.Group.table.c.priority ],
order_by = [ galaxy.model.Group.table.c.name ] )
groups = []
for row in q.execute():
# This 2nd query retrieves the number of datasets and dataset permitted_actions associated with each group
q2 = sa.select( ( ( galaxy.model.Group.table.c.id ).label( 'group_id' ),
( galaxy.model.GroupDatasetAssociation.table.c.permitted_actions ).label( 'permitted_actions' ),
sa.func.count( galaxy.model.Dataset.table.c.id ).label( 'total_datasets' ) ),
whereclause = galaxy.model.Group.table.c.id == row.group_id,
from_obj = [ sa.outerjoin( galaxy.model.Group.table,
galaxy.model.GroupDatasetAssociation.table
).outerjoin( galaxy.model.Dataset.table ) ],
group_by = [ galaxy.model.Group.table.c.id,
galaxy.model.GroupDatasetAssociation.table.c.permitted_actions ] )
for row2 in q2.execute():
total_datasets = row2.total_datasets
permitted_actions = []
# There may not yet be any GroupDatasetAssociations, in which case no
# actions will be found
if row2.permitted_actions:
for action in row2.permitted_actions:
permitted_actions.append( action.encode( 'ascii' ) )
permitted_actions.sort()
groups.append( ( row.group_id,
escape( row.group_name, entities ),
row.group_priority,
row.total_members,
total_datasets,
permitted_actions ) )
return trans.fill_template( '/admin/dataset_security/groups.mako',
groups=groups,
msg=msg )
@web.expose
def create_group( self, trans, **kwd ):
if not self.user_is_admin( trans ):
return trans.show_error_message( no_privilege_msg )
params = util.Params( kwd )
msg = params.msg
q = sa.select( ( ( galaxy.model.User.table.c.id ).label( 'user_id' ),
( galaxy.model.User.table.c.email ).label( 'user_email') ),
from_obj = [ galaxy.model.User.table ],
order_by = [ galaxy.model.User.table.c.email ] )
users = []
for row in q.execute():
users.append( ( row.user_id,
escape( row.user_email, entities ) ) )
return trans.fill_template( '/admin/dataset_security/group_create.mako', users=users, msg=msg )
@web.expose
def new_group( self, trans, **kwd ):
if not self.user_is_admin( trans ):
return trans.show_error_message( no_privilege_msg )
params = util.Params( kwd )
msg = params.msg
name = unescape( params.name, unentities )
if not name:
msg = "Please enter a name"
trans.response.send_redirect( '/admin/create_group?msg=%s' % msg )
else:
msg = 'Invalid password'
return msg
try:
priority = int( params.priority )
except:
priority = 0
# Create the group
group = galaxy.model.Group( name, priority )
group.flush()
# Add the members
members = params.members
for user_id in members:
user = galaxy.model.User.get( user_id )
# Create the UserGroupAssociation
user_group_association = galaxy.model.UserGroupAssociation( user, group )
user_group_association.flush()
msg = "The new group has been created with priority %s and %s members" % ( str( priority ), str( len( members ) ) )
trans.response.send_redirect( '/admin/groups?msg=%s' % msg )
@web.expose
def group_members( self, trans, **kwd ):
if not self.user_is_admin( trans ):
return trans.show_error_message( no_privilege_msg )
params = util.Params( kwd )
msg = params.msg
group_id = params.group_id
group_name = unescape( params.group_name, unentities )
# This query retrieves all members of the group
q = sa.select( ( ( galaxy.model.User.table.c.id ).label( 'user_id' ),
( galaxy.model.User.table.c.email ).label( 'user_email' ) ),
whereclause = galaxy.model.UserGroupAssociation.table.c.group_id == group_id,
from_obj = [ sa.outerjoin( galaxy.model.UserGroupAssociation.table,
galaxy.model.User.table ) ],
order_by = [ 'user_email' ] )
members = []
for row in q.execute():
members.append( ( row.user_id,
escape( row.user_email, entities ) ) )
return trans.fill_template( '/admin/dataset_security/group_members.mako',
group_id=group_id,
group_name=escape( group_name, entities ),
members=members,
msg=msg )
@web.expose
def group_members_edit( self, trans, **kwd ):
if not self.user_is_admin( trans ):
return trans.show_error_message( no_privilege_msg )
params = util.Params( kwd )
msg = params.msg
group_id = params.group_id
group_name = unescape( params.group_name, unentities )
members = params.members
# First get all users
q = sa.select( ( ( galaxy.model.User.table.c.id ).label( 'user_id' ),
( galaxy.model.User.table.c.email ).label( 'user_email' ) ),
order_by = [ 'user_email' ] )
users = []
for row in q.execute():
users.append( ( row.user_id,
escape( row.user_email, entities ) ) )
# Then get members of the group
q = sa.select( ( ( galaxy.model.User.table.c.id ).label( 'user_id' ),
( galaxy.model.User.table.c.email ).label( 'user_email' ) ),
whereclause = galaxy.model.UserGroupAssociation.table.c.group_id == group_id,
from_obj = [ sa.outerjoin( galaxy.model.UserGroupAssociation.table,
galaxy.model.User.table ) ],
order_by = [ 'user_email' ] )
members = []
for row in q.execute():
members.append( ( row.user_id,
escape( row.user_email, entities ) ) )
return trans.fill_template( '/admin/dataset_security/group_members_edit.mako',
group_id=group_id,
group_name=escape( group_name, entities ),
users=users,
members=members,
msg=msg )
@web.expose
def update_group_members( self, trans, **kwd ):
if not self.user_is_admin( trans ):
return trans.show_error_message( no_privilege_msg )
params = util.Params( kwd )
group_id = int( params.group_id )
members = params.members
if members and not isinstance( members, list ):
# mako passes singleton lists as strings for some reason
members = [ members ]
# Handle case where admin removed all members from group
elif members is None:
members = []
group = galaxy.model.Group.get( group_id )
# This is tricky since we have default association tables with
# records referring to members of this group. Because of this,
# we'll need to handle changes to the member list rather than the
# simpler approach of deleting all existing members and creating
# new records for user_ids in the received members param.
# First remove existing members that are not in the received members param
for user_group_assoc in group.users:
if user_group_assoc.user_id not in members:
user = galaxy.model.User.get( user_group_assoc.user_id )
# Delete DefaultUserGroupAssociations
for default_user_group_association in user.default_groups:
if default_user_group_association.group_id == group_id:
default_user_group_association.delete()
default_user_group_association.flush()
break # Should only be 1 record
# Delete DefaultHistoryGroupAssociations
for history in user.histories:
for default_history_group_association in history.default_groups:
if default_history_group_association.group_id == group_id:
default_history_group_association.delete()
default_history_group_association.flush()
# Delete the UserGroupAssociation
user_group_assoc.delete()
user_group_assoc.flush()
# Then add all new members to the group
for user_id in members:
user = galaxy.model.User.get( user_id )
if user not in group.users:
user_group_association = galaxy.model.UserGroupAssociation( user, group )
user_group_association.flush()
msg = "Group membership has been updated with a total of %s members" % len( members )
trans.response.send_redirect( '/admin/group_members?group_id=%s&group_name=%s&msg=%s' % ( str( group_id ), params.group_name, msg ) )
@web.expose
def group_dataset_permitted_actions( self, trans, **kwd ):
if not self.user_is_admin( trans ):
return trans.show_error_message( no_privilege_msg )
params = util.Params( kwd )
msg = params.msg
group_id = int( params.group_id )
group_name = unescape( params.group_name, unentities )
# Need to get all actions to send to the form
dataset_actions = []
dpas = RBACAgent.permitted_actions
for dpa in dpas.items():
if dpa[0].startswith( 'DATASET' ):
dataset_actions.append( dpa[1] )
dataset_actions.sort()
q = sa.select( ( ( galaxy.model.Group.table.c.priority ).label( 'group_priority' ),
( galaxy.model.GroupDatasetAssociation.table.c.permitted_actions ).label( 'permitted_actions' ) ),
whereclause = galaxy.model.GroupDatasetAssociation.table.c.id == group_id,
from_obj = [ sa.outerjoin( galaxy.model.Group.table,
galaxy.model.GroupDatasetAssociation.table ) ] )
gdas = []
for row in q.execute():
permitted_actions = []
# Although there may be GroupDatasetAssociations, there may not be any permitted_actions on them
if row.permitted_actions:
for action in row.permitted_actions:
permitted_actions.append( action.encode( 'ascii' ) )
permitted_actions.sort()
gdas.append( ( row.group_priority,
permitted_actions ) )
break # Just need 1 row
return trans.fill_template( '/admin/dataset_security/group_dataset_permitted_actions_edit.mako',
group_id=group_id,
group_name=escape( group_name, entities ),
gdas=gdas,
dataset_actions=dataset_actions,
msg=msg )
@web.expose
def group_dataset_permitted_actions_edit( self, trans, **kwd ):
if not self.user_is_admin( trans ):
return trans.show_error_message( no_privilege_msg )
params = util.Params( kwd )
group_id = int( params.group_id )
actions = params.actions
if actions and not isinstance( actions, list ):
actions = [ actions ]
# Update the permitted_actions for every GroupDatasetAssociation of the Group
q = sa.update( galaxy.model.GroupDatasetAssociation.table,
whereclause = galaxy.model.GroupDatasetAssociation.table.c.group_id == group_id,
values = { galaxy.model.GroupDatasetAssociation.table.c.permitted_actions : actions } )
result = q.execute()
msg = "The dataset permitted actions for the group have been updated, affecting %d rows in the group_dataset_association table" % result.rowcount
trans.response.send_redirect( '/admin/groups?msg=%s' % msg )
@web.expose
def mark_group_deleted( self, trans, **kwd ):
if not self.user_is_admin( trans ):
return trans.show_error_message( no_privilege_msg )
params = util.Params( kwd )
msg = params.msg
group_id = params.group_id
group = galaxy.model.Group.get( group_id )
group.deleted = True
group.flush()
msg = "The group has been marked as deleted."
trans.response.send_redirect( '/admin/groups?msg=%s' % msg )
@web.expose
def deleted_groups( self, trans, **kwd ):
if not self.user_is_admin( trans ):
return trans.show_error_message( no_privilege_msg )
params = util.Params( kwd )
msg = params.msg
# This query retrieves groups that are not deleted and members of each group
q = sa.select( ( ( galaxy.model.Group.table.c.id ).label( 'group_id' ),
( galaxy.model.Group.table.c.name ).label( 'group_name' ),
( galaxy.model.Group.table.c.priority ).label( 'group_priority' ),
sa.func.count( galaxy.model.User.table.c.id ).label( 'total_members' ) ),
whereclause = galaxy.model.Group.table.c.deleted == True,
from_obj = [ sa.outerjoin( galaxy.model.Group.table,
galaxy.model.UserGroupAssociation.table
).outerjoin( galaxy.model.User.table ) ],
group_by = [ galaxy.model.Group.table.c.id,
galaxy.model.Group.table.c.name,
galaxy.model.Group.table.c.priority ],
order_by = [ galaxy.model.Group.table.c.name ] )
groups = []
for row in q.execute():
# This 2nd query retrieves the number of datasets and dataset permitted_actions associated with each group
q2 = sa.select( ( ( galaxy.model.Group.table.c.id ).label( 'group_id' ),
( galaxy.model.GroupDatasetAssociation.table.c.permitted_actions ).label( 'permitted_actions' ),
sa.func.count( galaxy.model.Dataset.table.c.id ).label( 'total_datasets' ) ),
whereclause = galaxy.model.Group.table.c.id == row.group_id,
from_obj = [ sa.outerjoin( galaxy.model.Group.table,
galaxy.model.GroupDatasetAssociation.table
).outerjoin( galaxy.model.Dataset.table ) ],
group_by = [ galaxy.model.Group.table.c.id,
galaxy.model.GroupDatasetAssociation.table.c.permitted_actions ] )
for row2 in q2.execute():
total_datasets = row2.total_datasets
permitted_actions = []
# There may not yet be any GroupDatasetAssociations, in which case no
# actions will be found
if row2.permitted_actions:
for action in row2.permitted_actions:
permitted_actions.append( action.encode( 'ascii' ) )
permitted_actions.sort()
groups.append( ( row.group_id,
escape( row.group_name, entities ),
row.group_priority,
row.total_members,
total_datasets,
permitted_actions ) )
return trans.fill_template( '/admin/dataset_security/deleted_groups.mako',
groups=groups,
msg=msg )
@web.expose
def undelete_group( self, trans, **kwd ):
if not self.user_is_admin( trans ):
return trans.show_error_message( no_privilege_msg )
params = util.Params( kwd )
msg = params.msg
group_id = params.group_id
group = galaxy.model.Group.get( group_id )
group.deleted = False
group.flush()
msg = "The group has been marked as not deleted."
trans.response.send_redirect( '/admin/groups?msg=%s' % msg )
@web.expose
def purge_group( self, trans, **kwd ):
if not self.user_is_admin( trans ):
return trans.show_error_message( no_privilege_msg )
params = util.Params( kwd )
msg = params.msg
group_id = params.group_id
group = galaxy.model.Group.get( group_id )
# Remove members and all associations
for user_group_assoc in group.users:
user = galaxy.model.User.get( user_group_assoc.user_id )
# Delete DefaultUserGroupAssociations
for default_user_group_association in user.default_groups:
if default_user_group_association.group_id == group_id:
default_user_group_association.delete()
default_user_group_association.flush()
break # Should only be 1 record
# Delete DefaultHistoryGroupAssociations
for history in user.histories:
for default_history_group_association in history.default_groups:
if default_history_group_association.group_id == group_id:
default_history_group_association.delete()
default_history_group_association.flush()
# Delete the UserGroupAssociation
user_group_assoc.delete()
user_group_assoc.flush()
# Delete the Group
group.delete()
group.flush()
msg = "The group has been purged from the database."
trans.response.send_redirect( '/admin/deleted_groups?msg=%s' % msg )
# Galaxy User Stuff
@web.expose
def users( self, trans, **kwd ):
if not self.user_is_admin( trans ):
return trans.show_error_message( no_privilege_msg )
params = util.Params( kwd )
msg = params.msg
q = sa.select( ( ( galaxy.model.User.table.c.id ).label( 'user_id' ),
( galaxy.model.User.table.c.email ).label( 'user_email') ),
from_obj = [ galaxy.model.User.table ],
order_by = [ galaxy.model.User.table.c.email ] )
users = []
for row in q.execute():
users.append( ( row.user_id,
escape( row.user_email, entities ) ) )
return trans.fill_template( '/admin/dataset_security/users.mako',
users=users,
msg=msg )
@web.expose
def specified_users_groups( self, trans, **kwd ):
if not self.user_is_admin( trans ):
return trans.show_error_message( no_privilege_msg )
params = util.Params( kwd )
msg = params.msg
user_id = int( params.user_id )
user_email = unescape( params.user_email, unentities )
# Get the groups to which the user belongs
q = sa.select( ( ( galaxy.model.Group.table.c.id ).label( 'group_id' ),
( galaxy.model.Group.table.c.name ).label( 'group_name' ),
( galaxy.model.Group.table.c.priority ).label( 'group_priority' ) ),
whereclause = galaxy.model.User.table.c.id == user_id,
from_obj = [ sa.outerjoin( galaxy.model.User.table,
galaxy.model.UserGroupAssociation.table ).outerjoin( galaxy.model.Group.table ) ],
order_by = [ 'group_name' ] )
groups = []
for row in q.execute():
# Perform a 2nd query to get datasets associated with each group
q2 = sa.select( ( ( galaxy.model.Group.table.c.id ).label( 'group_id' ),
( galaxy.model.GroupDatasetAssociation.table.c.permitted_actions ).label( 'permitted_actions' ),
sa.func.count( galaxy.model.Dataset.table.c.id ).label( 'total_datasets' ) ),
whereclause = galaxy.model.Group.table.c.id == row.group_id,
from_obj = [ sa.outerjoin( galaxy.model.Group.table,
galaxy.model.GroupDatasetAssociation.table
).outerjoin( galaxy.model.Dataset.table ) ],
group_by = [ galaxy.model.Group.table.c.id,
galaxy.model.GroupDatasetAssociation.table.c.permitted_actions ] )
for row2 in q2.execute():
total_datasets = row2.total_datasets
permitted_actions = []
# There may not yet be any GroupDatasetAssociations, in which case no
# actions will be found
if row2.permitted_actions:
for action in row2.permitted_actions:
permitted_actions.append( action.encode( 'ascii' ) )
permitted_actions.sort()
groups.append( ( row.group_id,
escape( row.group_name, entities ),
row.group_priority,
row2.total_datasets,
permitted_actions ) )
return trans.fill_template( '/admin/dataset_security/specified_users_groups.mako',
user_id=user_id,
user_email=escape( user_email, entities ),
groups=groups,
msg=msg )
# Galaxy Library Stuff
@web.expose
def libraries( self, trans, **kwd ):
if not self.user_is_admin( trans ):
return trans.show_error_message( no_privilege_msg )
return trans.fill_template( '/admin/library/libraries.mako', libraries=trans.app.model.Library.select() )
@web.expose
def library( self, trans, id=None, name="Unnamed", description=None, **kwd ):
if not self.user_is_admin( trans ):
return trans.show_error_message( no_privilege_msg )
if 'create_library' in kwd:
library = trans.app.model.Library( name=name, description=description )
root_folder = trans.app.model.LibraryFolder( name=name, description=description )
root_folder.flush()
library.root_folder = root_folder
library.flush()
trans.response.send_redirect( web.url_for( action='folder', id = root_folder.id ) )
elif id is None:
return trans.show_form(
web.FormBuilder( action = web.url_for(), title = "Create a new Library", name = "create_library", submit_text = "Submit" )
.add_text( name = "name", label = "Name", value = "Unnamed", error = None, help = None )
.add_text( name = "description", label = "Description", value = None, error = None, help = None )
.add_input( 'hidden', "Create Library", 'create_library', use_label = False ) )
library = trans.app.model.Library.get( id )
if library:
return trans.fill_template( '/admin/library/library.mako', library = library )
else:
return trans.show_error_message( "Invalid library specified" )
@web.expose
def folder( self, trans, id=None, name="Unnamed", description=None, parent_id = None, **kwd ):
if not self.user_is_admin( trans ):
return trans.show_error_message( no_privilege_msg )
if 'create_folder' in kwd:
folder = trans.app.model.LibraryFolder( name = name, description = description )
# We are associating the last used genome_build with folders, so we will always
# initialize a new folder with the first dbkey in util.dbnames which is currently
# ? unspecified (?)
folder.genome_build = util.dbnames.default_value
if parent_id:
parent_folder = trans.app.model.LibraryFolder.get( parent_id )
parent_folder.add_folder( folder )
folder.flush()
trans.response.send_redirect( web.url_for( action='folder', id = folder.id ) )
elif id is None:
return trans.show_form(
web.FormBuilder( action = web.url_for(), title = "Create a new Folder", name = "create_folder", submit_text = "Submit" )
.add_text( name = "name", label = "Name", value = "Unnamed", error = None, help = None )
.add_text( name = "description", label = "Description", value = None, error = None, help = None )
.add_input( 'hidden', None, 'parent_id', value = parent_id, use_label = False )
.add_input( 'hidden', "Create Folder", 'create_folder', use_label = False ) )
folder = trans.app.model.LibraryFolder.get( id )
if folder:
msg = ''
if 'rename_folder' in kwd:
folder.name = name
folder.description = description
folder.flush()
msg = 'Folder has been renamed.'
return trans.fill_template( '/admin/library/folder.mako', folder=folder, msg=msg )
else:
return trans.show_error_message( "Invalid folder specified" )
@web.expose
def dataset( self, trans, id=None, name="Unnamed", info='no info', extension=None, folder_id=None, dbkey=None, **kwd ):
if not self.user_is_admin( trans ):
return trans.show_error_message( no_privilege_msg )
if isinstance( dbkey, list ):
last_used_build = dbkey[0]
else:
last_used_build = dbkey
if folder_id and not last_used_build:
folder = trans.app.model.LibraryFolder.get( folder_id )
last_used_build = folder.genome_build
data_files = []
# add_file method
def add_file( file_obj, name, extension, dbkey, last_used_build, groups, info='no info', space_to_tab=False ):
data_type = None
temp_name = sniff.stream_to_file( file_obj )
if space_to_tab:
line_count = sniff.convert_newlines_sep2tabs( temp_name )
else:
line_count = sniff.convert_newlines( temp_name )
if extension == 'auto':
data_type = sniff.guess_ext( temp_name, sniff_order=trans.app.datatypes_registry.sniff_order )
else:
data_type = extension
dataset = trans.app.model.LibraryFolderDatasetAssociation( name=name,
info=info,
extension=data_type,
dbkey=dbkey,
create_dataset=True )
folder = trans.app.model.LibraryFolder.get( folder_id )
folder.add_dataset( dataset, genome_build=last_used_build )
dataset.flush()
# GroupDatasetAssociations will enable security on the dataset based on the permitted_actions
# associated with the GroupDatasetAssociation. The default permitted_actions at this point
# will be DATASET_ACCESS, but the user can change this after the file is uploaded.
permitted_actions = [ RBACAgent.permitted_actions.DATASET_ACCESS ]
for group_id in groups:
group = galaxy.model.Group.get( group_id )
group_dataset_assoc = galaxy.model.GroupDatasetAssociation( group, dataset.dataset, permitted_actions )
group_dataset_assoc.flush()
shutil.move( temp_name, dataset.dataset.file_name )
dataset.dataset.state = dataset.dataset.states.OK
dataset.init_meta()
if line_count is not None:
try:
dataset.set_peek( line_count=line_count )
except:
dataset.set_peek()
else:
dataset.set_peek()
dataset.set_size()
if dataset.missing_meta():
dataset.datatype.set_meta( dataset )
trans.app.model.flush()
return dataset
# END add_file method
if 'create_dataset' in kwd:
# Copied from upload tool action
last_dataset_created = None
data_file = kwd['file_data']
url_paste = kwd['url_paste']
space_to_tab = False
if 'space_to_tab' in kwd:
if kwd['space_to_tab'] not in ["None", None]:
space_to_tab = True
groups = kwd['groups']
if groups and not isinstance( groups, list ):
# mako sends singleton lists as a string
groups = [ groups ]
if groups is None:
groups = []
temp_name = ""
data_list = []
if 'filename' in dir( data_file ):
file_name = data_file.filename
file_name = file_name.split( '\\' )[-1]
file_name = file_name.split( '/' )[-1]
last_dataset_created = add_file( data_file.file,
file_name,
extension,
dbkey,
last_used_build,
groups,
info="uploaded file",
space_to_tab=space_to_tab )
elif url_paste not in [ None, "" ]:
if url_paste.lower().find( 'http://' ) >= 0 or url_paste.lower().find( 'ftp://' ) >= 0:
url_paste = url_paste.replace( '\r', '' ).split( '\n' )
for line in url_paste:
line = line.rstrip( '\r\n' )
if line:
last_dataset_created = add_file( urllib.urlopen( line ),
line,
extension,
dbkey,
last_used_build,
groups,
info="uploaded url",
space_to_tab=space_to_tab )
else:
is_valid = False
for line in url_paste:
line = line.rstrip( '\r\n' )
if line:
is_valid = True
break
if is_valid:
last_dataset_created = add_file( StringIO.StringIO( url_paste ),
'Pasted Entry',
extension,
dbkey,
last_used_build,
groups,
info="pasted entry",
space_to_tab=space_to_tab )
trans.response.send_redirect( web.url_for( action='dataset', id=last_dataset_created.id ) )
elif id is None:
# Send list of data formats to the form so the "extension" select list can be populated dynamically
file_formats = trans.app.datatypes_registry.upload_file_formats
# Send list of genome builds to the form so the "dbkey" select list can be populated dynamically
def get_dbkey_options( last_used_build ):
for dbkey, build_name in util.dbnames:
yield build_name, dbkey, ( dbkey==last_used_build )
dbkeys = get_dbkey_options( last_used_build )
# Send list of groups to the form so the dataset can be associated with 1 or more of them.
groups = []
q = sa.select( ( ( galaxy.model.Group.table.c.id ).label( 'group_id' ),
( galaxy.model.Group.table.c.name ).label( 'group_name' ) ),
order_by = [ galaxy.model.Group.table.c.name ] )
for row in q.execute():
groups.append( ( row.group_id, row.group_name ) )
groups = sorted( groups, key=operator.itemgetter(1) )
return trans.fill_template( '/admin/library/new_dataset.mako',
folder_id=folder_id,
file_formats=file_formats,
dbkeys=dbkeys,
last_used_build=last_used_build,
groups=groups )
dataset = trans.app.model.LibraryFolderDatasetAssociation.get( id )
if dataset:
# Copied from edit attributes for 'regular' datasets with some additions
p = util.Params(kwd, safe=False)
if p.change_permitted_actions:
# The user clicked the Save button on the 'Group Associations' form
actions = p.actions
if actions and not isinstance( actions, list ):
actions = [ actions ]
if actions is None:
actions = []
# actions is a list of comma-separated strings consisting of group_id and permitted_action,
# something like: ['6,dataset_access', '6,dataset_edit_metadata']. We'll parse them and
# create a dict whose keys are groups_id and values are permitted_actions
gdpa_dict = {}
for action in actions:
group_id, dpa = action.split( ',' )
group_id = int( group_id )
if group_id in gdpa_dict.keys():
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:
if group_dataset_assoc.group_id not in gdpa_dict.keys():
group_dataset_assoc.delete()
group_dataset_assoc.flush()
# Use the dict to update the permitted actions for each GroupDatasetAssociaton
for group_id in gdpa_dict:
actions = gdpa_dict[ group_id ]
# Update the permitted_actions for every GroupDatasetAssociation of the Group
q = sa.update( galaxy.model.GroupDatasetAssociation.table,
whereclause = galaxy.model.GroupDatasetAssociation.table.c.group_id == group_id,
values = { galaxy.model.GroupDatasetAssociation.table.c.permitted_actions : actions } )
result = q.execute()
elif p.change:
# The user clicked the Save button on the 'Change data type' form
trans.app.datatypes_registry.change_datatype( dataset, p.datatype )
trans.app.model.flush()
elif p.save:
# The user clicked the Save button on the 'Edit Attributes' form
dataset.name = name
dataset.info = info
# The following for loop will save all metadata_spec items
for name, spec in dataset.datatype.metadata_spec.items():
if spec.get("readonly"):
continue
optional = p.get("is_"+name, None)
if optional and optional == 'true':
# optional element... == 'true' actually means it is NOT checked (and therefore ommitted)
setattr(dataset.metadata,name,None)
else:
setattr(dataset.metadata,name,spec.unwrap(p.get(name, None), p))
dataset.datatype.after_edit( dataset )
trans.app.model.flush()
return trans.show_ok_message( "Attributes updated" )
elif p.detect:
# The user clicked the Auto-detect button on the 'Edit Attributes' form
for name, spec in dataset.datatype.metadata_spec.items():
# We need to be careful about the attributes we are resetting
if name != 'name' and name != 'info' and name != 'dbkey':
if spec.get( 'default' ):
setattr( dataset.metadata,name,spec.unwrap( spec.get( 'default' ), spec ))
dataset.datatype.set_meta( dataset )
dataset.datatype.after_edit( dataset )
trans.app.model.flush()
return trans.show_ok_message( "Attributes updated" )
dataset.datatype.before_edit( dataset )
# Get all actions to send to the form
dataset_actions = []
dpas = RBACAgent.permitted_actions
for dpa in dpas.items():
if dpa[0].startswith( 'DATASET' ):
dataset_actions.append( dpa[1] )
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 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:
# Copy dbkey into metadata, for backwards compatability
# This looks like it does nothing, but getting the dbkey
# returns the metadata dbkey unless it is None, in which
# case it resorts to the old dbkey. Setting the dbkey
# sets it properly in the metadata
dataset.metadata.dbkey = dataset.dbkey
metadata = list()
# a list of MetadataParemeters
for name, spec in dataset.datatype.metadata_spec.items():
if spec.visible:
metadata.append( spec.wrap( dataset.metadata.get(name), dataset ) )
# let's not overwrite the imported datatypes module with the variable datatypes?
ldatatypes = [x for x in trans.app.datatypes_registry.datatypes_by_extension.iterkeys()]
ldatatypes.sort()
return trans.fill_template( "/admin/library/dataset.mako",
dataset=dataset,
metadata=metadata,
datatypes=ldatatypes,
dataset_actions=dataset_actions,
gdas=gdas,
err=None )
else:
return trans.show_error_message( "Invalid dataset specified" )
+3 -1
View File
@@ -60,7 +60,7 @@ class ASync( BaseController ):
if STATUS == 'OK':
key = hmac.new( trans.app.config.tool_secret, "%d:%d" % ( data.id, data.history_id), sha ).hexdigest()
if key != data_secret:
return "You do not have permision to alter data %s." % data_id
return "You do not have permission to alter data %s." % data_id
# push the job into the queue
data.state = data.blurb = data.states.RUNNING
log.debug('executing tool %s' % tool.id)
@@ -104,6 +104,8 @@ class ASync( BaseController ):
#history.datasets.add_dataset( data )
data = trans.app.model.HistoryDatasetAssociation( create_dataset = True, extension = GALAXY_TYPE )
# TODO, Nate: Make sure the following is functionally correct.
trans.app.security_agent.set_dataset_groups( data.dataset, trans.history.default_groups )
data.name = GALAXY_NAME
data.dbkey = GALAXY_BUILD
data.info = GALAXY_INFO
+23 -24
View File
@@ -103,28 +103,27 @@ class DatasetInterface( BaseController ):
@web.expose
def display(self, trans, dataset_id=None, filename=None, **kwd):
"""Catches the dataset id and displays file contents as directed"""
if filename is None or filename.lower() == "index":
try:
data = trans.app.model.HistoryDatasetAssociation.get( dataset_id )
if data:
mime = trans.app.datatypes_registry.get_mimetype_by_extension( data.extension.lower() )
trans.response.set_content_type(mime)
trans.log_event( "Display dataset id: %s" % str(dataset_id) )
try:
return open( data.file_name )
except:
return "This item contains no content"
except:
pass
return "Invalid dataset specified"
else:
#display files from directory here
try:
file_path = os.path.join(trans.app.model.HistoryDatasetAssociation.get( dataset_id ).extra_files_path, filename)
mime, encoding = mimetypes.guess_type(file_path)
if mime is None:
mime = trans.app.datatypes_registry.get_mimetype_by_extension(".".split(file_path)[-1])
data = trans.app.model.HistoryDatasetAssociation.get( dataset_id )
if not data:
raise paste.httpexceptions.HTTPRequestRangeNotSatisfiable( "Invalid reference dataset." )
if trans.app.security_agent.allow_action( trans.user, data.permitted_actions.DATASET_ACCESS, dataset = data ):
if filename is None or filename.lower() == "index":
mime = trans.app.datatypes_registry.get_mimetype_by_extension( data.extension.lower() )
trans.response.set_content_type(mime)
return open(file_path)
except:
raise paste.httpexceptions.HTTPNotFound( "File Not Found (%s)." % (filename) )
trans.log_event( "Display dataset id: %s" % str( dataset_id ) )
try:
return open( data.file_name )
except:
raise paste.httpexceptions.HTTPNotFound( "File Not Found (%s)." % ( filename ) )
else:
file_path = os.path.join( data.extra_files_path, filename )
mime, encoding = mimetypes.guess_type( file_path )
if mime is None:
mime = trans.app.datatypes_registry.get_mimetype_by_extension( ".".split( file_path )[-1] )
trans.response.set_content_type( mime )
try:
return open( file_path )
except:
raise paste.httpexceptions.HTTPNotFound( "File Not Found (%s)." % ( filename ) )
else:
raise paste.httpexceptions.HTTPForbidden( "You are not permitted to access this dataset." )
+96
View File
@@ -0,0 +1,96 @@
from galaxy.web.base.controller import *
import logging
log = logging.getLogger( __name__ )
class Library( BaseController ):
@web.expose
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()
for id in import_ids:
dataset = trans.app.model.LibraryFolderDatasetAssociation.get( id ).to_history_dataset_association()
history.add_dataset( dataset )
dataset.flush()
history.flush()
return trans.show_ok_message( "%i datasets have been imported into your history" % len( import_ids ), refresh_frames=['history'] )
elif library_id:
# 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 )
+179 -87
View File
@@ -14,6 +14,8 @@ import urllib
log = logging.getLogger( __name__ )
class RootController( BaseController ):
# TODO, Nate: This is where a lot of the new dataset security stuff is managed.
# Make sure it is is functionally correct.
@web.expose
def default(self, trans, target1=None, target2=None, **kwd):
@@ -137,22 +139,25 @@ class RootController( BaseController ):
except:
return "Dataset id '%s' is invalid" %str( id )
if data:
mime = trans.app.datatypes_registry.get_mimetype_by_extension( data.extension.lower() )
trans.response.set_content_type(mime)
if tofile:
fStat = os.stat(data.file_name)
trans.response.headers['Content-Length'] = int(fStat.st_size)
if toext[0:1] != ".":
toext = "." + toext
valid_chars = '.,^_-()[]0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
fname = data.name
fname = ''.join(c in valid_chars and c or '_' for c in fname)[0:150]
trans.response.headers["Content-Disposition"] = "attachment; filename=GalaxyHistoryItem-%s-[%s]%s" % (data.hid, fname, toext)
trans.log_event( "Display dataset id: %s" % str(id) )
try:
return open( data.file_name )
except:
return "This dataset contains no content"
if trans.app.security_agent.allow_action( trans.user, data.permitted_actions.DATASET_ACCESS, dataset = data ):
mime = trans.app.datatypes_registry.get_mimetype_by_extension( data.extension.lower() )
trans.response.set_content_type(mime)
if tofile:
fStat = os.stat(data.file_name)
trans.response.headers['Content-Length'] = int(fStat.st_size)
if toext[0:1] != ".":
toext = "." + toext
valid_chars = '.,^_-()[]0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
fname = data.name
fname = ''.join(c in valid_chars and c or '_' for c in fname)[0:150]
trans.response.headers["Content-Disposition"] = "attachment; filename=GalaxyHistoryItem-%s-[%s]%s" % (data.hid, fname, toext)
trans.log_event( "Display dataset id: %s" % str(id) )
try:
return open( data.file_name )
except:
return "This dataset contains no content"
else:
return "You are not privileged to view this dataset."
else:
return "No dataset with id '%s'" % str( id )
@@ -164,9 +169,12 @@ class RootController( BaseController ):
try:
data = self.app.model.HistoryDatasetAssociation.get( parent_id )
if data:
child = data.get_child_by_designation(designation)
child = data.get_child_by_designation( designation )
if child:
return self.display(trans, id=child.id, tofile=tofile, toext=toext)
if trans.app.security_agent.allow_action( trans.user, child.permitted_actions.DATASET_ACCESS, dataset = child ):
return self.display( trans, id=child.id, tofile=tofile, toext=toext )
else:
return "You are not privileged to access this dataset."
except Exception:
pass
return "A child named %s could not be found for data %s" % ( designation, parent_id )
@@ -176,9 +184,12 @@ class RootController( BaseController ):
"""Returns a file in a format that can successfully be displayed in display_app"""
data = self.app.model.HistoryDatasetAssociation.get( id )
if data:
trans.response.set_content_type(data.get_mime())
trans.log_event( "Formatted dataset id %s for display at %s" % ( str(id), display_app ) )
return data.as_display_type(display_app, **kwd)
if trans.app.security_agent.allow_action( trans.user, data.permitted_actions.DATASET_ACCESS, dataset = data ):
trans.response.set_content_type( data.get_mime() )
trans.log_event( "Formatted dataset id %s for display at %s" % ( str( id ), display_app ) )
return data.as_display_type( display_app, **kwd )
else:
return "You are not privileged to access this dataset."
else:
return "No data with id=%d" % id
@@ -206,69 +217,95 @@ class RootController( BaseController ):
data = self.app.model.HistoryDatasetAssociation.get( id )
if data is None:
return trans.show_error_message( "Problem retrieving dataset id %s with history id %s." % ( str( id ), str( hid ) ) )
p = util.Params(kwd, safe=False)
if p.change:
# The user clicked the Save button on the 'Change data type' form
trans.app.datatypes_registry.change_datatype( data, p.datatype )
trans.app.model.flush()
elif p.save:
# The user clicked the Save button on the 'Edit Attributes' form
data.name = p.name
data.info = p.info
if 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 ) )
if trans.app.security_agent.allow_action( trans.user, data.permitted_actions.DATASET_ACCESS, dataset = data ):
p = util.Params(kwd, safe=False)
# The following for loop will save all metadata_spec items
for name, spec in data.datatype.metadata_spec.items():
if spec.get("readonly"):
continue
optional = p.get("is_"+name, None)
if optional and optional == 'true':
# optional element... == 'true' actually means it is NOT checked (and therefore ommitted)
setattr(data.metadata,name,None)
else:
setattr(data.metadata,name,spec.unwrap(p.get(name, None), p))
if p.change:
# The user clicked the Save button on the 'Change data type' form
trans.app.datatypes_registry.change_datatype( data, p.datatype )
trans.app.model.flush()
elif p.save:
# The user clicked the Save button on the 'Edit Attributes' form
data.name = p.name
data.info = p.info
# The following for loop will save all metadata_spec items
for name, spec in data.datatype.metadata_spec.items():
if spec.get("readonly"):
continue
optional = p.get("is_"+name, None)
if optional and optional == 'true':
# optional element... == 'true' actually means it is NOT checked (and therefore ommitted)
setattr(data.metadata,name,None)
else:
setattr(data.metadata,name,spec.unwrap(p.get(name, None), p))
data.datatype.after_edit( data )
trans.app.model.flush()
return trans.show_ok_message( "Attributes updated", refresh_frames=['history'] )
elif p.detect:
# The user clicked the Auto-detect button on the 'Edit Attributes' form
data.datatype.after_edit( data )
trans.app.model.flush()
return trans.show_ok_message( "Attributes updated", refresh_frames=['history'] )
elif p.detect:
# The user clicked the Auto-detect button on the 'Edit Attributes' form
for name, spec in data.datatype.metadata_spec.items():
# We need to be careful about the attributes we are resetting
if name != 'name' and name != 'info' and name != 'dbkey':
if spec.get( 'default' ):
setattr( data.metadata,name,spec.unwrap( spec.get( 'default' ), spec ))
data.datatype.set_meta( data )
data.datatype.after_edit( data )
trans.app.model.flush()
return trans.show_ok_message( "Attributes updated", refresh_frames=['history'] )
elif p.convert_data:
"""The user clicked the Convert button on the 'Convert to new format' form"""
target_type = kwd.get("target_type", None)
if target_type:
msg = data.datatype.convert_dataset(trans, data, target_type)
return trans.show_ok_message( msg, refresh_frames=['history'] )
elif p.change_permission:
"""The user clicked the change_permission button on the 'Change permissions' form"""
if not trans.user:
return trans.show_error_message( "You must be logged in if you want to change dataset permitted actions." )
private_dataset = 'private_dataset'
public_group = trans.app.security_agent.get_public_group()
if private_dataset in kwd and trans.app.security_agent.dataset_has_group( data.dataset.id, public_group.id ):
#check user has permission and then remove public group
if trans.app.security_agent.allow_action( trans.user, data.dataset.permitted_actions.DATASET_MANAGE_PERMISSIONS, dataset = data.dataset ):
trans.app.security_agent.disassociate_components( dataset = data, group = public_group )
else:
return trans.show_error_message( "You are not authorized to change this dataset's permitted actions." )
elif private_dataset not in kwd and not trans.app.security_agent.dataset_has_group( data.dataset.id, public_group.id ):
#check user has permission and then add public group
if trans.app.security_agent.allow_action( trans.user, data.dataset.permitted_actions.DATASET_MANAGE_PERMISSIONS, dataset = data.dataset ):
trans.app.security_agent.associate_components( dataset = data, group = public_group)
else:
return trans.show_error_message( "You are not authorized to change this dataset's permitted actions." )
else:
return trans.show_error_message( "You have not specified a valid change of permitted actions." )
return trans.show_ok_message( 'Permitted actions have been changed.', refresh_frames=['history'] )
data.datatype.before_edit( data )
if "dbkey" in data.datatype.metadata_spec and not data.metadata.dbkey:
# Copy dbkey into metadata, for backwards compatability
# This looks like it does nothing, but getting the dbkey
# returns the metadata dbkey unless it is None, in which
# case it resorts to the old dbkey. Setting the dbkey
# sets it properly in the metadata
data.metadata.dbkey = data.dbkey
metadata = list()
# a list of MetadataParemeters
for name, spec in data.datatype.metadata_spec.items():
# We need to be careful about the attributes we are resetting
if name != 'name' and name != 'info' and name != 'dbkey':
if spec.get( 'default' ):
setattr( data.metadata,name,spec.unwrap( spec.get( 'default' ), spec ))
data.datatype.set_meta( data )
data.datatype.after_edit( data )
trans.app.model.flush()
return trans.show_ok_message( "Attributes updated", refresh_frames=['history'] )
elif p.convert_data:
"""The user clicked the Convert button on the 'Convert to new format' form"""
target_type = kwd.get("target_type", None)
if target_type:
msg = data.datatype.convert_dataset(trans, data, target_type)
return trans.show_ok_message( msg, refresh_frames=['history'] )
data.datatype.before_edit( data )
if "dbkey" in data.datatype.metadata_spec and not data.metadata.dbkey:
# Copy dbkey into metadata, for backwards compatability
# This looks like it does nothing, but getting the dbkey
# returns the metadata dbkey unless it is None, in which
# case it resorts to the old dbkey. Setting the dbkey
# sets it properly in the metadata
data.metadata.dbkey = data.dbkey
metadata = list()
# a list of MetadataParemeters
for name, spec in data.datatype.metadata_spec.items():
if spec.visible:
metadata.append( spec.wrap( data.metadata.get(name), data ) )
# let's not overwrite the imported datatypes module with the variable datatypes?
ldatatypes = [x for x in trans.app.datatypes_registry.datatypes_by_extension.iterkeys()]
ldatatypes.sort()
trans.log_event( "Opened edit view on dataset %s" % str(id) )
return trans.fill_template( "/dataset/edit_attributes.mako", data=data, metadata=metadata,
datatypes=ldatatypes, err=None )
if spec.visible:
metadata.append( spec.wrap( data.metadata.get(name), data ) )
# let's not overwrite the imported datatypes module with the variable datatypes?
ldatatypes = [x for x in trans.app.datatypes_registry.datatypes_by_extension.iterkeys()]
ldatatypes.sort()
trans.log_event( "Opened edit view on dataset %s" % str(id) )
return trans.fill_template( "/dataset/edit_attributes.mako", data=data, metadata=metadata,
datatypes=ldatatypes, err=None )
else:
return trans.show_error_message( "You do not have permission to edit this dataset's (%s) attributes." % id )
@web.expose
def delete( self, trans, id = None, **kwd):
@@ -327,6 +364,8 @@ class RootController( BaseController ):
self.app.job_stop_queue.put( data.creating_job_associations[0].job )
except IndexError:
pass # upload tool will cause this since it doesn't have a job
else:
return "Dataset id '%s' is invalid" %str( id )
return "OK"
## ---- History management -----------------------------------------------
@@ -356,8 +395,13 @@ class RootController( BaseController ):
if history:
if history.user_id != None and user:
assert user.id == history.user_id, "History does not belong to current user"
history_names.append(history.name)
# Delete DefaultHistoryGroupAssociations
for default_history_group_association in history.default_groups:
default_history_group_association.delete()
default_history_group_association.flush()
# Mark history as deleted in db
history.deleted = True
history_names.append(history.name)
# If deleting the current history, make a new current.
if history == trans.get_history():
trans.new_history()
@@ -404,7 +448,7 @@ class RootController( BaseController ):
send_to_err = "You can't send histories to yourself"
else:
for history in histories:
new_history = history.copy()
new_history = history.copy( target_user=send_to_user )
new_history.name = history.name+" from "+user.email
new_history.user_id = send_to_user.id
trans.log_event( "History share, id: %s, name: '%s': to new id: %s" % (str(history.id), history.name, str(new_history.id)) )
@@ -441,7 +485,7 @@ class RootController( BaseController ):
if user:
if import_history.user_id == user.id:
return trans.show_error_message( "You cannot import your own history.")
new_history = import_history.copy()
new_history = import_history.copy( target_user=trans.user )
new_history.name = "imported: "+new_history.name
new_history.user_id = user.id
galaxy_session = trans.get_galaxy_session()
@@ -548,11 +592,16 @@ class RootController( BaseController ):
return trans.show_message( "<p>%s" % change_msg, refresh_frames=['history'] )
@web.expose
def history_add_to( self, trans, history_id=None, file_data=None, name="Data Added to History",info=None,ext="txt",dbkey="?",**kwd ):
def history_add_to( self, trans, history_id=None, file_data=None, name="Data Added to History",info=None,ext="txt",dbkey="?",copy_access_from=None,**kwd ):
"""Adds a POSTed file to a History"""
try:
history = trans.app.model.History.get( history_id )
groups = history.default_groups
if copy_access_from:
copy_access_from = trans.app.model.HistoryDatasetAssociation.get( copy_access_from )
groups = copy_access_from.dataset.groups
data = trans.app.model.HistoryDatasetAssociation( name = name, info = info, extension = ext, dbkey = dbkey, create_dataset = True )
trans.app.security_agent.set_dataset_groups( data.dataset, groups )
data.flush()
data_file = open( data.file_name, "wb" )
file_data.file.seek( 0 )
@@ -569,9 +618,45 @@ class RootController( BaseController ):
data.flush()
trans.log_event("Added dataset %d to history %d" %(data.id, trans.history.id))
return trans.show_ok_message("Dataset "+str(data.hid)+" added to history "+str(history_id)+".")
except:
except Exception, e:
trans.log_event( "Failed to add dataset to history: %s" % ( e ) )
return trans.show_error_message("Adding File to History has Failed")
@web.expose
def history_set_default_permitted_actions( self, trans, **kwd ):
"""Sets the user's default permitted_actions for the current history"""
if trans.user:
if 'set_permitted_actions' in kwd:
"""The user clicked the set_permitted_actions button on the set_permitted_actions form"""
history = trans.get_history()
group_in = []
group_out = []
# Collect groups as entered by user
for name, value in kwd.items():
if name.startswith( "group_" ):
group = trans.app.security_agent.get_group( name.replace( "group_", "", 1 ) )
if not group:
return trans.show_error_message( 'You have specified an invalid group.' )
if value == 'in':
group_in.append( group )
else:
group_out.append( group )
if not group_in:
return trans.show_error_message( "You must specify at least one default group." )
cur_groups = [ assoc.group for assoc in history.default_groups ]
group_in.sort()
cur_groups.sort()
if cur_groups != group_in:
trans.app.security_agent.history_set_default_access( history, groups=group_in )
return trans.show_ok_message( 'Default history permitted actions have been changed.' )
else:
return trans.show_error_message( "You did not specify any changes to this history's default permitted actions." )
return trans.fill_template( 'history/permissions.mako' )
else:
#user not logged in, history group must be only public
return trans.show_error_message( "You must be logged in to change a history's default permitted actions." )
@web.expose
def dataset_make_primary( self, trans, id=None):
"""Copies a dataset and makes primary"""
@@ -596,8 +681,15 @@ class RootController( BaseController ):
bugs_email = trans.app.config.get( "bugs_email", "mailto:galaxy-bugs@bx.psu.edu" )
blog_url = trans.app.config.get( "blog_url", "http://g2.trac.bx.psu.edu/blog" )
screencasts_url = trans.app.config.get( "screencasts_url", "http://g2.trac.bx.psu.edu/wiki/ScreenCasts" )
admin_user = "false"
admin_users = trans.app.config.get( "admin_users", "" ).split( "," )
user = trans.get_user()
if user:
user_email = trans.get_user().email
if user_email in admin_users:
admin_user = "true"
return trans.fill_template( "/root/masthead.mako", brand=brand, wiki_url=wiki_url,
blog_url=blog_url,bugs_email=bugs_email, screencasts_url=screencasts_url )
blog_url=blog_url,bugs_email=bugs_email, screencasts_url=screencasts_url, admin_user=admin_user )
@web.expose
def dataset_errors( self, trans, id=None, **kwd ):
+35
View File
@@ -118,6 +118,7 @@ class User( BaseController ):
user = trans.app.model.User( email=email )
user.set_password_cleartext( password )
user.flush()
trans.app.security_agent.setup_new_user( user )
trans.set_user( user )
trans.ensure_valid_galaxy_session()
"""
@@ -166,3 +167,37 @@ class User( BaseController ):
return trans.show_form(
web.FormBuilder( web.url_for(), "Reset Password", submit_text="Submit" )
.add_text( "email", "Email", value=email, error=error ) )
@web.expose
def set_default_permitted_actions( self, trans, **kwd ):
# TODO, Nate: Make sure this method is functionally correct.
"""Sets the user's default permitted actions for the new histories"""
if trans.user:
if 'set_permitted_actions' in kwd:
"""The user clicked the set_permitted_actions button on the set_permitted_actions form"""
group_in = []
group_out = []
# Collect groups as entered by user
for name, value in kwd.items():
if name.startswith( "group_" ):
group = trans.app.security_agent.get_group( name.replace( "group_", "", 1 ) )
if not group:
return trans.show_error_message( 'You have specified an invalid group.' )
if value == 'in':
group_in.append( group )
else:
group_out.append( group )
if not group_in:
return trans.show_error_message( "You must specify at least one default group." )
cur_groups = [ assoc.group for assoc in trans.user.default_groups ]
group_in.sort()
cur_groups.sort()
if cur_groups != group_in:
trans.app.security_agent.user_set_default_access( trans.user, groups = group_in )
return trans.show_ok_message( 'Default new history permitted actions have been changed.' )
else:
return trans.show_error_message( "You did not specify any changes to new history's default permitted actions." )
return trans.fill_template( 'user/permissions.mako' )
else:
# User not logged in, history group must be only public
return trans.show_error_message( "You must be logged in to change your default permitted actions." )
+11 -4
View File
@@ -197,9 +197,10 @@ class UniverseWebTransaction( base.DefaultWebTransaction ):
return self.new_history()
return self.__history
def new_history( self ):
history = self.app.model.History()
history = self.app.model.History( user = self.user )
# Make sure we have an id
history.flush()
self.app.security_agent.history_set_default_access( history )
# Immediately associate the new history with self
self.__history = history
# Make sure we have a valid session to associate with the new history
@@ -271,6 +272,7 @@ class UniverseWebTransaction( base.DefaultWebTransaction ):
user.set_password_cleartext( 'external' )
user.external = True
user.flush()
self.app.security_agent.setup_new_user( user )
self.log_event( "Automatically created account '%s'" % user.email )
return user
def get_cookie_user( self ):
@@ -431,6 +433,10 @@ class UniverseWebTransaction( base.DefaultWebTransaction ):
galaxy_session.flush()
self.__galaxy_session = galaxy_session
if history is not None and user is not None:
# TODO, Nate: Make sure the following is functionally correct
if not history.user:
# This user will now acquire previously non-owned history, so set permitted actions to user's default
self.app.security_agent.history_set_default_access( history, groups=user.default_groups, dataset=True )
history.user_id = user.id
history.flush()
self.__history = history
@@ -522,8 +528,8 @@ class FormBuilder( object ):
self.action = action
self.submit_text = submit_text
self.inputs = []
def add_input( self, type, name, label, value=None, error=None, help=None ):
self.inputs.append( FormInput( type, label, name, value, error, help ) )
def add_input( self, type, name, label, value=None, error=None, help=None, use_label=True ):
self.inputs.append( FormInput( type, label, name, value, error, help, use_label ) )
return self
def add_text( self, name, label, value=None, error=None, help=None ):
return self.add_input( 'text', label, name, value, error, help )
@@ -534,13 +540,14 @@ class FormInput( object ):
"""
Simple class describing a form input element
"""
def __init__( self, type, name, label, value=None, error=None, help=None ):
def __init__( self, type, name, label, value=None, error=None, help=None, use_label=True ):
self.type = type
self.name = name
self.label = label
self.value = value
self.error = error
self.help = help
self.use_label = use_label
class FormData( object ):
"""
@@ -0,0 +1,96 @@
<%inherit file="/base.mako"/>
<%
from galaxy.web.controllers.admin import entities, unentities
from xml.sax.saxutils import escape, unescape
%>
<%def name="title()">Create Group</%def>
<div class="toolForm">
<div class="form-row">
<a href="${h.url_for( controller='admin', action='libraries' )}">Libraries</a>&nbsp;&nbsp;|&nbsp;&nbsp;
<a href="${h.url_for( controller='admin', action='groups' )}">Groups</a>&nbsp;&nbsp;|&nbsp;&nbsp;
<tr><td><a href="${h.url_for( controller='admin', action='users' )}">Users</a></td></tr>
</div>
<h3 align="center">Create Group</h3>
<table align="center" class="colored">
%if msg:
<tr><td><p class="ok_bgr">${msg}</p></td></tr>
%endif
<tr>
<td>
<table border="0">
<form name="group_create" action="${h.url_for( controller='admin', action='new_group' )}" method="post" >
<tr>
<td>Name: <input name="name" type="textfield" value="" size=40">&nbsp;&nbsp;Priority: <input name="priority" type="textfield" value="0" size=3"></td>
</tr>
%if len( users ) == 0:
<tr><td>There are no Galaxy users</td></tr>
%else:
<tr class="header"><td><a name="TOP">Add Members to Group - Quick Find</a></td></tr>
<tr>
<td>
|<a href="#A">A</a>|<a href="#B">B</a>|<a href="#C">C</a>|<a href="#D">D</a>|<a href="#E">E</a>|<a href="#F">F</a>
|<a href="#G">G</a>|<a href="#H">H</a>|<a href="#I">I</a>|<a href="#J">J</a>|<a href="#K">K</a>|<a href="#L">L</a>
|<a href="#M">M</a>|<a href="#N">N</a>|<a href="#O">O</a>|<a href="#P">P</a>|<a href="#Q">Q</a>|<a href="#R">R</a>
|<a href="#S">S</a>|<a href="#T">T</a>|<a href="#U">U</a>|<a href="#V">V</a>|<a href="#W">W</a>|<a href="#X">X</a>
|<a href="#Y">Y</a>|<a href="#Z">Z</a>
</td>
</tr>
<tr>
<td>
<%
ctr = 0
anchors = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z']
anchor_loc = 0
anchored = False
curr_anchor = 'A'
%>
%for user in users:
<% email = unescape( user[1], unentities ) %>
%if not email.upper().startswith( curr_anchor ):
<% anchored = False %>
%endif
%if ctr % 2 == 1:
<tr class="odd_row">
%else:
<tr class="tr">
%endif
<td>
%if email.upper().startswith( curr_anchor ):
%if not anchored:
<a name="${curr_anchor}"><hr/></a><br/>
<% anchored = True %>
%endif
<input type="checkbox" name="members" value="${user[0]}"/> ${email}
%else:
%for anchor in anchors[ anchor_loc: ]:
%if email.upper().startswith( anchor ):
%if not anchored:
<a name="${anchor}"><hr/></a><br/>
<%
curr_anchor = anchor
anchored = True
%>
%endif
<input type="checkbox" name="members" value="${user[0]}"/> ${email}
<%
anchor_loc = anchors.index( anchor )
break
%>
%endif
%endfor
%endif
</td>
<% ctr += 1 %>
%endfor
</td>
</tr>
%endif
<tr><td><center><button name="action" value="group_create">Create</button></center></td></tr>
</form>
</table>
</td>
</tr>
</table>
</div>
@@ -0,0 +1,71 @@
<%inherit file="/base.mako"/>
<%
from galaxy.web.controllers.admin import entities, unentities
from xml.sax.saxutils import escape, unescape
%>
<% gn = unescape( group_name, unentities ) %>
<%def name="title()">Permitted Actions on Datasets</%def>
<div class="toolForm">
<div class="form-row">
<a href="${h.url_for( controller='admin', action='libraries' )}">Libraries</a>&nbsp;&nbsp;|&nbsp;&nbsp;
<a href="${h.url_for( controller='admin', action='groups' )}">Groups</a>&nbsp;&nbsp;|&nbsp;&nbsp;
<tr><td><a href="${h.url_for( controller='admin', action='users' )}">Users</a></td></tr>
</div>
<h3 align="center">Manage Permitted Actions on Datasets for Group '${gn}'</h3>
<table align="center" class="colored">
%if msg:
<tr><td colspan="3"><p class="ok_bgr">${msg}</p></td></tr>
%endif
<tr><td colspan="3">&nbsp;</td>
%if len( gdas ) == 0:
<tr><td colspan="3">There is no Galaxy group named '${gn}'</td></tr>
%else:
<tr class="header">
<td>Group</td>
<td>Priority</td>
<td>Permitted Actions on Datasets</td>
</tr>
<% ctr = 0 %>
<form name="group_dataset_permitted_actions_edit" action="${h.url_for( controller='admin',
action='group_dataset_permitted_actions_edit',
group_id=group_id,
group_name=group_name )}" method="post" >
%for gda in gdas:
%if ctr % 2 == 1:
<tr class="odd_row">
%else:
<tr class="tr">
%endif
<td>${gn}</td>
<td>${gda[0]}</td>
<td>
%for da in dataset_actions:
<% check = False %>
%for action in gda[1]:
%if action == da:
<%
check = True
break
%>
%endif
%endfor
%if check:
<input type="checkbox" name="actions" value="${da}" checked/>
%else:
<input type="checkbox" name="actions" value="${da}"/>
%endif
${da}<br/>
%endfor
<br/>
</td>
</tr>
<% ctr += 1 %>
%endfor
<tr><td colspan="3"><center><button name="action" value="group_dataset_permitted_actions_edit">Update</button></center></td></tr>
</form>
%endif
</table>
</div>
@@ -0,0 +1,47 @@
<%inherit file="/base.mako"/>
<%
from galaxy.web.controllers.admin import entities, unentities
from xml.sax.saxutils import escape, unescape
%>
<% gn = unescape( group_name, unentities ) %>
<%def name="title()">Create Group</%def>
<div class="toolForm">
<div class="form-row">
<a href="${h.url_for( controller='admin', action='libraries' )}">Libraries</a>&nbsp;&nbsp;|&nbsp;&nbsp;
<a href="${h.url_for( controller='admin', action='groups' )}">Groups</a>&nbsp;&nbsp;|&nbsp;&nbsp;
<tr><td><a href="${h.url_for( controller='admin', action='users' )}">Users</a></td></tr>
</div>
<div class="form-row">
<a href="${h.url_for( controller='admin', action='group_members_edit', group_id=group_id, group_name=group_name )}">Manage group membership</a>
<br/>
</div>
<div class="toolFormTitle">Members of Group '${gn}'</div>
<table align="center" class="colored">
%if msg:
<tr><td><p class="ok_bgr">${msg}</p></td></tr>
%endif
<tr><td>&nbsp;</td></tr>
%if len( members ) == 0:
<tr><td>Group '${gn}' contains no members</td></tr>
%else:
<% ctr = 0 %>
%for member in members:
<% email = unescape( member[1], unentities ) %>
%if ctr % 2 == 1:
<tr class="odd_row">
%else:
<tr class="tr">
%endif
<td><a href="${h.url_for( controller='admin',
action='specified_users_groups',
user_id=member[0],
user_email=member[1] )}">${email}</a></td>
</tr>
<% ctr += 1 %>
%endfor
%endif
</table>
</div>
@@ -0,0 +1,114 @@
<%inherit file="/base.mako"/>
<%
from galaxy.web.controllers.admin import entities, unentities
from xml.sax.saxutils import escape, unescape
%>
<%def name="title()">Manage Group Membership</%def>
<div class="toolForm">
<div class="form-row">
<a href="${h.url_for( controller='admin', action='libraries' )}">Libraries</a>&nbsp;&nbsp;|&nbsp;&nbsp;
<a href="${h.url_for( controller='admin', action='groups' )}">Groups</a>&nbsp;&nbsp;|&nbsp;&nbsp;
<tr><td><a href="${h.url_for( controller='admin', action='users' )}">Users</a></td></tr>
</div>
<table align="center" class="colored">
%if msg:
<tr><td><p class="ok_bgr">${msg}</p></td></tr>
%endif
<% gn = unescape( group_name, unentities ) %>
<tr><td>&nbsp;</td></tr>
<tr>
<td>
<table border="0">
<form name="group_create" action="${h.url_for( controller='admin', action='update_group_members', group_id=group_id, group_name=group_name )}" method="post" >
%if len( users ) == 0:
<tr><td>There are no Galaxy users</td></tr>
%else:
<tr class="header"><td>Members of '${gn}' - Quick Find</td></tr>
<tr>
<td>
|<a href="#A">A</a>|<a href="#B">B</a>|<a href="#C">C</a>|<a href="#D">D</a>|<a href="#E">E</a>|<a href="#F">F</a>
|<a href="#G">G</a>|<a href="#H">H</a>|<a href="#I">I</a>|<a href="#J">J</a>|<a href="#K">K</a>|<a href="#L">L</a>
|<a href="#M">M</a>|<a href="#N">N</a>|<a href="#O">O</a>|<a href="#P">P</a>|<a href="#Q">Q</a>|<a href="#R">R</a>
|<a href="#S">S</a>|<a href="#T">T</a>|<a href="#U">U</a>|<a href="#V">V</a>|<a href="#W">W</a>|<a href="#X">X</a>
|<a href="#Y">Y</a>|<a href="#Z">Z</a>
</td>
</tr>
<tr>
<td>
<%
ctr = 0
anchors = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z']
anchor_loc = 0
anchored = False
curr_anchor = 'A'
%>
%for user in users:
<%
email = unescape( user[1], unentities )
check = False
%>
%for member in members:
<% member_email = unescape( member[1], unentities ) %>
%if email == member_email:
<%
check = True
break
%>
%endif
%endfor
%if not email.upper().startswith( curr_anchor ):
<% anchored = False %>
%endif
%if ctr % 2 == 1:
<tr class="odd_row">
%else:
<tr class="tr">
%endif
<td>
%if email.upper().startswith( curr_anchor ):
%if not anchored:
<a name="${curr_anchor}"><hr/></a><br/>
<% anchored = True %>
%endif
%if check:
<input type="checkbox" name="members" value="${user[0]}" checked/> ${email}
%else:
<input type="checkbox" name="members" value="${user[0]}"/> ${email}
%endif
%else:
%for anchor in anchors[ anchor_loc: ]:
%if email.upper().startswith( anchor ):
%if not anchored:
<a name="${anchor}"><hr/></a><br/>
<%
curr_anchor = anchor
anchored = True
%>
%endif
%if check:
<input type="checkbox" name="members" value="${user[0]}" checked/> ${email}
%else:
<input type="checkbox" name="members" value="${user[0]}"/> ${email}
%endif
<%
anchor_loc = anchors.index( anchor )
break
%>
%endif
%endfor
%endif
</td>
<% ctr += 1 %>
%endfor
</td>
</tr>
%endif
<tr><td><center><button name="action" value="update_group_members">Update Membership</button></center></td></tr>
</form>
</table>
</td>
</tr>
</table>
</div>
@@ -0,0 +1,67 @@
<%inherit file="/base.mako"/>
<%
from galaxy.web.controllers.admin import entities, unentities
from xml.sax.saxutils import escape, unescape
%>
<%def name="title()">Groups</%def>
<div class="toolForm">
<div class="form-row">
<a href="${h.url_for( controller='admin', action='libraries' )}">Libraries</a>&nbsp;&nbsp;|&nbsp;&nbsp;
<a href="${h.url_for( controller='admin', action='users' )}">Users</a>
</div>
<div class="form-row">
<a href="${h.url_for( controller='admin', action='create_group' )}">Create a new group</a>
<br/>
<a href="${h.url_for( controller='admin', action='deleted_groups' )}">Manage deleted groups</a>
<br/>
</div>
<h3 align="center">Groups</h3>
<table align="center" class="colored">
%if msg:
<tr><td colspan="5"><p class="ok_bgr">${msg}</p></td></tr>
%endif
%if len( groups ) == 0:
<tr><td colspan="5">There are no Galaxy groups</td></tr>
%else:
<tr class="header">
<td>Group</td>
<td>Priority</td>
<td>Members</td>
<td>Datasets</td>
<td>Group Permitted Actions on Datasets</td>
<td>&nbsp;</td>
</tr>
<% ctr = 0 %>
%for group in groups:
<% group_name = unescape( group[1], unentities ) %>
%if ctr % 2 == 1:
<tr class="odd_row">
%else:
<tr class="tr">
%endif
<td>${group_name}</td>
<td>${group[2]}</td>
<td><a href="${h.url_for( controller='admin', action='group_members', group_id=group[0], group_name=group[1] )}">${group[3]}</a></td>
%if group[4] > 0:
<td><a href="${h.url_for( controller='admin', action='group_dataset_permitted_actions', group_id=group[0], group_name=group[1] )}">${group[4]}</a></td>
%else:
<td>${group[4]}</td>
%endif
<td>
%if len( group[5] ) == 1:
${group[5][0]}
%elif len( group[5] ) > 1:
%for da in group[5]:
${da}<br/>
%endfor
%endif
</td>
<td><a href="${h.url_for( controller='admin', action='mark_group_deleted', group_id=group[0] )}">Mark group deleted</a></td>
</tr>
<% ctr += 1 %>
%endfor
%endif
</table>
</div>
@@ -0,0 +1,13 @@
<%inherit file="/base.mako"/>
<%def name="title()">Dataset Security</%def>
<div class="toolForm">
<div class="toolFormTitle">Dataset Security</div>
<table align="center" class="colored">
%if msg:
<tr><td><p class="ok_bgr">${msg}</p></td></tr>
%endif
<tr><td><a href="${h.url_for( controller='admin', action='groups' )}">Groups</a></td></tr>
<tr><td><a href="${h.url_for( controller='admin', action='users' )}">Users</a></td></tr>
</table>
</div>
@@ -0,0 +1,56 @@
<%inherit file="/base.mako"/>
<%
from galaxy.web.controllers.admin import entities, unentities
from xml.sax.saxutils import escape, unescape
%>
<% email = unescape( user_email, unentities ) %>
<%def name="title()">Create Group</%def>
<div class="toolForm">
<div class="form-row">
<a href="${h.url_for( controller='admin', action='libraries' )}">Libraries</a>&nbsp;&nbsp;|&nbsp;&nbsp;
<a href="${h.url_for( controller='admin', action='groups' )}">Groups</a>&nbsp;&nbsp;|&nbsp;&nbsp;
<tr><td><a href="${h.url_for( controller='admin', action='users' )}">Users</a></td></tr>
</div>
<h3 align="center">Groups of which '${email}' is a member</h3>
<table align="center" class="colored">
%if msg:
<tr><td colspan="4"><p class="ok_bgr">${msg}</p></td></tr>
%endif
%if len( groups ) == 0:
<tr><td colspan="4">User '${email}' belongs to no groups</td></tr>
%else:
<tr class="header">
<td>Group</td>
<td>Priority</td>
<td>Datasets</td>
<td>Permitted Actions on Datasets</td>
</tr>
<% ctr = 0 %>
%for group in groups:
<% gn = unescape( group[1], unentities ) %>
%if ctr % 2 == 1:
<tr class="odd_row">
%else:
<tr class="tr">
%endif
<td>${gn}</td>
<td>${group[2]}</td>
%if group[3] > 0:
<td><a href="${h.url_for( controller='admin', action='group_dataset_permitted_actions', group_id=group[0], group_name=group[1] )}">${group[3]}</a></td>
%else:
<td>${group[3]}</td>
%endif
<td>
%for da in group[4]:
${da}<br/>
%endfor
</td>
</tr>
<% ctr += 1 %>
%endfor
%endif
</table>
</div>
@@ -0,0 +1,80 @@
<%inherit file="/base.mako"/>
<%
from galaxy.web.controllers.admin import entities, unentities
from xml.sax.saxutils import escape, unescape
%>
<%def name="title()">Users</%def>
<div class="toolForm">
<div class="form-row">
<a href="${h.url_for( controller='admin', action='groups' )}">Groups</a>&nbsp;&nbsp;|&nbsp;&nbsp;
<a href="${h.url_for( controller='admin', action='libraries' )}">Libraries</a>
</div>
<table align="center" class="colored">
%if msg:
<tr><td><p class="ok_bgr">${msg}</p></td></tr>
%endif
<tr><td>&nbsp;</td>
%if len( users ) == 0:
<tr><td>There are no Galaxy users</td></tr>
%else:
<tr class="header"><td><center><a name="TOP">Galaxy Users - Quick Find</a></td></center></tr>
<tr>
<td>
|<a href="#A">A</a>|<a href="#B">B</a>|<a href="#C">C</a>|<a href="#D">D</a>|<a href="#E">E</a>|<a href="#F">F</a>
|<a href="#G">G</a>|<a href="#H">H</a>|<a href="#I">I</a>|<a href="#J">J</a>|<a href="#K">K</a>|<a href="#L">L</a>
|<a href="#M">M</a>|<a href="#N">N</a>|<a href="#O">O</a>|<a href="#P">P</a>|<a href="#Q">Q</a>|<a href="#R">R</a>
|<a href="#S">S</a>|<a href="#T">T</a>|<a href="#U">U</a>|<a href="#V">V</a>|<a href="#W">W</a>|<a href="#X">X</a>
|<a href="#Y">Y</a>|<a href="#Z">Z</a>
</td>
</tr>
<%
ctr = 0
anchors = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z']
anchor_loc = 0
anchored = False
curr_anchor = 'A'
%>
%for user in users:
<% email = unescape( user[1], unentities ) %>
%if not email.upper().startswith( curr_anchor ):
<% anchored = False %>
%endif
%if ctr % 2 == 1:
<tr class="odd_row">
%else:
<tr class="tr">
%endif
<td>
%if email.upper().startswith( curr_anchor ):
%if not anchored:
<a name="${curr_anchor}"><hr/></a><br/>
<% anchored = True %>
%endif
<a href="${h.url_for( controller='admin', action='specified_users_groups', user_id=user[0], user_email=user[1] )}">${email}</a>
%else:
%for anchor in anchors[ anchor_loc: ]:
%if email.upper().startswith( anchor ):
%if not anchored:
<a name="${anchor}"><hr/></a><br/>
<%
curr_anchor = anchor
anchored = True
%>
%endif
<a href="${h.url_for( controller='admin', action='specified_users_groups', user_id=user[0], user_email=user[1] )}">${email}</a>
<%
anchor_loc = anchors.index( anchor )
break
%>
%endif
%endfor
%endif
</td>
<% ctr += 1 %>
%endfor
</tr>
%endif
</table>
</div>
+13
View File
@@ -0,0 +1,13 @@
<%inherit file="/base.mako"/>
<div class="body">
<h3 align="center">Galaxy Administration</h3>
<table align="center" class="colored">
%if msg:
<tr><td><p class="ok_bgr">${msg}</p></td></tr>
%endif
<tr><td><a href="${h.url_for( controller='admin', action='dataset_security' )}">Dataset Security</a></td></tr>
<tr><td><a href="${h.url_for( controller='admin', action='libraries' )}">Libraries</a></td></tr>
<tr><td><a href="${h.url_for( controller='admin', action='reload_tool' )}">Reload a tool while the Galaxy server is running</a></td></tr>
</table>
</div>
+123
View File
@@ -0,0 +1,123 @@
<%inherit file="/base.mako"/>
<%def name="title()">Edit Dataset Attributes</%def>
<%def name="datatype( dataset, datatypes )">
<select name="datatype">
## $datatypes.sort()
%for ext in datatypes:
%if dataset.ext == ext:
<option value="${ext}" selected="yes">${ext}</option>
%else:
<option value="${ext}">${ext}</option>
%endif
%endfor
</select>
</%def>
<%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:
<input type="checkbox" name="actions" value="${gda[0]},${da}" checked/>
%else:
<input type="checkbox" name="actions" value="${gda[0]},${da}"/>
%endif
${da}<br/>
%endfor
</%def>
<div class="toolForm">
<div class="toolFormTitle">Group Associations</div>
<div class="toolFormBody">
<form name="edit_group_associations" action="${h.url_for( controller='admin', action='dataset' )}" method="post">
<input type="hidden" name="id" value="${dataset.id}">
%for gda in gdas:
<div class="form-row"><label>Group:</label>${gda[1]}</div>
<div class="form-row"><label>Permitted actions on dataset:</label></div>
<div class="form-row">
${group_dataset_permitted_actions( dataset_actions, gda )}
</div>
%endfor
<div class="form-row"><input type="submit" name="change_permitted_actions" value="Save"></div>
</form>
</div>
</div>
<div class="toolForm">
<div class="toolFormTitle">Edit Attributes</div>
<div class="toolFormBody">
<form name="edit_attributes" action="${h.url_for( controller='admin', action='dataset' )}" method="post">
<input type="hidden" name="id" value="${dataset.id}">
<div class="form-row">
<label>Name:</label>
<div style="float: left; width: 250px; margin-right: 10px;">
<input type="text" name="name" value="${dataset.name}" size="40">
</div>
<div style="clear: both"></div>
</div>
<div class="form-row">
<label>Info:</label>
<div style="float: left; width: 250px; margin-right: 10px;">
<input type="text" name="info" value="${dataset.info}" size="40">
</div>
<div style="clear: both"></div>
</div>
%for element in metadata:
<div class="form-row">
<label>${element.spec.desc}:</label>
<div style="float: left; width: 250px; margin-right: 10px;">
${element.get_html()}
</div>
<div style="clear: both"></div>
</div>
%endfor
<div class="form-row">
<input type="submit" name="save" value="Save">
</div>
</form>
<form name="auto_detect" action="${h.url_for( controller='admin', action='dataset' )}" method="post">
<input type="hidden" name="id" value="${dataset.id}">
<div style="float: left; width: 250px; margin-right: 10px;">
<input type="submit" name="detect" value="Auto-detect">
</div>
<div class="toolParamHelp" style="clear: both;">
This will inspect the dataset and attempt to correct the above column values
if they are not accurate.
</div>
</form>
</div>
</div>
<p/>
<div class="toolForm">
<div class="toolFormTitle">Change data type</div>
<div class="toolFormBody">
<form name="change_datatype" action="${h.url_for( controller='admin', action='dataset' )}" method="post">
<input type="hidden" name="id" value="${dataset.id}">
<div class="form-row">
<label>New Type:</label>
<div style="float: left; width: 250px; margin-right: 10px;">
${datatype( dataset, datatypes )}
</div>
<div class="toolParamHelp" style="clear: both;">
This will change the datatype of the existing dataset
but <i>not</i> modify its contents. Use this if Galaxy
has incorrectly guessed the type of your dataset.
</div>
<div style="clear: both"></div>
</div>
<div class="form-row">
<input type="submit" name="change" value="Save">
</div>
</form>
</div>
</div>
<a href="${h.url_for( controller='admin', action='folder', id=dataset.folder.id )}">manage containing folder</a>
<p/>
+158
View File
@@ -0,0 +1,158 @@
<%inherit file="/base.mako"/>
<%def name="render_component( component )">
<%
if isinstance( component, trans.app.model.LibraryFolder ):
return render_folder( component )
elif isinstance( component, trans.app.model.LibraryFolderDatasetAssociation ):
return render_dataset( component )
%>
</%def>
## Render the dataset `data` as history item, using `hid` as the displayed id
<%def name="render_dataset( data )">
<%
if data.state in ['no state','',None]:
data_state = "queued"
else:
data_state = data.state
%>
<div class="toolForm">
<div class="toolFormTitle">${data.display_name()}</div>
<div class="toolFormBody">
<div class="form-row">
## Header row for history items (name, state, action buttons)
<div style="overflow: hidden;" class="historyItemTitleBar">
%if data_state != 'ok':
<div><img src="${h.url_for( "/static/style/data_%s.png" % data_state )}" border="0" align="middle"></div>
%endif
</div>
<div style="float: right;">
<a href="${h.url_for( controller='admin',
action='dataset',
id=data.id )}"><img src="${h.url_for('/static/images/pencil_icon.png')}"
rollover="${h.url_for('/static/images/pencil_icon_dark.png')}"
width='16'
height='16'
alt='edit attributes'
title='edit attributes'
class='editButton'
border='0'></a>
</div>
##<span class="historyItemTitle"><b>${data.display_name()}</b></span>
</div>
## Body for history items, extra info and actions, data "peek"
<div id="info${data.id}" class="historyItemBody">
%if data_state == "queued":
<div>Job is waiting to run</div>
%elif data_state == "running":
<div>Job is currently running</div>
%elif data_state == "error":
<div>
An error occurred running this job: <i>${data.display_info().strip()}</i>,
<a href="${h.url_for( controller='dataset', action='errors', id=data.id )}" target="galaxy_main">report this error</a>
</div>
%elif data_state == "empty":
<div>No data: <i>${data.display_info()}</i></div>
%elif data_state == "ok":
<div>
${data.blurb},
format: <span class="${data.ext}">${data.ext}</span>,
database:
%if data.dbkey == '?':
<a href="${h.url_for( controller='admin', action='dataset', id=data.id )}">${data.dbkey}</a>
%else:
<span class="${data.dbkey}">${data.dbkey}</span>
%endif
</div>
<div class="info">Info: ${data.display_info()} </div>
%if data.peek != "no peek":
<div><pre id="peek${data.id}">${data.display_peek()}</pre></div>
%endif
%else:
<div>Error: unknown dataset state "${data_state}".</div>
%endif
## Recurse for child datasets
</div>
</div>
</div>
</%def>
## Render a folder
<%def name="render_folder( this_folder )">
<div class="toolForm">
<div class="toolFormTitle">Contents of Folder: ${this_folder.name}</div>
<div class="toolFormBody">
<div class="form-row">
<%
components = list( this_folder.folders ) + list( this_folder.datasets )
components = [ ( getattr( components[i], "order_id" ), i, components [i] ) for i in xrange( len( components ) ) ]
components.sort()
components = [ tup[-1] for tup in components ]
%>
%for component in components:
${render_component( component )}
%endfor
</div>
<div style="clear: both"></div>
<div class="form-row">
<a href="${h.url_for( controller='admin', action='dataset', folder_id=this_folder.id )}">Add new dataset to folder '${this_folder.name}'</a>
<div style="clear: both"></div>
<a href="${h.url_for( controller='admin', action='folder', parent_id=this_folder.id )}">Add new folder to folder '${this_folder.name}'</a>
</div>
<div style="clear: both"></div>
</div>
</div>
</%def>
<%def name="title()">Manage Folder: ${folder.name}</%def>
<div class="toolForm">
<div class="form-row">
<a href="${h.url_for( controller='admin', action='libraries' )}">Libraries</a>&nbsp;&nbsp;|&nbsp;&nbsp;
<a href="${h.url_for( controller='admin', action='groups' )}">Groups</a>&nbsp;&nbsp;|&nbsp;&nbsp;
<tr><td><a href="${h.url_for( controller='admin', action='users' )}">Users</a></td></tr>
</div>
<div class="toolFormTitle">Change Folder Attributes</div>
<div class="toolFormBody">
<form name="rename_folder" action="folder" method="post" >
<div class="form-row">
<label>Name:</label>
<div style="float: left; width: 250px; margin-right: 10px;">
<input type="text" name="name" value="${folder.name}" size="40">
</div>
<div style="clear: both"></div>
</div>
<div class="form-row">
<label>Description:</label>
<div style="float: left; width: 250px; margin-right: 10px;">
<input type="text" name="description" value="${folder.description}" size="40">
</div>
<div style="clear: both"></div>
</div>
<div class="form-row">
<div style="float: left; width: 250px; margin-right: 10px;">
<input type="hidden" name="rename_folder" value="rename_folder">
</div>
<div style="float: left; width: 250px; margin-right: 10px;">
<input type="hidden" name="id" value="${folder.id}">
</div>
</div>
<input type="submit" value="Save">
</form>
</div>
</div>
<div style="clear: both"></div>
<div class="toolForm">
<div class="toolFormTitle">Manage Folder Contents: ${folder.name}</div>
<div class="toolFormBody">
<div class="form-row">
%if folder.parent:
<a href="${h.url_for( controller='admin', action='folder', id=folder.parent_id )}">Up a Level</a>
%elif folder.library_root:
<a href="${h.url_for( controller='admin', action='library', id=folder.library_root[0].id )}">Manage Library</a>
%endif
</div>
<div style="clear: both"></div>
<div class="form-row">
${render_folder( folder )}
</div>
<div style="clear: both"></div>
</div>
</div>
+19
View File
@@ -0,0 +1,19 @@
<%inherit file="/base.mako"/>
<%def name="title()">Libraries</%def>
<div class="toolForm">
<div class="form-row">
<a href="${h.url_for( controller='admin', action='groups' )}">Groups</a>&nbsp;&nbsp;|&nbsp;&nbsp;
<a href="${h.url_for( controller='admin', action='users' )}">Users</a>
</div>
<div class="form-row"><a href="${h.url_for( controller='admin', action='library' )}">Create a new library</a></div>
<br/>
<div class="toolFormTitle">Galaxy Libraries</div>
<div class="toolFormBody">
%for library in libraries:
<div class="form-row">
<a href="${h.url_for( controller='admin', action='library', id=library.id )}">${library.name}</a>
</div>
%endfor
</div>
</div>
+35
View File
@@ -0,0 +1,35 @@
<%inherit file="/base.mako"/>
<%def name="title()">Library</%def>
<div class="toolForm">
<div class="form-row">
<a href="${h.url_for( controller='admin', action='libraries' )}">Libraries</a>&nbsp;&nbsp;|&nbsp;&nbsp;
<a href="${h.url_for( controller='admin', action='groups' )}">Groups</a>&nbsp;&nbsp;|&nbsp;&nbsp;
<a href="${h.url_for( controller='admin', action='users' )}">Users</a>
</div>
<div class="toolFormTitle">Manage Library '${library.name}'</div>
<div class="toolFormBody">
<form name="end_library" action="${h.url_for( controller='admin', action='library' )}" method="post" >
<input type="hidden" name="library_id" value="${library.id}">
<div class="form-row">
<label>Name:</label>
<div style="float: left; width: 250px; margin-right: 10px;">
<input type="text" name="name" value="${library.name}" size="40">
</div>
<div style="clear: both"></div>
</div>
<div class="form-row">
<label>Description:</label>
<div style="float: left; width: 250px; margin-right: 10px;">
<input type="text" name="description" value="${library.description}" size="40">
</div>
<div style="clear: both"></div>
</div>
<table>
<tr><td>&nbsp;</td><td><input type="submit" value="Save"></td></tr>
</table>
</form>
</div>
<div style="float: left; width: 250px; margin-right: 10px;">
<a href="${h.url_for( 'folder', id = library.root_folder.id )}">Manage Root Folder</a>
</div>
+82
View File
@@ -0,0 +1,82 @@
<%inherit file="/base.mako"/>
<%def name="title()">Create New Library Dataset</%def>
<div class="toolForm" id="new_dataset">
<div class="form-row">
<a href="${h.url_for( controller='admin', action='libraries' )}">Libraries</a>&nbsp;&nbsp;|&nbsp;&nbsp;
<a href="${h.url_for( controller='admin', action='groups' )}">Groups</a>&nbsp;&nbsp;|&nbsp;&nbsp;
<tr><td><a href="${h.url_for( controller='admin', action='users' )}">Users</a></td></tr>
</div>
<div class="toolFormTitle">Create a new Library Dataset</div>
<div class="toolFormBody">
<form name="tool_form" action="${h.url_for( controller='admin', action='dataset' )}" enctype="multipart/form-data" method="post">
<input type="hidden" name="folder_id" value="${folder_id}">
<div class="form-row">
<label>File:</label>
<div style="float: left; width: 250px; margin-right: 10px;"><input type="file" name="file_data"></div>
<div style="clear: both"></div>
</div>
<div class="form-row">
<label>URL/Text:</label>
<div style="float: left; width: 250px; margin-right: 10px;"><textarea name="url_paste" rows="5" cols="35"></textarea></div>
<div class="toolParamHelp" style="clear: both;">
Here you may specify a list of URLs (one per line) or paste the contents of a file.
</div>
<div style="clear: both"></div>
</div>
<div class="form-row">
<label>Convert spaces to tabs:</label>
<div style="float: left; width: 250px; margin-right: 10px;"><div><input type="checkbox" name="space_to_tab" value="Yes">Yes</div></div>
<div class="toolParamHelp" style="clear: both;">
Use this option if you are entering intervals by hand.
</div>
<div style="clear: both"></div>
</div>
<div class="form-row">
<label>File Format:</label>
<div style="float: left; width: 250px; margin-right: 10px;">
<select name="extension">
<option value="auto" selected>Auto-detect</option>
%for file_format in file_formats:
<option value="${file_format}">${file_format}</option>
%endfor
</select>
</div>
<div class="toolParamHelp" style="clear: both;">
Which format? See help below
</div>
<div style="clear: both"></div>
</div>
<div class="form-row">
<label>Genome:</label>
<div style="float: left; width: 250px; margin-right: 10px;">
<select name="dbkey">
%for dbkey in dbkeys:
%if dbkey[1] == last_used_build:
<option value="${dbkey[1]}" selected>${dbkey[0]}</option>
%else:
<option value="${dbkey[1]}">${dbkey[0]}</option>
%endif
%endfor
</select>
</div>
<div style="clear: both"></div>
</div>
<div class="form-row">
<label>Associate with Groups:</label>
<help>Multi-select list - hold the appropriate key while clicking to select multiple columns</help>
<div style="float: left; width: 250px; margin-right: 10px;">
<select name="groups" multiple="true" size="5">
%for group in groups:
<option value="${group[0]}">${group[1]}</option>
%endfor
</select>
</div>
</div>
<div style="clear: both"></div>
<div class="form-row">
<input type="submit" class="primary-button" name="create_dataset" value="Add Dataset to Folder">
</div>
</form>
</div>
</div>
+28
View File
@@ -0,0 +1,28 @@
<%inherit file="/base.mako"/>
<div class="body">
<h3 align="center">Reload a Tool</h3>
<table align="center" class="colored">
%if msg:
<tr><td><p class="ok_bgr">${msg}</p></td></tr>
%endif
<tr>
<td>
<form name="tool_reload" action="${h.url_for( controller='admin', action='tool_reload' )}" method="post" >
<p>
Reload tool:
<select name="tool_id">
%for i, section in enumerate( toolbox.sections ):
<optgroup label="${section.name}">
%for t in section.tools:
<option value="${t.id}">${t.name}</option>
%endfor
%endfor
</select>
<button name="action" value="tool_reload">Reload</button>
</p>
</form>
</td>
</tr>
</table>
</div>
-33
View File
@@ -1,33 +0,0 @@
<%inherit file="/base.mako"/>
<%def name="title()">Galaxy Administration</%def>
<table align="center" width="70%" class="border" cellpadding="5" cellspacing="5">
<tr>
<td>
<h3 align="center">Galaxy Administration</h3>
%if msg:
<p class="ok_bgr">${msg}</p>
%endif
</td>
</tr>
<tr>
<td>
<form method="post" action="admin">
<p>Admin password: <input type="password" name="passwd" size="8"></p>
<p>
Reload tool:
<select name="tool_id">
%for i, section in enumerate( toolbox.sections ):
<optgroup label="${section.name}">
%for t in section.tools:
<option value="${t.id}">${t.name}</option>
%endfor
%endfor
</select>
<button name="action" value="tool_reload">Reload</button>
</p>
</form>
</td>
</tr>
</table>
+34 -1
View File
@@ -1,5 +1,5 @@
<%inherit file="/base.mako"/>
<%def name="title()">Your saved histories</%def>
<%def name="title()">Edit Dataset Attributes</%def>
<%def name="datatype( dataset, datatypes )">
@@ -130,3 +130,36 @@
</form>
</div>
</div>
<p />
%if trans.app.config.enable_beta_features and trans.user and ( trans.app.security_agent.allow_action( trans.user, data.permitted_actions.DATASET_MANAGE_PERMISSIONS, dataset = data ) ):
<div class="toolForm">
<div class="toolFormTitle">Change Permitted Actions</div>
<div class="toolFormBody">
<form name="change_permission_form" action="${h.url_for( action='edit' )}" method="post">
<input type="hidden" name="id" value="${data.id}">
<div class="form-row">
<label>
Private Dataset:
</label>
<% checked = "" %>
%if not trans.app.security_agent.dataset_has_group( data.id, trans.app.model.Group.get_public_group().id ):
<% checked = " checked" %>
%endif
<div style="float: left; width: 250px; margin-right: 10px;">
<input type="checkbox" name="private_dataset"${checked}>
</div>
<div style="clear: both"></div>
<div class="toolParamHelp" style="clear: both;">
This will prevent other users from viewing or utilizing this dataset, even if you share your history with them.
</div>
<div style="clear: both"></div>
</div>
<div class="form-row">
<input type="submit" name="change_permission" value="Save">
</div>
</form>
</div>
</div>
%endif
+2
View File
@@ -21,9 +21,11 @@ $(function(){
cls += " form-row-error"
%>
<div class="${cls}">
%if input.use_label:
<label>
${input.label}:
</label>
%endif
<div style="float: left; width: 250px; margin-right: 10px;">
<input type="${input.type}" name="${input.name}" value="${input.value}" size="40">
</div>
+1
View File
@@ -18,6 +18,7 @@
%endif
%if app.config.enable_beta_features:
<li><a href="${h.url_for( controller='workflow', action='build_from_current_history' )}">Construct workflow</a> from the current history</li>
<li><a href="${h.url_for( action='history_set_default_permitted_actions' )}">Change default permitted actions</a> for the current history</li>
%endif
<li><a href="${h.url_for( action='history_share' )}" target="galaxy_main">Share</a> current history</div>
%endif
+42
View File
@@ -0,0 +1,42 @@
<%inherit file="/base.mako"/>
<%def name="title()">Change Default History Permitted Actions</%def>
%if trans.user:
<div class="toolForm">
<div class="toolFormTitle">Change Default History Permitted Actions</div>
<div class="toolFormBody">
<form name="set_permitted_actions" method="post">
<div class="form-row">
<% user_groups = [ assoc.group for assoc in trans.user.groups ] %>
<% cur_groups = [ assoc.group for assoc in trans.get_history().default_groups ] %>
<div style="float: left; width: 250px; margin-right: 10px;">
<table>
<tr><th>Group</th><th>In</th><th>Out</th></tr>
%for group in user_groups:
<tr><td>${group.name}</td><td><input type="radio" name="group_${group.id}" value="in"
%if group in cur_groups:
checked
%endif
></td><td><input type="radio" name="group_${group.id}" value="out"
%if group not in cur_groups:
checked
%endif
></td></tr>
%endfor
</table>
</div>
<div style="clear: both"></div>
<div class="toolParamHelp" style="clear: both;">
This will change the default permitted actions assigned to new datasets for your current history.
</div>
<div style="clear: both"></div>
</div>
<div class="form-row">
<input type="submit" name="set_permitted_actions" value="Save">
</div>
</form>
</div>
</div>
%endif
+13
View File
@@ -0,0 +1,13 @@
<%inherit file="/base.mako"/>
<%def name="title()">Libraries You Can Access</%def>
<div class="toolForm">
<div class="toolFormTitle">Libraries You Can Access</div>
<div class="toolFormBody">
%for library in libraries:
<div class="form-row">
<a href="${h.url_for( '/library/index', library_id=library.id )}">${library.name}</a>
</div>
%endfor
</div>
</div>
+69
View File
@@ -0,0 +1,69 @@
<%inherit file="/base.mako"/>
<%def name="render_component( component )">
<%
if isinstance( component, trans.app.model.LibraryFolder ):
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 ):
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 )
%>
</%def>
## Render the dataset `data` as history item, using `hid` as the displayed id
<%def name="render_dataset( data )">
<div>
<input type="checkbox" name="import_ids" value="${data.id}">${data.name}
<div>
</%def>
## Render a folder
<%def name="render_folder( this_folder )">
<div>
Folder: ${this_folder.name}
<%
components = list( this_folder.folders ) + list( this_folder.datasets )
components = [ ( getattr( components[i], "order_id" ), i, components [i] ) for i in xrange( len( components ) ) ]
components.sort()
components = [ tup[-1] for tup in components ]
%>
<blockquote>
%for component in components:
${render_component( component )}
%endfor
</blockquote>
</div>
</%def>
<%def name="title()">View Library: ${library.name}</%def>
<div class="toolForm">
<div class="toolFormTitle">Import from Library: ${library.name}</div>
<div class="toolFormBody">
<form name="view_library" action="${h.url_for( '/library/index' )}" method="post">
${render_folder( library.root_folder )}
<div style="clear: both"></div>
<input type="submit" class="primary-button" name="import_dataset" value="Import Datasets">
</form>
</div>
</div>
+4 -2
View File
@@ -32,7 +32,9 @@
## Body for history items, extra info and actions, data "peek"
<div id="info${data.id}" class="historyItemBody">
%if data_state == "queued":
%if not trans.app.security_agent.allow_action( trans.user, data.permitted_actions.DATASET_ACCESS, dataset = data.dataset ):
<div>You do not have permission to view this dataset.</div>
%elif data_state == "queued":
<div>Job is waiting to run</div>
%elif data_state == "running":
<div>Job is currently running</div>
@@ -100,4 +102,4 @@
</div>
</div>
</%def>
</%def>
+3
View File
@@ -20,6 +20,9 @@
| <a target="_blank" href="${wiki_url}">wiki</a>
| <a target="_blank" href="${screencasts_url}">screencasts</a>
| <a target="_blank" href="${blog_url}">blog</a>
%if admin_user == "true":
| <a target="galaxy_main" href="${h.url_for( controller='admin', action='index' )}">admin</a>
%endif
<!-- | <a target="mainframe" href="/static/index_frame_tools.html">tools</a>
| <a target="mainframe" href="/static/index_frame_history.html">history</a> -->
&nbsp;&nbsp;&nbsp;
+3
View File
@@ -8,6 +8,9 @@
<ul>
<li><a href="${h.url_for( action='change_password' )}">Change your password</a></li>
<li><a href="${h.url_for( action='change_email' )}">Update your email address</a></li>
%if app.config.enable_beta_features:
<li><a href="${h.url_for( action='set_default_permitted_actions' )}">Change default permitted actions</a> for new histories</li>
%endif
<li><a href="${h.url_for( action='logout' )}">Logout</a></li>
</ul>
%else:
+42
View File
@@ -0,0 +1,42 @@
<%inherit file="/base.mako"/>
<%def name="title()">Change Default History Permitted Actions</%def>
%if trans.user:
<div class="toolForm">
<div class="toolFormTitle">Change Default Permitted Actions for new Histories </div>
<div class="toolFormBody">
<form name="set_permitted_actions" method="post">
<div class="form-row">
<% user_groups = [ assoc.group for assoc in trans.user.groups ] %>
<% cur_groups = [ assoc.group for assoc in trans.user.default_groups ] %>
<div style="float: left; width: 250px; margin-right: 10px;">
<table>
<tr><th>Group</th><th>In</th><th>Out</th></tr>
%for group in user_groups:
<tr><td>${group.name}</td><td><input type="radio" name="group_${group.id}" value="in"
%if group in cur_groups:
checked
%endif
></td><td><input type="radio" name="group_${group.id}" value="out"
%if group not in cur_groups:
checked
%endif
></td></tr>
%endfor
</table>
</div>
<div style="clear: both"></div>
<div class="toolParamHelp" style="clear: both;">
This will change the default permitted actions assigned to new datasets for new histories.
</div>
<div style="clear: both"></div>
</div>
<div class="form-row">
<input type="submit" name="set_permitted_actions" value="Save">
</div>
</form>
</div>
</div>
%endif
+1
View File
@@ -12,6 +12,7 @@
<tool file="data_source/encode_db.xml" />
<tool file="data_source/hbvar.xml" />
<tool file="validation/fix_errors.xml" />
<tool file="data_source/access_libraries.xml" />
</section>
<section name="Get ENCODE Data" id="encode">
<tool file="data_source/encode_import_chromatin_and_chromosomes.xml"/>
+7
View File
@@ -0,0 +1,7 @@
<?xml version="1.0"?>
<tool name="Access Libraries" id="library_access1">
<description>stored locally</description>
<inputs action="library/index" method="get">
<param name="bogus_param" type="hidden" value="needed" />
</inputs>
</tool>
+4 -1
View File
@@ -5,7 +5,8 @@ from shutil import copyfile
#post processing, set build for data and add additional data to history
def exec_after_process(app, inp_data, out_data, param_dict, tool, stdout, stderr):
history = out_data.items()[0][1].history
base_dataset = out_data.items()[0][1]
history = base_dataset.history
if history == None:
print "unknown history!"
return
@@ -37,6 +38,8 @@ def exec_after_process(app, inp_data, out_data, param_dict, tool, stdout, stderr
newdata.extension = file_type
newdata.name = basic_name + " (" + description + ")"
history.add_dataset( newdata )
#TODO, Nate: Make sure the following is functionally correct
app.security_agent.set_dataset_groups( newdata.dataset, base_dataset.dataset.groups )
app.model.flush()
try:
copyfile(filepath,newdata.file_name)
+4 -1
View File
@@ -84,7 +84,8 @@ from galaxy import datatypes, config, jobs
from shutil import copyfile
def exec_after_process(app, inp_data, out_data, param_dict, tool, stdout, stderr):
history = out_data.items()[0][1].history
base_dataset = out_data.items()[0][1]
history = base_dataset.history
if history == None:
print "unknown history!"
return
@@ -128,6 +129,8 @@ def exec_after_process(app, inp_data, out_data, param_dict, tool, stdout, stderr
newdata.extension = file_type
newdata.name = basic_name + " (" + microbe_info[kingdom][org]['chrs'][chr]['data'][description]['feature'] +" for "+microbe_info[kingdom][org]['name']+":"+chr + ")"
newdata.flush()
#TODO, Nate: Make sure the following is functionally correct
app.security_agent.set_dataset_groups( newdata.dataset, base_dataset.dataset.groups )
history.add_dataset( newdata )
app.model.flush()
try:
+2 -1
View File
@@ -27,12 +27,13 @@ def exec_after_process(app, inp_data, out_data, param_dict, tool, stdout, stderr
fields = line.split("\t")
dbkey = fields[1]
filepath = fields[2]
newdata = app.model.HistoryDatasetAssociation( create_dataset = True )
newdata.extension = "bed"
newdata.name = basic_name + " (" + dbkey + ")"
newdata.flush()
history.add_dataset( newdata )
#TODO, Nate: Make sure the following is functionally correct
app.security_agent.set_dataset_groups( newdata.dataset, output_data.dataset.groups )
newdata.flush()
history.flush()
app.model.flush()
+2 -2
View File
@@ -77,8 +77,8 @@ use_lint = false
# NEVER enable this on a public site (even test or QA)
use_interactive = true
# Admin Password
admin_pass = galaxy
# Admin Users - this should be a comma-separated list of valid Galaxy users
#admin_users = user1@bx.psu.edu,user2@bx.psu.edu
# path to sendmail
sendmail_path = /usr/sbin/sendmail