# -*- coding: utf-8 -*-
"""WSGI middleware initialization for the allura application."""
import mimetypes
import pylons
import pylons.middleware
import tg
import tg.error
pylons.c = pylons.tmpl_context
pylons.g = pylons.app_globals
import pkg_resources
from tg import config
from paste.deploy.converters import asbool
from paste.registry import RegistryManager
from beaker.middleware import SessionMiddleware
from routes.middleware import RoutesMiddleware
from pylons.middleware import StatusCodeRedirect
import activitystream
import ew
import ming
from ming.orm.middleware import MingMiddleware
from allura.config.app_cfg import base_config
from allura.config.environment import load_environment
from allura.config.app_cfg import ForgeConfig
from allura.lib.custom_middleware import AlluraTimerMiddleware
from allura.lib.custom_middleware import SSLMiddleware
from allura.lib.custom_middleware import StaticFilesMiddleware
from allura.lib.custom_middleware import CSRFMiddleware
from allura.lib.custom_middleware import LoginRedirectMiddleware
from allura.lib import patches
from allura.lib import helpers as h
__all__ = ['make_app']
# Use base_config to setup the necessary PasteDeploy application factory.
# make_base_app will wrap the TG2 app with all the middleware it needs.
make_base_app = base_config.setup_tg_wsgi_app(load_environment)
def make_app(global_conf, full_stack=True, **app_conf):
root = app_conf.get('override_root', 'root')
return _make_core_app(root, global_conf, full_stack, **app_conf)
def _make_core_app(root, global_conf, full_stack=True, **app_conf):
"""
Set allura up with the settings found in the PasteDeploy configuration
file used.
:param root: The controller module containing the TG root
:param global_conf: The global settings for allura (those
defined under the ``[DEFAULT]`` section).
:type global_conf: dict
:param full_stack: Should the whole TG2 stack be set up?
:type full_stack: str or bool
:return: The allura application with all the relevant middleware
loaded.
This is the PasteDeploy factory for the allura application.
``app_conf`` contains all the application-specific settings (those defined
under ``[app:main]``.
"""
# Run all the initialization code here
mimetypes.init(
[pkg_resources.resource_filename('allura', 'etc/mime.types')]
+ mimetypes.knownfiles)
patches.apply()
# Configure MongoDB
ming.configure(**app_conf)
# Configure ActivityStream
if asbool(app_conf.get('activitystream.recording.enabled', False)):
activitystream.configure(**app_conf)
# Configure EW variable provider
ew.render.TemplateEngine.register_variable_provider(get_tg_vars)
# Create base app
base_config = ForgeConfig(root)
load_environment = base_config.make_load_environment()
# Code adapted from tg.configuration, replacing the following lines:
# make_base_app = base_config.setup_tg_wsgi_app(load_environment)
# app = make_base_app(global_conf, full_stack=True, **app_conf)
# Configure the Pylons environment
load_environment(global_conf, app_conf)
if config.get('zarkov.host'):
try:
import zmq
except ImportError:
raise ImportError, "Unable to import the zmq library. Please"\
" check that zeromq is installed or comment out"\
" the zarkov.host setting in your ini file."
app = tg.TGApp()
if asbool(config.get('auth.method', 'local')=='sfx'):
import sfx.middleware
d = h.config_with_prefix(config, 'auth.')
d.update(h.config_with_prefix(config, 'sfx.'))
app = sfx.middleware.SfxMiddleware(app, d)
# Required for pylons
app = RoutesMiddleware(app, config['routes.map'])
# Required for sessions
app = SessionMiddleware(app, config)
# Converts exceptions to HTTP errors, shows traceback in debug mode
app = tg.error.ErrorHandler(app, global_conf, **config['pylons.errorware'])
# Redirect some status codes to /error/document
if config.get('override_root') != 'task':
# "task" wsgi would get a 2nd request to /error/document if we used this middleware
if asbool(config['debug']):
app = StatusCodeRedirect(app, base_config.handle_status_codes)
else:
app = StatusCodeRedirect(app, base_config.handle_status_codes + [500])
# Redirect 401 to the login page
app = LoginRedirectMiddleware(app)
# Add instrumentation
app = AlluraTimerMiddleware(app, app_conf)
# Clear cookies when the CSRF field isn't posted
if not app_conf.get('disable_csrf_protection'):
app = CSRFMiddleware(app, '_session_id')
# Setup the allura SOPs
app = allura_globals_middleware(app)
# Ensure https for logged in users, http for anonymous ones
if asbool(app_conf.get('auth.method', 'local')=='sfx'):
app = SSLMiddleware(app, app_conf.get('no_redirect.pattern'))
# Setup resource manager, widget context SOP
app = ew.WidgetMiddleware(
app,
compress=not asbool(global_conf['debug']),
# compress=True,
script_name=app_conf.get('ew.script_name', '/_ew_resources/'),
url_base=app_conf.get('ew.url_base', '/_ew_resources/'),
extra_headers=eval(app_conf.get('ew.extra_headers', 'None')))
# Make sure that the wsgi.scheme is set appropriately when we
# have the funky HTTP_X_SFINC_SSL environ var
if asbool(app_conf.get('auth.method', 'local')=='sfx'):
app = set_scheme_middleware(app)
# Handle static files (by tool)
app = StaticFilesMiddleware(app, app_conf.get('static.script_name'))
# Handle setup and flushing of Ming ORM sessions
app = MingMiddleware(app)
# Set up the registry for stacked object proxies (SOPs).
# streaming=true ensures they won't be cleaned up till
# the WSGI application's iterator is exhausted
app = RegistryManager(app, streaming=True)
return app
def set_scheme_middleware(app):
def SchemeMiddleware(environ, start_response):
if asbool(environ.get('HTTP_X_SFINC_SSL', 'false')):
environ['wsgi.url_scheme'] = 'https'
return app(environ, start_response)
return SchemeMiddleware
def allura_globals_middleware(app):
def AlluraGlobalsMiddleware(environ, start_response):
import allura.lib.security
import allura.lib.app_globals
registry = environ['paste.registry']
registry.register(allura.credentials, allura.lib.security.Credentials())
return app(environ, start_response)
return AlluraGlobalsMiddleware
def get_tg_vars(context):
import pylons, tg
from allura.lib import helpers as h
from urllib import quote, quote_plus
context.setdefault('g', pylons.g)
context.setdefault('c', pylons.c)
context.setdefault('h', h)
context.setdefault('request', pylons.request)
context.setdefault('response', pylons.response)
context.setdefault('url', pylons.url)
context.setdefault('tg', dict(
config=tg.config,
flash_obj=tg.flash,
quote=quote,
quote_plus=quote_plus,
url=tg.url))