##// END OF EJS Templates
core: handle bogus GET params so we don't crash on translate subscriber.
marcink -
r3132:fb303e8b default
parent child Browse files
Show More
@@ -1,329 +1,341 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2018 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20 import io
21 21 import re
22 import os
22 23 import datetime
23 24 import logging
24 25 import Queue
25 26 import subprocess32
26 import os
27 27
28 28
29 29 from dateutil.parser import parse
30 from pyramid.i18n import get_localizer
30 from pyramid.httpexceptions import HTTPBadRequest
31 31 from pyramid.threadlocal import get_current_request
32 32 from pyramid.interfaces import IRoutesMapper
33 33 from pyramid.settings import asbool
34 34 from pyramid.path import AssetResolver
35 35 from threading import Thread
36 36
37 37 from rhodecode.translation import _ as tsf
38 38 from rhodecode.config.jsroutes import generate_jsroutes_content
39 39 from rhodecode.lib import auth
40 40 from rhodecode.lib.base import get_auth_user
41 41
42 42
43 43 import rhodecode
44 44
45 45
46 46 log = logging.getLogger(__name__)
47 47
48 48
49 49 def add_renderer_globals(event):
50 50 from rhodecode.lib import helpers
51 51
52 52 # TODO: When executed in pyramid view context the request is not available
53 53 # in the event. Find a better solution to get the request.
54 54 request = event['request'] or get_current_request()
55 55
56 56 # Add Pyramid translation as '_' to context
57 57 event['_'] = request.translate
58 58 event['_ungettext'] = request.plularize
59 59 event['h'] = helpers
60 60
61 61
62 62 def add_localizer(event):
63 63 request = event.request
64 localizer = request.localizer
64 try:
65 localizer = request.localizer
66 except Exception:
67 log.exception('Failed to get localizer')
68 # NOTE(marcink): Handle bug in pyramid by malformed GET params could crash
69 # this resulting on various odd errors on missing translators
70 # see: https://github.com/Pylons/pyramid/issues/3399
71
72 def dummy_translate(*args, **kwargs):
73 return ''.join(args)
74 request.translate = tsf
75 request.plularize = dummy_translate
76 raise HTTPBadRequest()
65 77
66 78 def auto_translate(*args, **kwargs):
67 79 return localizer.translate(tsf(*args, **kwargs))
68 80
69 81 request.translate = auto_translate
70 82 request.plularize = localizer.pluralize
71 83
72 84
73 85 def set_user_lang(event):
74 86 request = event.request
75 87 cur_user = getattr(request, 'user', None)
76 88
77 89 if cur_user:
78 90 user_lang = cur_user.get_instance().user_data.get('language')
79 91 if user_lang:
80 92 log.debug('lang: setting current user:%s language to: %s', cur_user, user_lang)
81 93 event.request._LOCALE_ = user_lang
82 94
83 95
84 96 def add_request_user_context(event):
85 97 """
86 98 Adds auth user into request context
87 99 """
88 100 request = event.request
89 101 # access req_id as soon as possible
90 102 req_id = request.req_id
91 103
92 104 if hasattr(request, 'vcs_call'):
93 105 # skip vcs calls
94 106 return
95 107
96 108 if hasattr(request, 'rpc_method'):
97 109 # skip api calls
98 110 return
99 111
100 112 auth_user = get_auth_user(request)
101 113 request.user = auth_user
102 114 request.environ['rc_auth_user'] = auth_user
103 115 request.environ['rc_auth_user_id'] = auth_user.user_id
104 116 request.environ['rc_req_id'] = req_id
105 117
106 118
107 119 def inject_app_settings(event):
108 120 settings = event.app.registry.settings
109 121 # inject info about available permissions
110 122 auth.set_available_permissions(settings)
111 123
112 124
113 125 def scan_repositories_if_enabled(event):
114 126 """
115 127 This is subscribed to the `pyramid.events.ApplicationCreated` event. It
116 128 does a repository scan if enabled in the settings.
117 129 """
118 130 settings = event.app.registry.settings
119 131 vcs_server_enabled = settings['vcs.server.enable']
120 132 import_on_startup = settings['startup.import_repos']
121 133 if vcs_server_enabled and import_on_startup:
122 134 from rhodecode.model.scm import ScmModel
123 135 from rhodecode.lib.utils import repo2db_mapper, get_rhodecode_base_path
124 136 repositories = ScmModel().repo_scan(get_rhodecode_base_path())
125 137 repo2db_mapper(repositories, remove_obsolete=False)
126 138
127 139
128 140 def write_metadata_if_needed(event):
129 141 """
130 142 Writes upgrade metadata
131 143 """
132 144 import rhodecode
133 145 from rhodecode.lib import system_info
134 146 from rhodecode.lib import ext_json
135 147
136 148 fname = '.rcmetadata.json'
137 149 ini_loc = os.path.dirname(rhodecode.CONFIG.get('__file__'))
138 150 metadata_destination = os.path.join(ini_loc, fname)
139 151
140 152 def get_update_age():
141 153 now = datetime.datetime.utcnow()
142 154
143 155 with open(metadata_destination, 'rb') as f:
144 156 data = ext_json.json.loads(f.read())
145 157 if 'created_on' in data:
146 158 update_date = parse(data['created_on'])
147 159 diff = now - update_date
148 160 return diff.total_seconds() / 60.0
149 161
150 162 return 0
151 163
152 164 def write():
153 165 configuration = system_info.SysInfo(
154 166 system_info.rhodecode_config)()['value']
155 167 license_token = configuration['config']['license_token']
156 168
157 169 setup = dict(
158 170 workers=configuration['config']['server:main'].get(
159 171 'workers', '?'),
160 172 worker_type=configuration['config']['server:main'].get(
161 173 'worker_class', 'sync'),
162 174 )
163 175 dbinfo = system_info.SysInfo(system_info.database_info)()['value']
164 176 del dbinfo['url']
165 177
166 178 metadata = dict(
167 179 desc='upgrade metadata info',
168 180 license_token=license_token,
169 181 created_on=datetime.datetime.utcnow().isoformat(),
170 182 usage=system_info.SysInfo(system_info.usage_info)()['value'],
171 183 platform=system_info.SysInfo(system_info.platform_type)()['value'],
172 184 database=dbinfo,
173 185 cpu=system_info.SysInfo(system_info.cpu)()['value'],
174 186 memory=system_info.SysInfo(system_info.memory)()['value'],
175 187 setup=setup
176 188 )
177 189
178 190 with open(metadata_destination, 'wb') as f:
179 191 f.write(ext_json.json.dumps(metadata))
180 192
181 193 settings = event.app.registry.settings
182 194 if settings.get('metadata.skip'):
183 195 return
184 196
185 197 # only write this every 24h, workers restart caused unwanted delays
186 198 try:
187 199 age_in_min = get_update_age()
188 200 except Exception:
189 201 age_in_min = 0
190 202
191 203 if age_in_min > 60 * 60 * 24:
192 204 return
193 205
194 206 try:
195 207 write()
196 208 except Exception:
197 209 pass
198 210
199 211
200 212 def write_js_routes_if_enabled(event):
201 213 registry = event.app.registry
202 214
203 215 mapper = registry.queryUtility(IRoutesMapper)
204 216 _argument_prog = re.compile('\{(.*?)\}|:\((.*)\)')
205 217
206 218 def _extract_route_information(route):
207 219 """
208 220 Convert a route into tuple(name, path, args), eg:
209 221 ('show_user', '/profile/%(username)s', ['username'])
210 222 """
211 223
212 224 routepath = route.pattern
213 225 pattern = route.pattern
214 226
215 227 def replace(matchobj):
216 228 if matchobj.group(1):
217 229 return "%%(%s)s" % matchobj.group(1).split(':')[0]
218 230 else:
219 231 return "%%(%s)s" % matchobj.group(2)
220 232
221 233 routepath = _argument_prog.sub(replace, routepath)
222 234
223 235 if not routepath.startswith('/'):
224 236 routepath = '/'+routepath
225 237
226 238 return (
227 239 route.name,
228 240 routepath,
229 241 [(arg[0].split(':')[0] if arg[0] != '' else arg[1])
230 242 for arg in _argument_prog.findall(pattern)]
231 243 )
232 244
233 245 def get_routes():
234 246 # pyramid routes
235 247 for route in mapper.get_routes():
236 248 if not route.name.startswith('__'):
237 249 yield _extract_route_information(route)
238 250
239 251 if asbool(registry.settings.get('generate_js_files', 'false')):
240 252 static_path = AssetResolver().resolve('rhodecode:public').abspath()
241 253 jsroutes = get_routes()
242 254 jsroutes_file_content = generate_jsroutes_content(jsroutes)
243 255 jsroutes_file_path = os.path.join(
244 256 static_path, 'js', 'rhodecode', 'routes.js')
245 257
246 258 try:
247 259 with io.open(jsroutes_file_path, 'w', encoding='utf-8') as f:
248 260 f.write(jsroutes_file_content)
249 261 except Exception:
250 262 log.exception('Failed to write routes.js into %s', jsroutes_file_path)
251 263
252 264
253 265 class Subscriber(object):
254 266 """
255 267 Base class for subscribers to the pyramid event system.
256 268 """
257 269 def __call__(self, event):
258 270 self.run(event)
259 271
260 272 def run(self, event):
261 273 raise NotImplementedError('Subclass has to implement this.')
262 274
263 275
264 276 class AsyncSubscriber(Subscriber):
265 277 """
266 278 Subscriber that handles the execution of events in a separate task to not
267 279 block the execution of the code which triggers the event. It puts the
268 280 received events into a queue from which the worker process takes them in
269 281 order.
270 282 """
271 283 def __init__(self):
272 284 self._stop = False
273 285 self._eventq = Queue.Queue()
274 286 self._worker = self.create_worker()
275 287 self._worker.start()
276 288
277 289 def __call__(self, event):
278 290 self._eventq.put(event)
279 291
280 292 def create_worker(self):
281 293 worker = Thread(target=self.do_work)
282 294 worker.daemon = True
283 295 return worker
284 296
285 297 def stop_worker(self):
286 298 self._stop = False
287 299 self._eventq.put(None)
288 300 self._worker.join()
289 301
290 302 def do_work(self):
291 303 while not self._stop:
292 304 event = self._eventq.get()
293 305 if event is not None:
294 306 self.run(event)
295 307
296 308
297 309 class AsyncSubprocessSubscriber(AsyncSubscriber):
298 310 """
299 311 Subscriber that uses the subprocess32 module to execute a command if an
300 312 event is received. Events are handled asynchronously.
301 313 """
302 314
303 315 def __init__(self, cmd, timeout=None):
304 316 super(AsyncSubprocessSubscriber, self).__init__()
305 317 self._cmd = cmd
306 318 self._timeout = timeout
307 319
308 320 def run(self, event):
309 321 cmd = self._cmd
310 322 timeout = self._timeout
311 323 log.debug('Executing command %s.', cmd)
312 324
313 325 try:
314 326 output = subprocess32.check_output(
315 327 cmd, timeout=timeout, stderr=subprocess32.STDOUT)
316 328 log.debug('Command finished %s', cmd)
317 329 if output:
318 330 log.debug('Command output: %s', output)
319 331 except subprocess32.TimeoutExpired as e:
320 332 log.exception('Timeout while executing command.')
321 333 if e.output:
322 334 log.error('Command output: %s', e.output)
323 335 except subprocess32.CalledProcessError as e:
324 336 log.exception('Error while executing command.')
325 337 if e.output:
326 338 log.error('Command output: %s', e.output)
327 339 except:
328 340 log.exception(
329 341 'Exception while executing command %s.', cmd)
General Comments 0
You need to be logged in to leave comments. Login now