text: """
  """
  import datetime
  import hashlib
  import inspect
  import logging
  import os
  import random
  import socket
  import string
  import time
  from importlib import import_module
  
  import mako.lookup
  import mako.runtime
  from babel import Locale
  from babel.support import Translations
  from Cheetah.Template import Template
  from six import string_types
  from six.moves.http_cookies import CookieError
  from six.moves.urllib.parse import urlparse
  from sqlalchemy import and_, true
  from sqlalchemy.orm import joinedload
  from sqlalchemy.orm.exc import NoResultFound
  
  from galaxy import util
  from galaxy.exceptions import ConfigurationError, MessageException
  from galaxy.managers import context
  from galaxy.util import (
      asbool,
      safe_makedirs,
      safe_str_cmp,
      smart_str,
      unicodify
  )
  from galaxy.util.sanitize_html import sanitize_html
  from galaxy.web.framework import (
      base,
      helpers,
      url_for
  )
  from galaxy.web.stack import get_app_kwds
  
  log = logging.getLogger(__name__)
  
  
  UCSC_SERVERS = (
      'hgw1.cse.ucsc.edu',
      'hgw2.cse.ucsc.edu',
      'hgw3.cse.ucsc.edu',
      'hgw4.cse.ucsc.edu',
      'hgw5.cse.ucsc.edu',
      'hgw6.cse.ucsc.edu',
      'hgw7.cse.ucsc.edu',
      'hgw8.cse.ucsc.edu',
      'hgw1.soe.ucsc.edu',
      'hgw2.soe.ucsc.edu',
      'hgw3.soe.ucsc.edu',
      'hgw4.soe.ucsc.edu',
      'hgw5.soe.ucsc.edu',
      'hgw6.soe.ucsc.edu',
      'hgw7.soe.ucsc.edu',
      'hgw8.soe.ucsc.edu',
  )
  
  
  class WebApplication(base.WebApplication):
      """
      Base WSGI application instantiated for all Galaxy webapps.
  
      A web application that:
          * adds API and UI controllers by scanning given directories and
          importing all modules found there.
          * has a security object.
          * builds mako template lookups.
          * generates GalaxyWebTransactions.
      """
  
      def __init__(self, galaxy_app, session_cookie='galaxysession', name=None):
          self.name = name
          base.WebApplication.__init__(self)
          self.set_transaction_factory(lambda e: self.transaction_chooser(e, galaxy_app, session_cookie))
          # Mako support
          self.mako_template_lookup = self.create_mako_template_lookup(galaxy_app, name)
          # Security helper
          self.security = galaxy_app.security
  
      def create_mako_template_lookup(self, galaxy_app, name):
          paths = []
          # First look in webapp specific directory
          if name is not None:
              paths.append(os.path.join(galaxy_app.config.template_path, 'webapps', name))
          # Then look in root directory
          paths.append(galaxy_app.config.template_path)
          # Create TemplateLookup with a small cache
          return mako.lookup.TemplateLookup(directories=paths,
                                            module_directory=galaxy_app.config.template_cache,
                                            collection_size=500)
  
      def handle_controller_exception(self, e, trans, **kwargs):
          if isinstance(e, MessageException):
              # In the case of a controller exception, sanitize to make sure
              # unsafe html input isn't reflected back to the user
              return trans.show_message(sanitize_html(e.err_msg), e.type)
  
      def make_body_iterable(self, trans, body):
          return base.WebApplication.make_body_iterable(self, trans, body)
  
      def transaction_chooser(self, environ, galaxy_app, session_cookie):
          return GalaxyWebTransaction(environ, galaxy_app, self, session_cookie)
  
      def add_ui_controllers(self, package_name, app):
          """
          Search for UI controllers in `package_name` and add
          them to the webapp.
          """
          from galaxy.web.base.controller import BaseUIController
          from galaxy.web.base.controller import ControllerUnavailable
          package = import_module(package_name)
          controller_dir = package.__path__[0]
          for fname in os.listdir(controller_dir):
              if not(fname.startswith("_")) and fname.endswith(".py"):
                  name = fname[:-3]
                  module_name = package_name + "." + name
                  try:
                      module = import_module(module_name)
                  except ControllerUnavailable as exc:
                      log.debug("%s could not be loaded: %s" % (module_name, str(exc)))
                      continue
                  # Look for a controller inside the modules
                  for key in dir(module):
                      T = getattr(module, key)
                      if inspect.isclass(T) and T is not BaseUIController and issubclass(T, BaseUIController):
                          controller = self._instantiate_controller(T, app)
                          self.add_ui_controller(name, controller)
  
      def add_api_controllers(self, package_name, app):
          """
          Search for UI controllers in `package_name` and add
          them to the webapp.
          """
          from galaxy.web.base.controller import BaseAPIController
          from galaxy.web.base.controller import ControllerUnavailable
          package = import_module(package_name)
          controller_dir = package.__path__[0]
          for fname in os.listdir(controller_dir):
              if not(fname.startswith("_")) and fname.endswith(".py"):
                  name = fname[:-3]
                  module_name = package_name + "." + name
                  try:
                      module = import_module(module_name)
                  except ControllerUnavailable as exc:
                      log.debug("%s could not be loaded: %s" % (module_name, str(exc)))
                      continue
                  for key in dir(module):
                      T = getattr(module, key)
                      # Exclude classes such as BaseAPIController and BaseTagItemsController
                      if inspect.isclass(T) and not key.startswith("Base") and issubclass(T, BaseAPIController):
                          # By default use module_name, but allow controller to override name
                          controller_name = getattr(T, "controller_name", name)
                          controller = self._instantiate_controller(T, app)
                          self.add_api_controller(controller_name, controller)
  
      def _instantiate_controller(self, T, app):
          """ Extension point, allow apps to construct controllers differently,
          really just used to stub out actual controllers for routes testing.
          """
          return T(app)
  
  
  class GalaxyWebTransaction(base.DefaultWebTransaction,
                             context.ProvidesAppContext, context.ProvidesUserContext, context.ProvidesHistoryContext):
      """
      Encapsulates web transaction specific state for the Galaxy application
      (specifically the user's "cookie" session and history)
      """
  
      def __init__(self, environ, app, webapp, session_cookie=None):
          self.app = app
          self.webapp = webapp
          self.security = webapp.security
          base.DefaultWebTransaction.__init__(self, environ)
          self.setup_i18n()
          self.expunge_all()
          config = self.app.config
          self.debug = asbool(config.get('debug', False))
          x_frame_options = getattr(config, 'x_frame_options', None)
          if x_frame_options:
              self.response.headers['X-Frame-Options'] = x_frame_options
          # Flag indicating whether we are in workflow building mode (means
          # that the current history should not be used for parameter values
          # and such).
          self.workflow_building_mode = False
          # Flag indicating whether this is an API call and the API key user is an administrator
          self.api_inherit_admin = False
          self.__user = None
          self.galaxy_session = None
          self.error_message = None
  
          # set any cross origin resource sharing headers if configured to do so
          self.set_cors_headers()
  
          if self.environ.get('is_api_request', False):
              # With API requests, if there's a key, use it and associate the
              # user with the transaction.
              # If not, check for an active session but do not create one.
              # If an error message is set here, it's sent back using
              # trans.show_error in the response -- in expose_api.
              self.error_message = self._authenticate_api(session_cookie)
          elif self.app.name == "reports":
              self.galaxy_session = None
          else:
              # This is a web request, get or create session.
              self._ensure_valid_session(session_cookie)
          if self.galaxy_session:
              # When we've authenticated by session, we have to check the
              # following.
              # Prevent deleted users from accessing Galaxy
              if config.use_remote_user and self.galaxy_session.user.deleted:
                  self.response.send_redirect(url_for('/static/user_disabled.html'))
              if config.require_login:
                  self._ensure_logged_in_user(environ, session_cookie)
              if config.session_duration:
                  # TODO DBTODO All ajax calls from the client need to go through
                  # a single point of control where we can do things like
                  # redirect/etc.  This is API calls as well as something like 40
                  # @web.json requests that might not get handled well on the
                  # clientside.
                  #
                  # Make sure we're not past the duration, and either log out or
                  # update timestamp.
                  now = datetime.datetime.now()
                  if self.galaxy_session.last_action:
                      expiration_time = self.galaxy_session.last_action + datetime.timedelta(minutes=config.session_duration)
                  else:
                      expiration_time = now
                      self.galaxy_session.last_action = now - datetime.timedelta(seconds=1)
                      self.sa_session.add(self.galaxy_session)
                      self.sa_session.flush()
                  if expiration_time < now:
                      # Expiration time has passed.
                      self.handle_user_logout()
                      if self.environ.get('is_api_request', False):
                          self.response.status = 401
                          self.user = None
                          self.galaxy_session = None
                      else:
                          self.response.send_redirect(url_for(controller='user',
                                                       action='login',
                                                       message="You have been logged out due to inactivity.  Please log in again to continue using Galaxy.",
                                                       status='info',
                                                       use_panels=True))
                  else:
                      self.galaxy_session.last_action = now
                      self.sa_session.add(self.galaxy_session)
                      self.sa_session.flush()
  
      def setup_i18n(self):
          locales = []
          if 'HTTP_ACCEPT_LANGUAGE' in self.environ:
              # locales looks something like: ['en', 'en-us;q=0.7', 'ja;q=0.3']
              client_locales = self.environ['HTTP_ACCEPT_LANGUAGE'].split(',')
              for locale in client_locales:
                  try:
                      locales.append(Locale.parse(locale.split(';')[0].strip(), sep='-').language)
                  except Exception as e:
                      log.debug("Error parsing locale '%s'. %s: %s", locale, type(e), e)
          if not locales:
              # Default to English
              locales = 'en'
          t = Translations.load(dirname='locale', locales=locales, domain='ginga')
          self.template_context.update(dict(_=t.ugettext, n_=t.ugettext, N_=t.ungettext))
  
      def set_cors_headers(self):
          """Allow CORS requests if configured to do so by echoing back the request's
          'Origin' header (if any) as the response header 'Access-Control-Allow-Origin'
          """
          # TODO: in order to use these, we need preflight to work, and to do that we
          # need the OPTIONS method on all api calls (or everywhere we can POST/PUT)
          # ALLOWED_METHODS = ( 'POST', 'PUT' )
  
          # do not set any access control headers if not configured for it (common case)
          if not self.app.config.get('allowed_origin_hostnames', None):
              return
          # do not set any access control headers if there's no origin header on the request
          origin_header = self.request.headers.get("Origin", None)
          if not origin_header:
              return
  
          # singular match
          def matches_allowed_origin(origin, allowed_origin):
              if isinstance(allowed_origin, string_types):
                  return origin == allowed_origin
              match = allowed_origin.match(origin)
              return match and match.group() == origin
  
          # check for '*' or compare to list of allowed
          def is_allowed_origin(origin):
              # localhost uses no origin header (== null)
              if not origin:
                  return False
              for allowed_origin in self.app.config.allowed_origin_hostnames:
                  if allowed_origin == '*' or matches_allowed_origin(origin, allowed_origin):
                      return True
              return False
  
          # boil origin header down to hostname
          origin = urlparse(origin_header).hostname
          # check against the list of allowed strings/regexp hostnames, echo original if cleared
          if is_allowed_origin(origin):
              self.response.headers['Access-Control-Allow-Origin'] = origin_header
              # TODO: see the to do on ALLOWED_METHODS above
              # self.response.headers[ 'Access-Control-Allow-Methods' ] = ', '.join( ALLOWED_METHODS )
  
          # NOTE: raising some errors (such as httpexceptions), will remove the header
          # (e.g. client will get both cors error and 404 inside that)
  
      def get_user(self):
          """Return the current user if logged in or None."""
          user = self.__user
          if not user and self.galaxy_session:
              user = self.galaxy_session.user
              self.__user = user
          return user
  
      def set_user(self, user):
          """Set the current user."""
          if self.galaxy_session:
              self.galaxy_session.user = user
              self.sa_session.add(self.galaxy_session)
              self.sa_session.flush()
          self.__user = user
  
      user = property(get_user, set_user)
  
      def get_cookie(self, name='galaxysession'):
          """Convenience method for getting a session cookie"""
          try:
              # If we've changed the cookie during the request return the new value
              if name in self.response.cookies:
                  return self.response.cookies[name].value
              else:
                  return self.request.cookies[name].value
          except Exception:
              return None
  
      def set_cookie(self, value, name='galaxysession', path='/', age=90, version='1'):
          """Convenience method for setting a session cookie"""
          # The galaxysession cookie value must be a high entropy 128 bit random number encrypted
          # using a server secret key.  Any other value is invalid and could pose security issues.
          self.response.cookies[name] = unicodify(value)
          self.response.cookies[name]['path'] = path
          self.response.cookies[name]['max-age'] = 3600 * 24 * age  # 90 days
          tstamp = time.localtime(time.time() + 3600 * 24 * age)
          self.response.cookies[name]['expires'] = time.strftime('%a, %d-%b-%Y %H:%M:%S GMT', tstamp)
          self.response.cookies[name]['version'] = version
          https = self.request.environ["wsgi.url_scheme"] == "https"
          if https:
              self.response.cookies[name]['secure'] = True
          try:
              self.response.cookies[name]['httponly'] = True
          except CookieError as e:
              log.warning("Error setting httponly attribute in cookie '%s': %s" % (name, e))
  
      def _authenticate_api(self, session_cookie):
          """
          Authenticate for the API via key or session (if available).
          """
          api_key = self.request.params.get('key', None) or self.request.headers.get('x-api-key', None)
          secure_id = self.get_cookie(name=session_cookie)
          api_key_supplied = self.environ.get('is_api_request', False) and api_key
          if api_key_supplied and self._check_master_api_key(api_key):
              self.api_inherit_admin = True
              log.info("Session authenticated using Galaxy master api key")
              self.user = None
              self.galaxy_session = None
          elif api_key_supplied:
              # Sessionless API transaction, we just need to associate a user.
              try:
                  provided_key = self.sa_session.query(self.app.model.APIKeys).filter(self.app.model.APIKeys.table.c.key == api_key).one()
              except NoResultFound:
                  return 'Provided API key is not valid.'
              if provided_key.user.deleted:
                  return 'User account is deactivated, please contact an administrator.'
              newest_key = provided_key.user.api_keys[0]
              if newest_key.key != provided_key.key:
                  return 'Provided API key has expired.'
              self.set_user(provided_key.user)
          elif secure_id:
              # API authentication via active session
              # Associate user using existing session
              # This will throw an exception under remote auth with anon users.
              try:
                  self._ensure_valid_session(session_cookie)
              except Exception:
                  log.exception("Exception during Session-based API authentication, this was most likely an attempt to use an anonymous cookie under remote authentication (so, no user), which we don't support.")
                  self.user = None
                  self.galaxy_session = None
          else:
              # Anonymous API interaction -- anything but @expose_api_anonymous will fail past here.
              self.user = None
              self.galaxy_session = None
  
      def _check_master_api_key(self, api_key):
          master_api_key = getattr(self.app.config, 'master_api_key', None)
          if not master_api_key:
              return False
          # Hash keys to make them the same size, so we can do safe comparison.
          master_hash = hashlib.sha256(smart_str(master_api_key)).hexdigest()
          provided_hash = hashlib.sha256(smart_str(api_key)).hexdigest()
          return safe_str_cmp(master_hash, provided_hash)
  
      def _ensure_valid_session(self, session_cookie, create=True):
          """
          Ensure that a valid Galaxy session exists and is available as
          trans.session (part of initialization)
  
          Support for universe_session and universe_user cookies has been
          removed as of 31 Oct 2008.
          """
          # Try to load an existing session
          secure_id = self.get_cookie(name=session_cookie)
          galaxy_session = None
          prev_galaxy_session = None
          user_for_new_session = None
          invalidate_existing_session = False
          # Track whether the session has changed so we can avoid calling flush
          # in the most common case (session exists and is valid).
          galaxy_session_requires_flush = False
          if secure_id:
              # Decode the cookie value to get the session_key
              try:
                  session_key = self.security.decode_guid(secure_id)
              except Exception:
                  # We'll end up creating a new galaxy_session
                  session_key = None
              if session_key:
                  # Retrieve the galaxy_session id via the unique session_key
                  galaxy_session = self.sa_session.query(self.app.model.GalaxySession) \
                                                  .filter(and_(self.app.model.GalaxySession.table.c.session_key == session_key,
                                                               self.app.model.GalaxySession.table.c.is_valid == true())).options(joinedload("user")).first()
          # If remote user is in use it can invalidate the session and in some
          # cases won't have a cookie set above, so we need to to check some
          # things now.
          if self.app.config.use_remote_user:
              remote_user_email = self.environ.get(self.app.config.remote_user_header, None)
              if galaxy_session:
                  if remote_user_email and galaxy_session.user is None:
                      # No user, associate
                      galaxy_session.user = self.get_or_create_remote_user(remote_user_email)
                      galaxy_session_requires_flush = True
                  elif (remote_user_email and
                        (galaxy_session.user.email != remote_user_email) and
                        ((not self.app.config.allow_user_impersonation) or
                         (remote_user_email not in self.app.config.admin_users_list))):
                      # Session exists but is not associated with the correct
                      # remote user, and the currently set remote_user is not a
                      # potentially impersonating admin.
                      invalidate_existing_session = True
                      user_for_new_session = self.get_or_create_remote_user(remote_user_email)
                      log.warning("User logged in as '%s' externally, but has a cookie as '%s' invalidating session",
                                  remote_user_email, galaxy_session.user.email)
              elif remote_user_email:
                  # No session exists, get/create user for new session
                  user_for_new_session = self.get_or_create_remote_user(remote_user_email)
              if ((galaxy_session and galaxy_session.user is None) and user_for_new_session is None):
                  raise Exception("Remote Authentication Failure - user is unknown and/or not supplied.")
          else:
              if galaxy_session is not None and galaxy_session.user and galaxy_session.user.external:
                  # Remote user support is not enabled, but there is an existing
                  # session with an external user, invalidate
                  invalidate_existing_session = True
                  log.warning("User '%s' is an external user with an existing session, invalidating session since external auth is disabled",
                              galaxy_session.user.email)
              elif galaxy_session is not None and galaxy_session.user is not None and galaxy_session.user.deleted:
                  invalidate_existing_session = True
                  log.warning("User '%s' is marked deleted, invalidating session" % galaxy_session.user.email)
          # Do we need to invalidate the session for some reason?
          if invalidate_existing_session:
              prev_galaxy_session = galaxy_session
              prev_galaxy_session.is_valid = False
              galaxy_session = None
          # No relevant cookies, or couldn't find, or invalid, so create a new session
          if galaxy_session is None:
              galaxy_session = self.__create_new_session(prev_galaxy_session, user_for_new_session)
              galaxy_session_requires_flush = True
              self.galaxy_session = galaxy_session
              self.__update_session_cookie(name=session_cookie)
          else:
              self.galaxy_session = galaxy_session
          # Do we need to flush the session?
          if galaxy_session_requires_flush:
              self.sa_session.add(galaxy_session)
              # FIXME: If prev_session is a proper relation this would not
              #        be needed.
              if prev_galaxy_session:
                  self.sa_session.add(prev_galaxy_session)
              self.sa_session.flush()
          # If the old session was invalid, get a new (or existing default,
          # unused) history with our new session
          if invalidate_existing_session:
              self.get_or_create_default_history()
  
      def _ensure_logged_in_user(self, environ, session_cookie):
          # The value of session_cookie can be one of
          # 'galaxysession' or 'galaxycommunitysession'
          # Currently this method does nothing unless session_cookie is 'galaxysession'
          if session_cookie == 'galaxysession' and self.galaxy_session.user is None:
              # TODO: re-engineer to eliminate the use of allowed_paths
              # as maintenance overhead is far too high.
              allowed_paths = [
                  # client app route
                  # TODO: might be better as '/:username/login', '/:username/logout'
                  url_for(controller='root', action='login'),
                  # mako app routes
                  url_for(controller='user', action='login'),
                  url_for(controller='user', action='logout'),
                  url_for(controller='user', action='reset_password'),
                  url_for(controller='user', action='change_password'),
                  # required to log in w/ openid
                  url_for(controller='user', action='openid_auth'),
                  url_for(controller='user', action='openid_process'),
                  url_for(controller='user', action='openid_associate'),
                  # TODO: do any of these still need to bypass require login?
                  url_for(controller='user', action='api_keys'),
                  url_for(controller='user', action='create'),
                  url_for(controller='user', action='index'),
                  url_for(controller='user', action='manage_user_info'),
                  url_for(controller='user', action='set_default_permissions'),
              ]
              # append the welcome url to allowed paths if we'll show it at the login screen
              if self.app.config.show_welcome_with_login:
                  allowed_paths.append(url_for(controller='root', action='welcome'))
  
              # prevent redirect when UCSC server attempts to get dataset contents as 'anon' user
              display_as = url_for(controller='root', action='display_as')
              if self.app.datatypes_registry.get_display_sites('ucsc') and self.request.path == display_as:
                  try:
                      host = socket.gethostbyaddr(self.environ['REMOTE_ADDR'])[0]
                  except(socket.error, socket.herror, socket.gaierror, socket.timeout):
                      host = None
                  if host in UCSC_SERVERS:
                      return
              # prevent redirect for external, enabled display applications getting dataset contents
              external_display_path = url_for(controller='', action='display_application')
              if self.request.path.startswith(external_display_path):
                  request_path_split = self.request.path.split('/')
                  try:
                      if (self.app.datatypes_registry.display_applications.get(request_path_split[-5]) and
                              request_path_split[-4] in self.app.datatypes_registry.display_applications.get(request_path_split[-5]).links and
                              request_path_split[-3] != 'None'):
                          return
                  except IndexError:
                      pass
              # redirect to root if the path is not in the list above
              if self.request.path not in allowed_paths:
                  login_url = url_for(controller='root', action='login', redirect=self.request.path)
                  self.response.send_redirect(login_url)
  
      def __create_new_session(self, prev_galaxy_session=None, user_for_new_session=None):
          """
          Create a new GalaxySession for this request, possibly with a connection
          to a previous session (in `prev_galaxy_session`) and an existing user
          (in `user_for_new_session`).
  
          Caller is responsible for flushing the returned session.
          """
          session_key = self.security.get_new_guid()
          galaxy_session = self.app.model.GalaxySession(
              session_key=session_key,
              is_valid=True,
              remote_host=self.request.remote_host,
              remote_addr=self.request.remote_addr,
              referer=self.request.headers.get('Referer', None))
          if prev_galaxy_session:
              # Invalidated an existing session for some reason, keep track
              galaxy_session.prev_session_id = prev_galaxy_session.id
          if user_for_new_session:
              # The new session should be associated with the user
              galaxy_session.user = user_for_new_session
          return galaxy_session
  
      def get_or_create_remote_user(self, remote_user_email):
          """
          Create a remote user with the email remote_user_email and return it
          """
          if not self.app.config.use_remote_user:
              return None
          if getattr(self.app.config, "normalize_remote_user_email", False):
              remote_user_email = remote_user_email.lower()
          user = self.sa_session.query(self.app.model.User).filter(self.app.model.User.table.c.email == remote_user_email).first()
          if user:
              # GVK: June 29, 2009 - This is to correct the behavior of a previous bug where a private
              # role and default user / history permissions were not set for remote users.  When a
              # remote user authenticates, we'll look for this information, and if missing, create it.
              if not self.app.security_agent.get_private_user_role(user):
                  self.app.security_agent.create_private_user_role(user)
              if 'webapp' not in self.environ or self.environ['webapp'] != 'tool_shed':
                  if not user.default_permissions:
                      self.app.security_agent.user_set_default_permissions(user)
                      self.app.security_agent.user_set_default_permissions(user, history=True, dataset=True)
          elif user is None:
              username = remote_user_email.split('@', 1)[0].lower()
              random.seed()
              user = self.app.model.User(email=remote_user_email)
              user.set_random_password(length=12)
              user.external = True
              # Replace invalid characters in the username
              for char in [x for x in username if x not in string.ascii_lowercase + string.digits + '-' + '.']:
                  username = username.replace(char, '-')
              # Find a unique username - user can change it later
              if self.sa_session.query(self.app.model.User).filter_by(username=username).first():
                  i = 1
                  while self.sa_session.query(self.app.model.User).filter_by(username=(username + '-' + str(i))).first():
                      i += 1
                  username += '-' + str(i)
              user.username = username
              self.sa_session.add(user)
              self.sa_session.flush()
              self.app.security_agent.create_private_user_role(user)
              # We set default user permissions, before we log in and set the default history permissions
              if 'webapp' not in self.environ or self.environ['webapp'] != 'tool_shed':
                  self.app.security_agent.user_set_default_permissions(user)
              # self.log_event( "Automatically created account '%s'", user.email )
          return user
  
      def __update_session_cookie(self, name='galaxysession'):
          """
          Update the session cookie to match the current session.
          """
          self.set_cookie(self.security.encode_guid(self.galaxy_session.session_key),
                          name=name, path=self.app.config.cookie_path)
  
      def check_user_library_import_dir(self, user):
          if getattr(self.app.config, "user_library_import_dir_auto_creation", False):
              # try to create a user library import directory
              try:
                  safe_makedirs(os.path.join(self.app.config.user_library_import_dir, user.email))
              except ConfigurationError as e:
                  self.log_event(str(e))
  
      def user_checks(self, user):
          """
          This could contain more checks around a user upon login
          """
          self.check_user_library_import_dir(user)
  
      def _associate_user_history(self, user, prev_galaxy_session=None):
          """
          Associate the user's last accessed history (if exists) with their new session
          """
          history = None
          try:
              users_last_session = user.galaxy_sessions[0]
              last_accessed = True
          except Exception:
              users_last_session = None
              last_accessed = False
          if (prev_galaxy_session and
                  prev_galaxy_session.current_history and not
                  prev_galaxy_session.current_history.deleted and
                  prev_galaxy_session.current_history.datasets):
              if prev_galaxy_session.current_history.user is None or prev_galaxy_session.current_history.user == user:
                  # If the previous galaxy session had a history, associate it with the new
                  # session, but only if it didn't belong to a different user.
                  history = prev_galaxy_session.current_history
                  if prev_galaxy_session.user is None:
                      # Increase the user's disk usage by the amount of the previous history's datasets if they didn't already own it.
                      for hda in history.datasets:
                          user.adjust_total_disk_usage(hda.quota_amount(user))
          elif self.galaxy_session.current_history:
              history = self.galaxy_session.current_history
          if (not history and users_last_session and
                  users_last_session.current_history and not
                  users_last_session.current_history.deleted):
              history = users_last_session.current_history
          elif not history:
              history = self.get_history(create=True, most_recent=True)
          if history not in self.galaxy_session.histories:
              self.galaxy_session.add_history(history)
          if history.user is None:
              history.user = user
          self.galaxy_session.current_history = history
          if not last_accessed:
              # Only set default history permissions if current history is not from a previous session
              self.app.security_agent.history_set_default_permissions(history, dataset=True, bypass_manage_permission=True)
          self.sa_session.add_all((prev_galaxy_session, self.galaxy_session, history))
  
      def handle_user_login(self, user):
          """
          Login a new user (possibly newly created)
             - do some 'system' checks (if any) for this user
             - create a new session
             - associate new session with user
             - if old session had a history and it was not associated with a user, associate it with the new session,
               otherwise associate the current session's history with the user
             - add the disk usage of the current session to the user's total disk usage
          """
          self.user_checks(user)
          # Set the previous session
          prev_galaxy_session = self.galaxy_session
          prev_galaxy_session.is_valid = False
          # Define a new current_session
          self.galaxy_session = self.__create_new_session(prev_galaxy_session, user)
          if self.webapp.name == 'galaxy':
              cookie_name = 'galaxysession'
              self._associate_user_history(user, prev_galaxy_session)
          else:
              cookie_name = 'galaxycommunitysession'
              self.sa_session.add_all((prev_galaxy_session, self.galaxy_session))
          self.sa_session.flush()
          # This method is not called from the Galaxy reports, so the cookie will always be galaxysession
          self.__update_session_cookie(name=cookie_name)
  
      def handle_user_logout(self, logout_all=False):
          """
          Logout the current user:
             - invalidate the current session
             - create a new session with no user associated
          """
          prev_galaxy_session = self.galaxy_session
          prev_galaxy_session.is_valid = False
          self.galaxy_session = self.__create_new_session(prev_galaxy_session)
          self.sa_session.add_all((prev_galaxy_session, self.galaxy_session))
          galaxy_user_id = prev_galaxy_session.user_id
          if logout_all and galaxy_user_id is not None:
              for other_galaxy_session in (self.sa_session.query(self.app.model.GalaxySession)
                                           .filter(and_(self.app.model.GalaxySession.table.c.user_id == galaxy_user_id,
                                                        self.app.model.GalaxySession.table.c.is_valid == true(),
                                                        self.app.model.GalaxySession.table.c.id != prev_galaxy_session.id))):
                  other_galaxy_session.is_valid = False
                  self.sa_session.add(other_galaxy_session)
          self.sa_session.flush()
          if self.webapp.name == 'galaxy':
              # This method is not called from the Galaxy reports, so the cookie will always be galaxysession
              self.__update_session_cookie(name='galaxysession')
          elif self.webapp.name == 'tool_shed':
              self.__update_session_cookie(name='galaxycommunitysession')
  
      def get_galaxy_session(self):
          """
          Return the current galaxy session
          """
          return self.galaxy_session
  
      def get_history(self, create=False, most_recent=False):
          """
          Load the current history.
              - If that isn't available, we find the most recently updated history.
              - If *that* isn't available, we get or create the default history.
          Transactions will not always have an active history (API requests), so
          None is a valid response.
          """
          history = None
          if self.galaxy_session:
              if hasattr(self.galaxy_session, 'current_history'):
                  history = self.galaxy_session.current_history
          if not history and most_recent:
              history = self.get_most_recent_history()
          if not history and util.string_as_bool(create):
              history = self.get_or_create_default_history()
          return history
  
      def set_history(self, history):
          if history and not history.deleted:
              self.galaxy_session.current_history = history
          self.sa_session.add(self.galaxy_session)
          self.sa_session.flush()
  
      history = property(get_history, set_history)
  
      def get_or_create_default_history(self):
          """
          Gets or creates a default history and associates it with the current
          session.
          """
  
          # There must be a user to fetch a default history.
          if not self.galaxy_session.user:
              return self.new_history()
  
          # Look for default history that (a) has default name + is not deleted and
          # (b) has no datasets. If suitable history found, use it; otherwise, create
          # new history.
          unnamed_histories = self.sa_session.query(self.app.model.History).filter_by(
              user=self.galaxy_session.user,
              name=self.app.model.History.default_name,
              deleted=False)
          default_history = None
          for history in unnamed_histories:
              if len(history.datasets) == 0:
                  # Found suitable default history.
                  default_history = history
                  break
  
          # Set or create history.
          if default_history:
              history = default_history
              self.set_history(history)
          else:
              history = self.new_history()
  
          return history
  
      def get_most_recent_history(self):
          """
          Gets the most recently updated history.
          """
          # There must be a user to fetch histories, and without a user we have
          # no recent history.
          if not self.galaxy_session.user:
              return None
          try:
              recent_history = self.sa_session.query(self.app.model.History).filter_by(
                  user=self.galaxy_session.user,
                  deleted=False).order_by(self.app.model.History.update_time.desc()).first()
          except NoResultFound:
              return None
          self.set_history(recent_history)
          return recent_history
  
      def new_history(self, name=None):
          """
          Create a new history and associate it with the current session and
          its associated user (if set).
          """
          # Create new history
          history = self.app.model.History()
          if name:
              history.name = name
          # Associate with session
          history.add_galaxy_session(self.galaxy_session)
          # Make it the session's current history
          self.galaxy_session.current_history = history
          # Associate with user
          if self.galaxy_session.user:
              history.user = self.galaxy_session.user
          # Track genome_build with history
          history.genome_build = self.app.genome_builds.default_value
          # Set the user's default history permissions
          self.app.security_agent.history_set_default_permissions(history)
          # Save
          self.sa_session.add_all((self.galaxy_session, history))
          self.sa_session.flush()
          return history
  
      @base.lazy_property
      def template_context(self):
          return dict()
  
      def set_message(self, message, type=None):
          """
          Convenience method for setting the 'message' and 'message_type'
          element of the template context.
          """
          self.template_context['message'] = message
          if type:
              self.template_context['status'] = type
  
      def get_message(self):
          """
          Convenience method for getting the 'message' element of the template
          context.
          """
          return self.template_context['message']
  
      def show_message(self, message, type='info', refresh_frames=[], cont=None, use_panels=False, active_view=""):
          """
          Convenience method for displaying a simple page with a single message.
  
          `type`: one of "error", "warning", "info", or "done"; determines the
                  type of dialog box and icon displayed with the message
  
          `refresh_frames`: names of frames in the interface that should be
                            refreshed when the message is displayed
          """
          return self.fill_template("message.mako", status=type, message=message, refresh_frames=refresh_frames, cont=cont, use_panels=use_panels, active_view=active_view)
  
      def show_error_message(self, message, refresh_frames=[], use_panels=False, active_view=""):
          """
          Convenience method for displaying an error message. See `show_message`.
          """
          return self.show_message(message, 'error', refresh_frames, use_panels=use_panels, active_view=active_view)
  
      def show_ok_message(self, message, refresh_frames=[], use_panels=False, active_view=""):
          """
          Convenience method for displaying an ok message. See `show_message`.
          """
          return self.show_message(message, 'done', refresh_frames, use_panels=use_panels, active_view=active_view)
  
      def show_warn_message(self, message, refresh_frames=[], use_panels=False, active_view=""):
          """
          Convenience method for displaying an warn message. See `show_message`.
          """
          return self.show_message(message, 'warning', refresh_frames, use_panels=use_panels, active_view=active_view)
  
      @property
      def session_csrf_token(self):
          token = ''
          if self.galaxy_session:
              token = self.security.encode_id(
                  self.galaxy_session.id, kind="csrf"
              )
          return token
  
      def check_csrf_token(self):
          session_csrf_token = self.request.params.get("session_csrf_token", None)
          problem = False
          if not session_csrf_token:
              log.warning("No session_csrf_token set, denying request.")
              problem = True
          elif session_csrf_token != self.session_csrf_token:
              log.warning("Wrong session token found, denying request.")
              problem = True
  
          if problem:
              return self.show_warn_message("Failed to authorize action.")
  
      def fill_template(self, filename, **kwargs):
          """
          Fill in a template, putting any keyword arguments on the context.
          """
          # call get_user so we can invalidate sessions from external users,
          # if external auth has been disabled.
          self.get_user()
          if filename.endswith(".mako"):
              return self.fill_template_mako(filename, **kwargs)
          else:
              template = Template(file=os.path.join(self.app.config.template_path, filename),
                                  searchList=[kwargs, self.template_context, dict(caller=self, t=self, h=helpers, util=util, request=self.request, response=self.response, app=self.app)])
              return str(template)
  
      def fill_template_mako(self, filename, template_lookup=None, **kwargs):
          template_lookup = template_lookup or self.webapp.mako_template_lookup
          template = template_lookup.get_template(filename)
  
          data = dict(caller=self, t=self, trans=self, h=helpers, util=util, request=self.request, response=self.response, app=self.app)
          data.update(self.template_context)
          data.update(kwargs)
          return template.render(**data)
  
      def stream_template_mako(self, filename, **kwargs):
          template = self.webapp.mako_template_lookup.get_template(filename)
          data = dict(caller=self, t=self, trans=self, h=helpers, util=util, request=self.request, response=self.response, app=self.app)
          data.update(self.template_context)
          data.update(kwargs)
  
          def render(environ, start_response):
              response_write = start_response(self.response.wsgi_status(), self.response.wsgi_headeritems())
  
              class StreamBuffer(object):
                  def write(self, d):
                      response_write(d.encode('utf-8'))
              buffer = StreamBuffer()
              context = mako.runtime.Context(buffer, **data)
              template.render_context(context)
              return []
          return render
  
      def fill_template_string(self, template_string, context=None, **kwargs):
          """
          Fill in a template, putting any keyword arguments on the context.
          """
          template = Template(source=template_string,
                              searchList=[context or kwargs, dict(caller=self)])
          return str(template)
  
  
  def build_native_uwsgi_app(paste_factory, config_section):
      """uwsgi can load paste factories with --ini-paste, but this builds non-paste uwsgi apps.
  
      In particular these are useful with --yaml or --json for config."""
      # TODO: just move this to a classmethod on stack?
      app_kwds = get_app_kwds(config_section)
      uwsgi_app = paste_factory({}, load_app_kwds=app_kwds)
      return uwsgi_app
  
  
  def build_url_map(app, global_conf, local_conf):
      from paste.urlmap import URLMap
      from galaxy.web.framework.middleware.static import CacheableStaticURLParser as Static
      urlmap = URLMap()
      # Merge the global and local configurations
      conf = global_conf.copy()
      conf.update(local_conf)
      # Get cache time in seconds
      cache_time = conf.get("static_cache_time", None)
      if cache_time is not None:
          cache_time = int(cache_time)
      # Send to dynamic app by default
      urlmap["/"] = app
      # Define static mappings from config
      urlmap["/static"] = Static(conf.get("static_dir", "./static/"), cache_time)
      urlmap["/images"] = Static(conf.get("static_images_dir", "./static/images"), cache_time)
      urlmap["/static/scripts"] = Static(conf.get("static_scripts_dir", "./static/scripts/"), cache_time)
      urlmap["/static/style"] = Static(conf.get("static_style_dir", "./static/style/blue"), cache_time)
      urlmap["/favicon.ico"] = Static(conf.get("static_favicon_dir", "./static/favicon.ico"), cache_time)
      urlmap["/robots.txt"] = Static(conf.get("static_robots_txt", "./static/robots.txt"), cache_time)
      return urlmap, cache_time
  