# -*- coding: utf-8 -*-
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
"""WSGI middleware initialization for the allura application."""
import mimetypes
import pylons.middleware
import tg
import tg.error
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 formencode
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()
try:
import newrelic
except ImportError:
pass
else:
patches.newrelic()
# 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)
# Set FormEncode language to english, as we don't support any other locales
formencode.api.set_stdtranslation(domain='FormEncode', languages=['en'])
# 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)
# 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')))
# 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)
# 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)
# "task" wsgi would get a 2nd request to /error/document if we used this middleware
if config.get('override_root') != 'task':
# Converts exceptions to HTTP errors, shows traceback in debug mode
tg.error.footer_html = '<!-- %s %s -->' # don't use TG footer with extra CSS & images that take time to load
app = tg.error.ErrorHandler(app, global_conf, **config['pylons.errorware'])
# Redirect some status codes to /error/document
if asbool(config['debug']):
app = StatusCodeRedirect(app, base_config.handle_status_codes)
else:
app = StatusCodeRedirect(app, base_config.handle_status_codes + [500])
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.app_globals)
context.setdefault('c', pylons.tmpl_context)
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))