mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-09-24 16:30:27 +08:00
Merged heads from central and this repo
This commit is contained in:
@@ -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 )
|
||||
|
||||
@@ -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"
|
||||
|
||||
+177
-106
@@ -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,10 +103,174 @@ class JobToOutputDatasetAssociation( object ):
|
||||
self.name = name
|
||||
self.dataset = dataset
|
||||
|
||||
class GroupDatasetAssociation( object ):
|
||||
dataset_actions = RBACAgent.permitted_actions.dataset_actions
|
||||
group_actions = RBACAgent.permitted_actions.group_actions
|
||||
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.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 )
|
||||
|
||||
class Group( object ):
|
||||
public_id = None
|
||||
permitted_actions = GroupDatasetAssociation.group_actions
|
||||
def __init__( self, name = None, priority = 0 ):
|
||||
self.name = name
|
||||
self.priority = priority
|
||||
@classmethod
|
||||
def get_public_group( cls ):
|
||||
# TODO, Nate: Make sure this method is functionally correct.
|
||||
return Group.get( cls.public_id )
|
||||
@classmethod
|
||||
def set_public_group( cls, group ):
|
||||
# TODO, Nate: Make sure this method is functionally correct.
|
||||
#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 ):
|
||||
# TODO, Nate: Make sure this method is functionally correct.
|
||||
#retrieve from database and store public group id, assume first created group is public
|
||||
cls.set_public_group( Group.select_by( name = 'public' ) )
|
||||
|
||||
class UserGroupAssociation( object ):
|
||||
def __init__( self, user, group ):
|
||||
self.user = user
|
||||
self.group = group
|
||||
|
||||
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
|
||||
|
||||
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',
|
||||
QUEUED = 'queued',
|
||||
RUNNING = 'running',
|
||||
OK = 'ok',
|
||||
EMPTY = 'empty',
|
||||
ERROR = 'error',
|
||||
DISCARDED = 'discarded' )
|
||||
permitted_actions = GroupDatasetAssociation.dataset_actions
|
||||
file_path = "/tmp/"
|
||||
engine = None
|
||||
def __init__( self, id=None, state=None, external_filename=None, extra_files_path=None, file_size=None, purgable=True ):
|
||||
self.id = id
|
||||
self.state = state
|
||||
self.deleted = False
|
||||
self.purged = False
|
||||
self.purgable = purgable
|
||||
self.external_filename = external_filename
|
||||
self._extra_files_path = extra_files_path
|
||||
self.file_size = file_size
|
||||
|
||||
def get_file_name( self ):
|
||||
if not self.external_filename:
|
||||
assert self.id is not None, "ID must be set before filename used (commit the object)"
|
||||
# First try filename directly under file_path
|
||||
filename = os.path.join( self.file_path, "dataset_%d.dat" % self.id )
|
||||
# Only use that filename if it already exists (backward compatibility),
|
||||
# otherwise construct hashed path
|
||||
if not os.path.exists( filename ):
|
||||
dir = os.path.join( self.file_path, *directory_hash_id( self.id ) )
|
||||
# Create directory if it does not exist
|
||||
try:
|
||||
os.makedirs( dir )
|
||||
except OSError, e:
|
||||
# File Exists is okay, otherwise reraise
|
||||
if e.errno != errno.EEXIST:
|
||||
raise
|
||||
# Return filename inside hashed directory
|
||||
return os.path.abspath( os.path.join( dir, "dataset_%d.dat" % self.id ) )
|
||||
else:
|
||||
filename = self.external_filename
|
||||
# Make filename absolute
|
||||
return os.path.abspath( filename )
|
||||
|
||||
def set_file_name ( self, filename ):
|
||||
if not filename:
|
||||
self.external_filename = None
|
||||
else:
|
||||
self.external_filename = filename
|
||||
|
||||
file_name = property( get_file_name, set_file_name )
|
||||
|
||||
@property
|
||||
def extra_files_path( self ):
|
||||
if self._extra_files_path:
|
||||
path = self._extra_files_path
|
||||
else:
|
||||
path = os.path.join( self.file_path, "dataset_%d_files" % self.id )
|
||||
#only use path directly under self.file_path if it exists
|
||||
if not os.path.exists( path ):
|
||||
path = os.path.join( os.path.join( self.file_path, *directory_hash_id( self.id ) ), "dataset_%d_files" % self.id )
|
||||
# Make path absolute
|
||||
return os.path.abspath( path )
|
||||
|
||||
def get_size( self ):
|
||||
"""Returns the size of the data on disk"""
|
||||
if self.file_size:
|
||||
return self.file_size
|
||||
else:
|
||||
try:
|
||||
return os.path.getsize( self.file_name )
|
||||
except OSError:
|
||||
return 0
|
||||
def set_size( self ):
|
||||
"""Returns the size of the data on disk"""
|
||||
try:
|
||||
self.file_size = os.path.getsize( self.file_name )
|
||||
except OSError:
|
||||
self.file_size = 0
|
||||
def has_data( self ):
|
||||
"""Detects whether there is any data"""
|
||||
return self.get_size() > 0
|
||||
def mark_deleted( self, include_children=True ):
|
||||
self.deleted = True
|
||||
|
||||
# FIXME: sqlalchemy will replace this
|
||||
def _delete(self):
|
||||
"""Remove the file that corresponds to this data"""
|
||||
try:
|
||||
os.remove(self.data.file_name)
|
||||
except OSError, e:
|
||||
log.critical('%s delete error %s' % (self.__class__.__name__, e))
|
||||
|
||||
class HistoryDatasetAssociation( object ):
|
||||
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, copied_from_history_dataset_association = None, validation_errors=None, visible=True, create_dataset = False ):
|
||||
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
|
||||
@@ -131,10 +297,6 @@ class HistoryDatasetAssociation( object ):
|
||||
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 ):
|
||||
@@ -252,7 +414,8 @@ class HistoryDatasetAssociation( object ):
|
||||
def get_converter_types(self):
|
||||
return self.datatype.get_converter_types( self, datatypes_registry)
|
||||
|
||||
def copy( self, copy_children = False, parent_id = None ):
|
||||
def copy( self, copy_children = False, parent_id = None, target_user = None ):
|
||||
if target_user is None: target_user = self.user
|
||||
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:
|
||||
@@ -275,7 +438,6 @@ class HistoryDatasetAssociation( object ):
|
||||
child.mark_deleted()
|
||||
|
||||
|
||||
|
||||
class History( object ):
|
||||
def __init__( self, id=None, name=None, user=None ):
|
||||
self.id = id
|
||||
@@ -326,18 +488,21 @@ class History( object ):
|
||||
self.genome_build = genome_build
|
||||
self.datasets.append( dataset )
|
||||
|
||||
def copy(self):
|
||||
des = History()
|
||||
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
|
||||
des.user_id = self.user_id
|
||||
for data in self.datasets:
|
||||
new_data = data.copy( copy_children = True )
|
||||
new_data = data.copy( copy_children = True, target_user = target_user )
|
||||
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 ):
|
||||
@@ -348,100 +513,6 @@ class History( object ):
|
||||
# self.history = history
|
||||
# self.datasets = []
|
||||
|
||||
class Dataset( object ):
|
||||
states = Bunch( NEW = 'new',
|
||||
QUEUED = 'queued',
|
||||
RUNNING = 'running',
|
||||
OK = 'ok',
|
||||
EMPTY = 'empty',
|
||||
ERROR = 'error',
|
||||
DISCARDED = 'discarded' )
|
||||
file_path = "/tmp/"
|
||||
engine = None
|
||||
def __init__( self, id=None, state=None, external_filename=None, extra_files_path=None, file_size=None, purgable=True ):
|
||||
self.id = id
|
||||
self.state = state
|
||||
self.deleted = False
|
||||
self.purged = False
|
||||
self.purgable = purgable
|
||||
self.external_filename = external_filename
|
||||
self._extra_files_path = extra_files_path
|
||||
self.file_size = file_size
|
||||
|
||||
def get_file_name( self ):
|
||||
if not self.external_filename:
|
||||
assert self.id is not None, "ID must be set before filename used (commit the object)"
|
||||
# First try filename directly under file_path
|
||||
filename = os.path.join( self.file_path, "dataset_%d.dat" % self.id )
|
||||
# Only use that filename if it already exists (backward compatibility),
|
||||
# otherwise construct hashed path
|
||||
if not os.path.exists( filename ):
|
||||
dir = os.path.join( self.file_path, *directory_hash_id( self.id ) )
|
||||
# Create directory if it does not exist
|
||||
try:
|
||||
os.makedirs( dir )
|
||||
except OSError, e:
|
||||
# File Exists is okay, otherwise reraise
|
||||
if e.errno != errno.EEXIST:
|
||||
raise
|
||||
# Return filename inside hashed directory
|
||||
return os.path.abspath( os.path.join( dir, "dataset_%d.dat" % self.id ) )
|
||||
else:
|
||||
filename = self.external_filename
|
||||
# Make filename absolute
|
||||
return os.path.abspath( filename )
|
||||
|
||||
def set_file_name ( self, filename ):
|
||||
if not filename:
|
||||
self.external_filename = None
|
||||
else:
|
||||
self.external_filename = filename
|
||||
|
||||
file_name = property( get_file_name, set_file_name )
|
||||
|
||||
@property
|
||||
def extra_files_path( self ):
|
||||
if self._extra_files_path:
|
||||
path = self._extra_files_path
|
||||
else:
|
||||
path = os.path.join( self.file_path, "dataset_%d_files" % self.id )
|
||||
#only use path directly under self.file_path if it exists
|
||||
if not os.path.exists( path ):
|
||||
path = os.path.join( os.path.join( self.file_path, *directory_hash_id( self.id ) ), "dataset_%d_files" % self.id )
|
||||
# Make path absolute
|
||||
return os.path.abspath( path )
|
||||
|
||||
def get_size( self ):
|
||||
"""Returns the size of the data on disk"""
|
||||
if self.file_size:
|
||||
return self.file_size
|
||||
else:
|
||||
try:
|
||||
return os.path.getsize( self.file_name )
|
||||
except OSError:
|
||||
return 0
|
||||
def set_size( self ):
|
||||
"""Returns the size of the data on disk"""
|
||||
try:
|
||||
self.file_size = os.path.getsize( self.file_name )
|
||||
except OSError:
|
||||
self.file_size = 0
|
||||
def has_data( self ):
|
||||
"""Detects whether there is any data"""
|
||||
return self.get_size() > 0
|
||||
def mark_deleted( self, include_children=True ):
|
||||
self.deleted = True
|
||||
|
||||
# FIXME: sqlalchemy will replace this
|
||||
def _delete(self):
|
||||
"""Remove the file that corresponds to this data"""
|
||||
try:
|
||||
os.remove(self.data.file_name)
|
||||
except OSError, e:
|
||||
log.critical('%s delete error %s' % (self.__class__.__name__, e))
|
||||
|
||||
class Old_Dataset( Dataset ):
|
||||
pass
|
||||
|
||||
class ValidationError( object ):
|
||||
def __init__( self, message=None, err_type=None, attributes=None ):
|
||||
|
||||
@@ -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 )
|
||||
@@ -114,6 +115,47 @@ 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 ) )
|
||||
|
||||
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=[] ) )
|
||||
|
||||
# TODO, Nate: Need to better understand what these Default tables are for and add appropriate
|
||||
# comments here to clarify them. Need to ensure that they should include the permitted_actions
|
||||
# columns, and if so, that they are correctly populated.
|
||||
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=[] ) )
|
||||
|
||||
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=[] ) )
|
||||
|
||||
Job.table = Table( "job", metadata,
|
||||
Column( "id", Integer, primary_key=True ),
|
||||
Column( "create_time", DateTime, default=now ),
|
||||
@@ -298,6 +340,30 @@ 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" ) ) )
|
||||
|
||||
|
||||
# TODO, Nate: Need to make sure we have optimal performance - may need more mappers...
|
||||
# if we have a user and a list of datasets, what is the fastest
|
||||
# way to ask whether the user has a certain action on all of them.
|
||||
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, JobToInputDatasetAssociation, JobToInputDatasetAssociation.table,
|
||||
properties=dict( job=relation( Job ), dataset=relation( HistoryDatasetAssociation ) ) )
|
||||
|
||||
@@ -411,6 +477,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():
|
||||
|
||||
@@ -0,0 +1,231 @@
|
||||
"""
|
||||
Utility functions used systemwide.
|
||||
|
||||
"""
|
||||
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(
|
||||
EDIT_METADATA = 'edit_metadata',
|
||||
MANAGE_PERMISSIONS = 'manage_permissions',
|
||||
ACCESS = '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 ):
|
||||
# TODO, Nate: Make sure this method is functionally correct.
|
||||
"""Returns true when user has permission to perform an action"""
|
||||
while 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 in [ self.permitted_actions.dataset_actions.USE, self.permitted_actions.dataset_actions.VIEW ] 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 ):
|
||||
for pa in group_dataset_assoc.permitted_actions:
|
||||
if action in pa.permitted_actions.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 ):
|
||||
# TODO, Nate: Make sure this method is functionally correct.
|
||||
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 associate_group_dataset( self, group, dataset, permitted_actions=[] ):
|
||||
# TODO, Nate: Make sure this method is functionally correct.
|
||||
# TODO: For now, just take the dataset's permitted_actions, but we need to make sure
|
||||
# we can associate group permitted_actions if necessary - need to look into this...
|
||||
if not permitted_actions:
|
||||
if isinstance( dataset.permitted_actions, Bunch ):
|
||||
permitted_actions = dataset.permitted_actions.__dict__.values()
|
||||
else:
|
||||
permitted_actions = dataset.permitted_actions
|
||||
log.debug("In associate_group_dataset, permitted_actions: %s" %str(permitted_actions) )
|
||||
assoc = self.model.GroupDatasetAssociation( group, dataset, permitted_actions )
|
||||
assoc.flush()
|
||||
return assoc
|
||||
def associate_user_group( self, user, group ):
|
||||
assoc = self.model.UserGroupAssociation( user, group )
|
||||
assoc.flush()
|
||||
return assoc
|
||||
def create_private_user_group( self, user ):
|
||||
# TODO, Nate: Make sure this method is functionally correct.
|
||||
# Create private group
|
||||
group = self.model.Group( user.email, 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.get_public_group(), 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:
|
||||
log.debug("In user_set_default_access, group: %s" %str(group))
|
||||
if isinstance( group, self.model.Group ):
|
||||
permitted_actions = group.permitted_actions.__dict__.values()
|
||||
else:
|
||||
permitted_actions = group.permitted_actions
|
||||
log.debug("In user_set_default_access, permitted_actions: %s" % str( 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 = 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:
|
||||
log.debug("In history_set_default_access, group: %s" %str(group))
|
||||
if isinstance( group, self.model.Group ):
|
||||
permitted_actions = group.permitted_actions.__dict__.values()
|
||||
else:
|
||||
permitted_actions = group.permitted_actions
|
||||
log.debug("In history_set_default_access, permitted_actions: %s" % str( 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 ):
|
||||
# TODO, Nate: Make sure this method is functionally correct.
|
||||
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
|
||||
log.debug("In set_dataset_groups, before elf.associate_components, dataset: %s, group: %s" % ( str(dataset), str(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
|
||||
@@ -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 )
|
||||
|
||||
@@ -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.USE, 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.USE, 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 )
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.USE, 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.USE, 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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -103,28 +103,28 @@ 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." )
|
||||
# TODO, Nate: Make sure the following is functionally correct.
|
||||
if trans.app.security_agent.allow_action( trans.user, data.permitted_actions.VIEW, 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 privileged to access this dataset." )
|
||||
@@ -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.VIEW, 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.VIEW, 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.VIEW, 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.USE, 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_permision:
|
||||
"""The user clicked the change_permision 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 data.dataset.has_group( public_group ):
|
||||
#check user has permission and then remove public group
|
||||
if trans.app.security_agent.allow_action( trans.user, data.dataset.permitted_actions.REMOVE_GROUP, dataset = data.dataset ):
|
||||
trans.app.security_agent.remove_component_association( 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 data.dataset.has_group( public_group ):
|
||||
#check user has permission and then add public group
|
||||
if trans.app.security_agent.allow_action( trans.user, data.dataset.permitted_actions.ADD_GROUP, 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 -----------------------------------------------
|
||||
@@ -404,7 +443,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 +480,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 +587,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 +613,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 +676,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 ):
|
||||
|
||||
@@ -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." )
|
||||
|
||||
@@ -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
|
||||
@@ -431,6 +432,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
|
||||
|
||||
@@ -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.REMOVE_GROUP, dataset = data ) or trans.app.security_agent.allow_action( trans.user, data.permitted_actions.ADD_GROUP, dataset = data ) ):
|
||||
<div class="toolForm">
|
||||
<div class="toolFormTitle">Change Permitted Actions</div>
|
||||
<div class="toolFormBody">
|
||||
<form name="change_permision_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 data.dataset.has_group( trans.app.model.Group.get_public_group() ):
|
||||
<% 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_permision" value="Save">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
%endif
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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.VIEW, dataset = data.dataset ):
|
||||
<div>You do not have permision 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>
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user