[062892]: / Allura / allura / config / middleware.py  Maximize  Restore  History

Download this file

256 lines (223 with data), 10.8 kB

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
# 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.
"""core app and WSGI middleware initialization. New TurboGears2 apps name it application.py"""
import ast
import importlib
import mimetypes
import pickle
import re
import warnings
import six
import tg
import pkg_resources
from tg import config
from paste.deploy.converters import asbool, aslist, asint
from tg.support.registry import RegistryManager
from beaker.middleware import SessionMiddleware
from beaker.util import PickleSerializer
from paste.exceptions.errormiddleware import ErrorMiddleware
from werkzeug.debug import DebuggedApplication
import activitystream
import ew
import formencode
import ming
from ming.odm.middleware import MingMiddleware
from beaker_session_jwt import JWTCookieSession
# Must apply patches before other Allura imports to ensure all the patches are effective.
# This file gets imported from paste/deploy/loadwsgi.py pretty early in the app execution
# ruff: noqa: E402
from allura.lib import patches
patches.apply()
try:
import newrelic
except ImportError:
pass
else:
patches.newrelic()
from allura.config.app_cfg import AlluraJinjaRenderer
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 CORSMiddleware
from allura.lib.custom_middleware import LoginRedirectMiddleware
from allura.lib.custom_middleware import RememberLoginMiddleware
from allura.lib.custom_middleware import SetRequestHostFromConfig
from allura.lib.custom_middleware import MingTaskSessionSetupMiddleware
from allura.lib.custom_middleware import ContentSecurityPolicyMiddleware
from allura.lib.custom_middleware import SetHeadersMiddleware
from allura.lib.custom_middleware import StatusCodeRedirect
from allura.lib import helpers as h
from allura.lib.utils import configure_ming
__all__ = ['make_app']
# this is webapp entry point from setup.py
def make_app(global_conf: dict, **app_conf):
override_root_module_name = app_conf.get('override_root', None)
if override_root_module_name:
# get an actual instance of it, like BasetestProjectRootController or TaskController
className = override_root_module_name.title().replace('_', '') + 'Controller'
module = importlib.import_module(f'allura.controllers.{override_root_module_name}')
rootClass = getattr(module, className)
root = rootClass()
else:
root = None # will default to RootController in root.py
return _make_core_app(root, global_conf, **app_conf)
class BeakerPickleSerializerWithLatin1(PickleSerializer):
def loads(self, data_string):
# need latin1 to decode py2 timestamps in py https://docs.python.org/3/library/pickle.html#pickle.Unpickler
return pickle.loads(data_string, encoding='latin1') # noqa: S301
def _make_core_app(root, global_conf: dict, **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
: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)
# Configure MongoDB
configure_ming(app_conf)
# Configure ActivityStream
if asbool(app_conf.get('activitystream.recording.enabled', False)):
activitystream.configure(**h.convert_bools(app_conf, prefix='activitystream.'))
# 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)
app = base_config.make_wsgi_app(global_conf, app_conf, wrap_app=None)
for mw_ep in h.iter_entry_points('allura.middleware'):
Middleware = mw_ep.load()
if getattr(Middleware, 'when', 'inner') == 'inner':
app = Middleware(app, config)
# CSP headers
app = ContentSecurityPolicyMiddleware(app, config)
# broswer permissions policy
app = SetHeadersMiddleware(app, config)
# Required for sessions
with warnings.catch_warnings():
# the session_class= arg triggers this warning but is the only way it works, so suppress warning
warnings.filterwarnings('ignore',
re.escape('Session options should start with session. instead of session_.'),
DeprecationWarning)
app = SessionMiddleware(app, config,
original_format_data_serializer=BeakerPickleSerializerWithLatin1(),
session_class=JWTCookieSession)
# Handle "Remember me" functionality
app = RememberLoginMiddleware(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')
if asbool(config.get('cors.enabled', False)):
# Handle CORS requests
allowed_methods = aslist(config.get('cors.methods'))
allowed_headers = aslist(config.get('cors.headers'))
cache_duration = asint(config.get('cors.cache_duration', 0))
app = CORSMiddleware(app, allowed_methods, allowed_headers, cache_duration)
# Setup the allura SOPs
app = allura_globals_middleware(app)
# Ensure http and https used per config
if config.get('override_root') != 'task':
app = SSLMiddleware(app, app_conf.get('no_redirect.pattern'),
app_conf.get('force_ssl.pattern'))
# Setup resource manager, widget context SOP
app = ew.WidgetMiddleware(
app,
compress=True,
use_cache=not asbool(global_conf['debug']),
script_name=app_conf.get('ew.script_name', '/_ew_resources/'),
url_base=app_conf.get('ew.url_base', '/_ew_resources/'),
extra_headers=ast.literal_eval(app_conf.get('ew.extra_headers', '[]')),
cache_max_age=asint(app_conf.get('ew.cache_header_seconds', 60*60*24*365)),
# settings to pass through to jinja Environment for EW core widgets
# these are for the easywidgets' own [easy_widgets.engines] entry point
# (the Allura [easy_widgets.engines] entry point is named "jinja" (not jinja2) but it doesn't need
# any settings since it is a class that uses the same jinja env as the rest of allura)
**{
'jinja2.auto_reload': asbool(config['auto_reload_templates']),
'jinja2.bytecode_cache': AlluraJinjaRenderer._setup_bytecode_cache(),
'jinja2.cache_size': asint(config.get('jinja_cache_size', -1)),
}
)
# Handle static files (by tool)
app = StaticFilesMiddleware(app, app_conf.get('static.script_name'))
# Handle setup and flushing of Ming ORM sessions
app = MingTaskSessionSetupMiddleware(app)
app = MingMiddleware(app)
# Set up the registry for stacked object proxies (SOPs).
app = RegistryManager(app,
# streaming=True causes cleanup problems when StatusCodeRedirect does an extra request
streaming=False,
preserve_exceptions=asbool(config['debug']), # allow inspecting them when debugging errors
)
# "task" wsgi would get a 2nd request to /error/document if we used this middleware
if config.get('override_root') not in ('task', 'basetest_project_root'):
if asbool(config['debug']):
# Converts exceptions to HTTP errors, shows traceback in debug mode
app = DebuggedApplication(app, evalex=True)
else:
app = ErrorMiddleware(app, config)
app = SetRequestHostFromConfig(app, config)
# Redirect some status codes to /error/document
handle_status_codes = [403, 404, 410]
if asbool(config['debug']):
app = StatusCodeRedirect(app, handle_status_codes)
else:
app = StatusCodeRedirect(app, handle_status_codes + [500])
for mw_ep in h.iter_entry_points('allura.middleware'):
Middleware = mw_ep.load()
if getattr(Middleware, 'when', 'inner') == 'outer':
app = Middleware(app, config)
return app
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 tg
from allura.lib import helpers as h
from urllib.parse import quote, quote_plus
context.setdefault('g', tg.app_globals)
context.setdefault('c', tg.tmpl_context)
context.setdefault('h', h)
context.setdefault('request', tg.request)
context.setdefault('response', tg.response)
context.setdefault('url', tg.url)
context.setdefault('tg', dict(
config=tg.config,
flash_obj=tg.flash,
quote=quote,
quote_plus=quote_plus,
url=tg.url))