[85ab89]: / Allura / allura / lib / patches.py  Maximize  Restore  History

Download this file

132 lines (114 with data), 5.9 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
# 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.
import re
import webob
import tg.decorators
from decorator import decorator
from tg import request
import mock
import json
from allura.lib import helpers as h
import six
_patched = False
def apply():
global _patched # noqa: PLW0603
if _patched:
return
_patched = True
old_lookup_template_engine = tg.decorators.Decoration.lookup_template_engine
@h.monkeypatch(tg.decorators.Decoration)
def lookup_template_engine(self, request):
'''Wrapper to handle totally borked-up HTTP-ACCEPT headers'''
try:
return old_lookup_template_engine(self, request)
except Exception:
pass
environ = dict(request.environ, HTTP_ACCEPT='*/*')
request = webob.Request(environ)
return old_lookup_template_engine(self, request)
@h.monkeypatch(tg, tg.decorators)
def override_template(controller, template):
'''Copy-pasted patch to allow multiple colons in a template spec'''
if hasattr(controller, 'decoration'):
decoration = controller.decoration
else:
return
if hasattr(decoration, 'engines'):
engines = decoration.engines
else:
return
for content_type, content_engine in engines.items():
template = template.split(':', 1)
template.extend(content_engine[2:])
try:
override_mapping = request._override_mapping
except AttributeError:
override_mapping = request._override_mapping = {}
override_mapping[controller.__func__] = {content_type: template}
@h.monkeypatch(tg, tg.decorators)
@decorator
def without_trailing_slash(func, *args, **kwargs):
'''Monkey-patched to use 301 redirects for SEO, and handle query strings'''
__traceback_hide__ = 'before_and_this' # for paste/werkzeug shorter traces
response_type = getattr(request, 'response_type', None)
if (request.method == 'GET' and request.path.endswith('/') and not response_type):
location = request.path_url[:-1]
if request.query_string:
location += '?' + request.query_string
raise webob.exc.HTTPMovedPermanently(location=location)
return func(*args, **kwargs)
@h.monkeypatch(tg, tg.decorators)
@decorator
def with_trailing_slash(func, *args, **kwargs):
'''Monkey-patched to use 301 redirects for SEO, and handle query strings'''
__traceback_hide__ = 'before_and_this' # for paste/werkzeug shorter traces
response_type = getattr(request, 'response_type', None)
if (request.method == 'GET' and not request.path.endswith('/') and not response_type):
location = request.path_url + '/'
if request.query_string:
location += '?' + request.query_string
raise webob.exc.HTTPMovedPermanently(location=location)
return func(*args, **kwargs)
# http://blog.watchfire.com/wfblog/2011/10/json-based-xss-exploitation.html
# change < to its unicode escape when rendering JSON out of turbogears
# This is to avoid IE9 and earlier, which don't know the json content type
# and may attempt to render JSON data as HTML if the URL ends in .html
original_tg_jsonify_JSONEncoder_encode = tg.jsonify.JSONEncoder.encode
@h.monkeypatch(tg.jsonify.JSONEncoder)
def encode(self, o):
return original_tg_jsonify_JSONEncoder_encode(self, o).replace('<', r'\u003C')
old_controller_call = tg.controllers.DecoratedController._call
def newrelic():
@h.monkeypatch(tg.controllers.DecoratedController,
tg.controllers.decoratedcontroller.DecoratedController)
def _call(self, controller, *args, **kwargs):
'''Set NewRelic transaction name to actual controller name'''
__traceback_hide__ = 'before_and_this' # for paste/werkzeug shorter traces
import newrelic.agent
controller_name = newrelic.agent.callable_name(controller)
# https://docs.newrelic.com/docs/apm/agents/python-agent/python-agent-api/settransactionname-python-agent-api/
# if a second internal request for /error/document happens, use a lower (1) priority so original name stays
name_priority = 1 if 'ErrorController' in controller_name else 2
newrelic.agent.set_transaction_name(controller_name, priority=name_priority)
return old_controller_call(self, controller, *args, **kwargs)
import newrelic.api.error_trace
import newrelic.api.function_trace
# These are based on newrelic/hooks/framework_pylons.py since TG is similar to Pylons
# capture exceptions:
newrelic.api.error_trace.wrap_error_trace('tg.wsgiapp', 'TGApp.__call__')
# record as its own component in transaction breakdown; should help distinguish middleware vs controller time
newrelic.api.function_trace.wrap_function_trace('tg.controllers.tgcontroller', 'TGController.__call__')