##// END OF EJS Templates
ux: show list of causes for vcs unavailable error page
dan -
r683:4961d77a default
parent child Browse files
Show More
@@ -1,478 +1,481 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2016 RhodeCode GmbH
3 # Copyright (C) 2010-2016 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22 Pylons middleware initialization
22 Pylons middleware initialization
23 """
23 """
24 import logging
24 import logging
25 from collections import OrderedDict
25 from collections import OrderedDict
26
26
27 from paste.registry import RegistryManager
27 from paste.registry import RegistryManager
28 from paste.gzipper import make_gzip_middleware
28 from paste.gzipper import make_gzip_middleware
29 from pylons.wsgiapp import PylonsApp
29 from pylons.wsgiapp import PylonsApp
30 from pyramid.authorization import ACLAuthorizationPolicy
30 from pyramid.authorization import ACLAuthorizationPolicy
31 from pyramid.config import Configurator
31 from pyramid.config import Configurator
32 from pyramid.settings import asbool, aslist
32 from pyramid.settings import asbool, aslist
33 from pyramid.wsgi import wsgiapp
33 from pyramid.wsgi import wsgiapp
34 from pyramid.httpexceptions import HTTPError, HTTPInternalServerError, HTTPFound
34 from pyramid.httpexceptions import HTTPError, HTTPInternalServerError, HTTPFound
35 from pyramid.events import ApplicationCreated
35 from pyramid.events import ApplicationCreated
36 import pyramid.httpexceptions as httpexceptions
36 import pyramid.httpexceptions as httpexceptions
37 from pyramid.renderers import render_to_response
37 from pyramid.renderers import render_to_response
38 from routes.middleware import RoutesMiddleware
38 from routes.middleware import RoutesMiddleware
39 import routes.util
39 import routes.util
40
40
41 import rhodecode
41 import rhodecode
42 from rhodecode.config import patches
42 from rhodecode.config import patches
43 from rhodecode.config.routing import STATIC_FILE_PREFIX
43 from rhodecode.config.routing import STATIC_FILE_PREFIX
44 from rhodecode.config.environment import (
44 from rhodecode.config.environment import (
45 load_environment, load_pyramid_environment)
45 load_environment, load_pyramid_environment)
46 from rhodecode.lib.exceptions import VCSServerUnavailable
46 from rhodecode.lib.exceptions import VCSServerUnavailable
47 from rhodecode.lib.vcs.exceptions import VCSCommunicationError
47 from rhodecode.lib.vcs.exceptions import VCSCommunicationError
48 from rhodecode.lib.middleware import csrf
48 from rhodecode.lib.middleware import csrf
49 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
49 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
50 from rhodecode.lib.middleware.https_fixup import HttpsFixup
50 from rhodecode.lib.middleware.https_fixup import HttpsFixup
51 from rhodecode.lib.middleware.vcs import VCSMiddleware
51 from rhodecode.lib.middleware.vcs import VCSMiddleware
52 from rhodecode.lib.plugins.utils import register_rhodecode_plugin
52 from rhodecode.lib.plugins.utils import register_rhodecode_plugin
53 from rhodecode.lib.utils2 import aslist as rhodecode_aslist
53 from rhodecode.lib.utils2 import aslist as rhodecode_aslist
54 from rhodecode.subscribers import scan_repositories_if_enabled
54 from rhodecode.subscribers import scan_repositories_if_enabled
55
55
56
56
57 log = logging.getLogger(__name__)
57 log = logging.getLogger(__name__)
58
58
59
59
60 # this is used to avoid avoid the route lookup overhead in routesmiddleware
60 # this is used to avoid avoid the route lookup overhead in routesmiddleware
61 # for certain routes which won't go to pylons to - eg. static files, debugger
61 # for certain routes which won't go to pylons to - eg. static files, debugger
62 # it is only needed for the pylons migration and can be removed once complete
62 # it is only needed for the pylons migration and can be removed once complete
63 class SkippableRoutesMiddleware(RoutesMiddleware):
63 class SkippableRoutesMiddleware(RoutesMiddleware):
64 """ Routes middleware that allows you to skip prefixes """
64 """ Routes middleware that allows you to skip prefixes """
65
65
66 def __init__(self, *args, **kw):
66 def __init__(self, *args, **kw):
67 self.skip_prefixes = kw.pop('skip_prefixes', [])
67 self.skip_prefixes = kw.pop('skip_prefixes', [])
68 super(SkippableRoutesMiddleware, self).__init__(*args, **kw)
68 super(SkippableRoutesMiddleware, self).__init__(*args, **kw)
69
69
70 def __call__(self, environ, start_response):
70 def __call__(self, environ, start_response):
71 for prefix in self.skip_prefixes:
71 for prefix in self.skip_prefixes:
72 if environ['PATH_INFO'].startswith(prefix):
72 if environ['PATH_INFO'].startswith(prefix):
73 # added to avoid the case when a missing /_static route falls
73 # added to avoid the case when a missing /_static route falls
74 # through to pylons and causes an exception as pylons is
74 # through to pylons and causes an exception as pylons is
75 # expecting wsgiorg.routingargs to be set in the environ
75 # expecting wsgiorg.routingargs to be set in the environ
76 # by RoutesMiddleware.
76 # by RoutesMiddleware.
77 if 'wsgiorg.routing_args' not in environ:
77 if 'wsgiorg.routing_args' not in environ:
78 environ['wsgiorg.routing_args'] = (None, {})
78 environ['wsgiorg.routing_args'] = (None, {})
79 return self.app(environ, start_response)
79 return self.app(environ, start_response)
80
80
81 return super(SkippableRoutesMiddleware, self).__call__(
81 return super(SkippableRoutesMiddleware, self).__call__(
82 environ, start_response)
82 environ, start_response)
83
83
84
84
85 def make_app(global_conf, static_files=True, **app_conf):
85 def make_app(global_conf, static_files=True, **app_conf):
86 """Create a Pylons WSGI application and return it
86 """Create a Pylons WSGI application and return it
87
87
88 ``global_conf``
88 ``global_conf``
89 The inherited configuration for this application. Normally from
89 The inherited configuration for this application. Normally from
90 the [DEFAULT] section of the Paste ini file.
90 the [DEFAULT] section of the Paste ini file.
91
91
92 ``app_conf``
92 ``app_conf``
93 The application's local configuration. Normally specified in
93 The application's local configuration. Normally specified in
94 the [app:<name>] section of the Paste ini file (where <name>
94 the [app:<name>] section of the Paste ini file (where <name>
95 defaults to main).
95 defaults to main).
96
96
97 """
97 """
98 # Apply compatibility patches
98 # Apply compatibility patches
99 patches.kombu_1_5_1_python_2_7_11()
99 patches.kombu_1_5_1_python_2_7_11()
100 patches.inspect_getargspec()
100 patches.inspect_getargspec()
101
101
102 # Configure the Pylons environment
102 # Configure the Pylons environment
103 config = load_environment(global_conf, app_conf)
103 config = load_environment(global_conf, app_conf)
104
104
105 # The Pylons WSGI app
105 # The Pylons WSGI app
106 app = PylonsApp(config=config)
106 app = PylonsApp(config=config)
107 if rhodecode.is_test:
107 if rhodecode.is_test:
108 app = csrf.CSRFDetector(app)
108 app = csrf.CSRFDetector(app)
109
109
110 expected_origin = config.get('expected_origin')
110 expected_origin = config.get('expected_origin')
111 if expected_origin:
111 if expected_origin:
112 # The API can be accessed from other Origins.
112 # The API can be accessed from other Origins.
113 app = csrf.OriginChecker(app, expected_origin,
113 app = csrf.OriginChecker(app, expected_origin,
114 skip_urls=[routes.util.url_for('api')])
114 skip_urls=[routes.util.url_for('api')])
115
115
116 # Establish the Registry for this application
116 # Establish the Registry for this application
117 app = RegistryManager(app)
117 app = RegistryManager(app)
118
118
119 app.config = config
119 app.config = config
120
120
121 return app
121 return app
122
122
123
123
124 def make_pyramid_app(global_config, **settings):
124 def make_pyramid_app(global_config, **settings):
125 """
125 """
126 Constructs the WSGI application based on Pyramid and wraps the Pylons based
126 Constructs the WSGI application based on Pyramid and wraps the Pylons based
127 application.
127 application.
128
128
129 Specials:
129 Specials:
130
130
131 * We migrate from Pylons to Pyramid. While doing this, we keep both
131 * We migrate from Pylons to Pyramid. While doing this, we keep both
132 frameworks functional. This involves moving some WSGI middlewares around
132 frameworks functional. This involves moving some WSGI middlewares around
133 and providing access to some data internals, so that the old code is
133 and providing access to some data internals, so that the old code is
134 still functional.
134 still functional.
135
135
136 * The application can also be integrated like a plugin via the call to
136 * The application can also be integrated like a plugin via the call to
137 `includeme`. This is accompanied with the other utility functions which
137 `includeme`. This is accompanied with the other utility functions which
138 are called. Changing this should be done with great care to not break
138 are called. Changing this should be done with great care to not break
139 cases when these fragments are assembled from another place.
139 cases when these fragments are assembled from another place.
140
140
141 """
141 """
142 # The edition string should be available in pylons too, so we add it here
142 # The edition string should be available in pylons too, so we add it here
143 # before copying the settings.
143 # before copying the settings.
144 settings.setdefault('rhodecode.edition', 'Community Edition')
144 settings.setdefault('rhodecode.edition', 'Community Edition')
145
145
146 # As long as our Pylons application does expect "unprepared" settings, make
146 # As long as our Pylons application does expect "unprepared" settings, make
147 # sure that we keep an unmodified copy. This avoids unintentional change of
147 # sure that we keep an unmodified copy. This avoids unintentional change of
148 # behavior in the old application.
148 # behavior in the old application.
149 settings_pylons = settings.copy()
149 settings_pylons = settings.copy()
150
150
151 sanitize_settings_and_apply_defaults(settings)
151 sanitize_settings_and_apply_defaults(settings)
152 config = Configurator(settings=settings)
152 config = Configurator(settings=settings)
153 add_pylons_compat_data(config.registry, global_config, settings_pylons)
153 add_pylons_compat_data(config.registry, global_config, settings_pylons)
154
154
155 load_pyramid_environment(global_config, settings)
155 load_pyramid_environment(global_config, settings)
156
156
157 includeme_first(config)
157 includeme_first(config)
158 includeme(config)
158 includeme(config)
159 pyramid_app = config.make_wsgi_app()
159 pyramid_app = config.make_wsgi_app()
160 pyramid_app = wrap_app_in_wsgi_middlewares(pyramid_app, config)
160 pyramid_app = wrap_app_in_wsgi_middlewares(pyramid_app, config)
161 pyramid_app.config = config
161 pyramid_app.config = config
162 return pyramid_app
162 return pyramid_app
163
163
164
164
165 def make_not_found_view(config):
165 def make_not_found_view(config):
166 """
166 """
167 This creates the view which should be registered as not-found-view to
167 This creates the view which should be registered as not-found-view to
168 pyramid. Basically it contains of the old pylons app, converted to a view.
168 pyramid. Basically it contains of the old pylons app, converted to a view.
169 Additionally it is wrapped by some other middlewares.
169 Additionally it is wrapped by some other middlewares.
170 """
170 """
171 settings = config.registry.settings
171 settings = config.registry.settings
172 vcs_server_enabled = settings['vcs.server.enable']
172 vcs_server_enabled = settings['vcs.server.enable']
173
173
174 # Make pylons app from unprepared settings.
174 # Make pylons app from unprepared settings.
175 pylons_app = make_app(
175 pylons_app = make_app(
176 config.registry._pylons_compat_global_config,
176 config.registry._pylons_compat_global_config,
177 **config.registry._pylons_compat_settings)
177 **config.registry._pylons_compat_settings)
178 config.registry._pylons_compat_config = pylons_app.config
178 config.registry._pylons_compat_config = pylons_app.config
179
179
180 # Appenlight monitoring.
180 # Appenlight monitoring.
181 pylons_app, appenlight_client = wrap_in_appenlight_if_enabled(
181 pylons_app, appenlight_client = wrap_in_appenlight_if_enabled(
182 pylons_app, settings)
182 pylons_app, settings)
183
183
184 # The VCSMiddleware shall operate like a fallback if pyramid doesn't find
184 # The VCSMiddleware shall operate like a fallback if pyramid doesn't find
185 # a view to handle the request. Therefore we wrap it around the pylons app.
185 # a view to handle the request. Therefore we wrap it around the pylons app.
186 if vcs_server_enabled:
186 if vcs_server_enabled:
187 pylons_app = VCSMiddleware(
187 pylons_app = VCSMiddleware(
188 pylons_app, settings, appenlight_client, registry=config.registry)
188 pylons_app, settings, appenlight_client, registry=config.registry)
189
189
190 pylons_app_as_view = wsgiapp(pylons_app)
190 pylons_app_as_view = wsgiapp(pylons_app)
191
191
192 def pylons_app_with_error_handler(context, request):
192 def pylons_app_with_error_handler(context, request):
193 """
193 """
194 Handle exceptions from rc pylons app:
194 Handle exceptions from rc pylons app:
195
195
196 - old webob type exceptions get converted to pyramid exceptions
196 - old webob type exceptions get converted to pyramid exceptions
197 - pyramid exceptions are passed to the error handler view
197 - pyramid exceptions are passed to the error handler view
198 """
198 """
199 def is_vcs_response(response):
199 def is_vcs_response(response):
200 return 'X-RhodeCode-Backend' in response.headers
200 return 'X-RhodeCode-Backend' in response.headers
201
201
202 def is_http_error(response):
202 def is_http_error(response):
203 # webob type error responses
203 # webob type error responses
204 return (400 <= response.status_int <= 599)
204 return (400 <= response.status_int <= 599)
205
205
206 def is_error_handling_needed(response):
206 def is_error_handling_needed(response):
207 return is_http_error(response) and not is_vcs_response(response)
207 return is_http_error(response) and not is_vcs_response(response)
208
208
209 try:
209 try:
210 response = pylons_app_as_view(context, request)
210 response = pylons_app_as_view(context, request)
211 if is_error_handling_needed(response):
211 if is_error_handling_needed(response):
212 response = webob_to_pyramid_http_response(response)
212 response = webob_to_pyramid_http_response(response)
213 return error_handler(response, request)
213 return error_handler(response, request)
214 except HTTPError as e: # pyramid type exceptions
214 except HTTPError as e: # pyramid type exceptions
215 return error_handler(e, request)
215 return error_handler(e, request)
216 except Exception as e:
216 except Exception as e:
217 log.exception(e)
217 log.exception(e)
218
218
219 if settings.get('debugtoolbar.enabled', False):
219 if settings.get('debugtoolbar.enabled', False):
220 raise
220 raise
221
221
222 if isinstance(e, VCSCommunicationError):
222 if isinstance(e, VCSCommunicationError):
223 return error_handler(VCSServerUnavailable(), request)
223 return error_handler(VCSServerUnavailable(), request)
224
224
225 return error_handler(HTTPInternalServerError(), request)
225 return error_handler(HTTPInternalServerError(), request)
226
226
227 return response
227 return response
228
228
229 return pylons_app_with_error_handler
229 return pylons_app_with_error_handler
230
230
231
231
232 def add_pylons_compat_data(registry, global_config, settings):
232 def add_pylons_compat_data(registry, global_config, settings):
233 """
233 """
234 Attach data to the registry to support the Pylons integration.
234 Attach data to the registry to support the Pylons integration.
235 """
235 """
236 registry._pylons_compat_global_config = global_config
236 registry._pylons_compat_global_config = global_config
237 registry._pylons_compat_settings = settings
237 registry._pylons_compat_settings = settings
238
238
239
239
240 def webob_to_pyramid_http_response(webob_response):
240 def webob_to_pyramid_http_response(webob_response):
241 ResponseClass = httpexceptions.status_map[webob_response.status_int]
241 ResponseClass = httpexceptions.status_map[webob_response.status_int]
242 pyramid_response = ResponseClass(webob_response.status)
242 pyramid_response = ResponseClass(webob_response.status)
243 pyramid_response.status = webob_response.status
243 pyramid_response.status = webob_response.status
244 pyramid_response.headers.update(webob_response.headers)
244 pyramid_response.headers.update(webob_response.headers)
245 if pyramid_response.headers['content-type'] == 'text/html':
245 if pyramid_response.headers['content-type'] == 'text/html':
246 pyramid_response.headers['content-type'] = 'text/html; charset=UTF-8'
246 pyramid_response.headers['content-type'] = 'text/html; charset=UTF-8'
247 return pyramid_response
247 return pyramid_response
248
248
249
249
250 def error_handler(exception, request):
250 def error_handler(exception, request):
251 # TODO: dan: replace the old pylons error controller with this
252 from rhodecode.model.settings import SettingsModel
251 from rhodecode.model.settings import SettingsModel
253 from rhodecode.lib.utils2 import AttributeDict
252 from rhodecode.lib.utils2 import AttributeDict
254
253
255 try:
254 try:
256 rc_config = SettingsModel().get_all_settings()
255 rc_config = SettingsModel().get_all_settings()
257 except Exception:
256 except Exception:
258 log.exception('failed to fetch settings')
257 log.exception('failed to fetch settings')
259 rc_config = {}
258 rc_config = {}
260
259
261 base_response = HTTPInternalServerError()
260 base_response = HTTPInternalServerError()
262 # prefer original exception for the response since it may have headers set
261 # prefer original exception for the response since it may have headers set
263 if isinstance(exception, HTTPError):
262 if isinstance(exception, HTTPError):
264 base_response = exception
263 base_response = exception
265
264
266 c = AttributeDict()
265 c = AttributeDict()
267 c.error_message = base_response.status
266 c.error_message = base_response.status
268 c.error_explanation = base_response.explanation or str(base_response)
267 c.error_explanation = base_response.explanation or str(base_response)
269 c.visual = AttributeDict()
268 c.visual = AttributeDict()
270
269
271 c.visual.rhodecode_support_url = (
270 c.visual.rhodecode_support_url = (
272 request.registry.settings.get('rhodecode_support_url') or
271 request.registry.settings.get('rhodecode_support_url') or
273 request.route_url('rhodecode_support')
272 request.route_url('rhodecode_support')
274 )
273 )
275 c.redirect_time = 0
274 c.redirect_time = 0
276 c.rhodecode_name = rc_config.get('rhodecode_title', '')
275 c.rhodecode_name = rc_config.get('rhodecode_title', '')
277 if not c.rhodecode_name:
276 if not c.rhodecode_name:
278 c.rhodecode_name = 'Rhodecode'
277 c.rhodecode_name = 'Rhodecode'
279
278
279 c.causes = []
280 if hasattr(base_response, 'causes'):
281 c.causes = base_response.causes
282
280 response = render_to_response(
283 response = render_to_response(
281 '/errors/error_document.html', {'c': c}, request=request,
284 '/errors/error_document.html', {'c': c}, request=request,
282 response=base_response)
285 response=base_response)
283
286
284 return response
287 return response
285
288
286
289
287 def includeme(config):
290 def includeme(config):
288 settings = config.registry.settings
291 settings = config.registry.settings
289
292
290 # plugin information
293 # plugin information
291 config.registry.rhodecode_plugins = OrderedDict()
294 config.registry.rhodecode_plugins = OrderedDict()
292
295
293 config.add_directive(
296 config.add_directive(
294 'register_rhodecode_plugin', register_rhodecode_plugin)
297 'register_rhodecode_plugin', register_rhodecode_plugin)
295
298
296 if asbool(settings.get('appenlight', 'false')):
299 if asbool(settings.get('appenlight', 'false')):
297 config.include('appenlight_client.ext.pyramid_tween')
300 config.include('appenlight_client.ext.pyramid_tween')
298
301
299 # Includes which are required. The application would fail without them.
302 # Includes which are required. The application would fail without them.
300 config.include('pyramid_mako')
303 config.include('pyramid_mako')
301 config.include('pyramid_beaker')
304 config.include('pyramid_beaker')
302 config.include('rhodecode.channelstream')
305 config.include('rhodecode.channelstream')
303 config.include('rhodecode.admin')
306 config.include('rhodecode.admin')
304 config.include('rhodecode.authentication')
307 config.include('rhodecode.authentication')
305 config.include('rhodecode.integrations')
308 config.include('rhodecode.integrations')
306 config.include('rhodecode.login')
309 config.include('rhodecode.login')
307 config.include('rhodecode.tweens')
310 config.include('rhodecode.tweens')
308 config.include('rhodecode.api')
311 config.include('rhodecode.api')
309 config.include('rhodecode.svn_support')
312 config.include('rhodecode.svn_support')
310 config.add_route(
313 config.add_route(
311 'rhodecode_support', 'https://rhodecode.com/help/', static=True)
314 'rhodecode_support', 'https://rhodecode.com/help/', static=True)
312
315
313 # Add subscribers.
316 # Add subscribers.
314 config.add_subscriber(scan_repositories_if_enabled, ApplicationCreated)
317 config.add_subscriber(scan_repositories_if_enabled, ApplicationCreated)
315
318
316 # Set the authorization policy.
319 # Set the authorization policy.
317 authz_policy = ACLAuthorizationPolicy()
320 authz_policy = ACLAuthorizationPolicy()
318 config.set_authorization_policy(authz_policy)
321 config.set_authorization_policy(authz_policy)
319
322
320 # Set the default renderer for HTML templates to mako.
323 # Set the default renderer for HTML templates to mako.
321 config.add_mako_renderer('.html')
324 config.add_mako_renderer('.html')
322
325
323 # include RhodeCode plugins
326 # include RhodeCode plugins
324 includes = aslist(settings.get('rhodecode.includes', []))
327 includes = aslist(settings.get('rhodecode.includes', []))
325 for inc in includes:
328 for inc in includes:
326 config.include(inc)
329 config.include(inc)
327
330
328 # This is the glue which allows us to migrate in chunks. By registering the
331 # This is the glue which allows us to migrate in chunks. By registering the
329 # pylons based application as the "Not Found" view in Pyramid, we will
332 # pylons based application as the "Not Found" view in Pyramid, we will
330 # fallback to the old application each time the new one does not yet know
333 # fallback to the old application each time the new one does not yet know
331 # how to handle a request.
334 # how to handle a request.
332 config.add_notfound_view(make_not_found_view(config))
335 config.add_notfound_view(make_not_found_view(config))
333
336
334 if not settings.get('debugtoolbar.enabled', False):
337 if not settings.get('debugtoolbar.enabled', False):
335 # if no toolbar, then any exception gets caught and rendered
338 # if no toolbar, then any exception gets caught and rendered
336 config.add_view(error_handler, context=Exception)
339 config.add_view(error_handler, context=Exception)
337
340
338 config.add_view(error_handler, context=HTTPError)
341 config.add_view(error_handler, context=HTTPError)
339
342
340
343
341 def includeme_first(config):
344 def includeme_first(config):
342 # redirect automatic browser favicon.ico requests to correct place
345 # redirect automatic browser favicon.ico requests to correct place
343 def favicon_redirect(context, request):
346 def favicon_redirect(context, request):
344 return HTTPFound(
347 return HTTPFound(
345 request.static_path('rhodecode:public/images/favicon.ico'))
348 request.static_path('rhodecode:public/images/favicon.ico'))
346
349
347 config.add_view(favicon_redirect, route_name='favicon')
350 config.add_view(favicon_redirect, route_name='favicon')
348 config.add_route('favicon', '/favicon.ico')
351 config.add_route('favicon', '/favicon.ico')
349
352
350 config.add_static_view(
353 config.add_static_view(
351 '_static/deform', 'deform:static')
354 '_static/deform', 'deform:static')
352 config.add_static_view(
355 config.add_static_view(
353 '_static/rhodecode', path='rhodecode:public', cache_max_age=3600 * 24)
356 '_static/rhodecode', path='rhodecode:public', cache_max_age=3600 * 24)
354
357
355
358
356 def wrap_app_in_wsgi_middlewares(pyramid_app, config):
359 def wrap_app_in_wsgi_middlewares(pyramid_app, config):
357 """
360 """
358 Apply outer WSGI middlewares around the application.
361 Apply outer WSGI middlewares around the application.
359
362
360 Part of this has been moved up from the Pylons layer, so that the
363 Part of this has been moved up from the Pylons layer, so that the
361 data is also available if old Pylons code is hit through an already ported
364 data is also available if old Pylons code is hit through an already ported
362 view.
365 view.
363 """
366 """
364 settings = config.registry.settings
367 settings = config.registry.settings
365
368
366 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
369 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
367 pyramid_app = HttpsFixup(pyramid_app, settings)
370 pyramid_app = HttpsFixup(pyramid_app, settings)
368
371
369 # Add RoutesMiddleware to support the pylons compatibility tween during
372 # Add RoutesMiddleware to support the pylons compatibility tween during
370 # migration to pyramid.
373 # migration to pyramid.
371 pyramid_app = SkippableRoutesMiddleware(
374 pyramid_app = SkippableRoutesMiddleware(
372 pyramid_app, config.registry._pylons_compat_config['routes.map'],
375 pyramid_app, config.registry._pylons_compat_config['routes.map'],
373 skip_prefixes=(STATIC_FILE_PREFIX, '/_debug_toolbar'))
376 skip_prefixes=(STATIC_FILE_PREFIX, '/_debug_toolbar'))
374
377
375 pyramid_app, _ = wrap_in_appenlight_if_enabled(pyramid_app, settings)
378 pyramid_app, _ = wrap_in_appenlight_if_enabled(pyramid_app, settings)
376
379
377 if settings['gzip_responses']:
380 if settings['gzip_responses']:
378 pyramid_app = make_gzip_middleware(
381 pyramid_app = make_gzip_middleware(
379 pyramid_app, settings, compress_level=1)
382 pyramid_app, settings, compress_level=1)
380
383
381 return pyramid_app
384 return pyramid_app
382
385
383
386
384 def sanitize_settings_and_apply_defaults(settings):
387 def sanitize_settings_and_apply_defaults(settings):
385 """
388 """
386 Applies settings defaults and does all type conversion.
389 Applies settings defaults and does all type conversion.
387
390
388 We would move all settings parsing and preparation into this place, so that
391 We would move all settings parsing and preparation into this place, so that
389 we have only one place left which deals with this part. The remaining parts
392 we have only one place left which deals with this part. The remaining parts
390 of the application would start to rely fully on well prepared settings.
393 of the application would start to rely fully on well prepared settings.
391
394
392 This piece would later be split up per topic to avoid a big fat monster
395 This piece would later be split up per topic to avoid a big fat monster
393 function.
396 function.
394 """
397 """
395
398
396 # Pyramid's mako renderer has to search in the templates folder so that the
399 # Pyramid's mako renderer has to search in the templates folder so that the
397 # old templates still work. Ported and new templates are expected to use
400 # old templates still work. Ported and new templates are expected to use
398 # real asset specifications for the includes.
401 # real asset specifications for the includes.
399 mako_directories = settings.setdefault('mako.directories', [
402 mako_directories = settings.setdefault('mako.directories', [
400 # Base templates of the original Pylons application
403 # Base templates of the original Pylons application
401 'rhodecode:templates',
404 'rhodecode:templates',
402 ])
405 ])
403 log.debug(
406 log.debug(
404 "Using the following Mako template directories: %s",
407 "Using the following Mako template directories: %s",
405 mako_directories)
408 mako_directories)
406
409
407 # Default includes, possible to change as a user
410 # Default includes, possible to change as a user
408 pyramid_includes = settings.setdefault('pyramid.includes', [
411 pyramid_includes = settings.setdefault('pyramid.includes', [
409 'rhodecode.lib.middleware.request_wrapper',
412 'rhodecode.lib.middleware.request_wrapper',
410 ])
413 ])
411 log.debug(
414 log.debug(
412 "Using the following pyramid.includes: %s",
415 "Using the following pyramid.includes: %s",
413 pyramid_includes)
416 pyramid_includes)
414
417
415 # TODO: johbo: Re-think this, usually the call to config.include
418 # TODO: johbo: Re-think this, usually the call to config.include
416 # should allow to pass in a prefix.
419 # should allow to pass in a prefix.
417 settings.setdefault('rhodecode.api.url', '/_admin/api')
420 settings.setdefault('rhodecode.api.url', '/_admin/api')
418
421
419 # Sanitize generic settings.
422 # Sanitize generic settings.
420 _list_setting(settings, 'default_encoding', 'UTF-8')
423 _list_setting(settings, 'default_encoding', 'UTF-8')
421 _bool_setting(settings, 'is_test', 'false')
424 _bool_setting(settings, 'is_test', 'false')
422 _bool_setting(settings, 'gzip_responses', 'false')
425 _bool_setting(settings, 'gzip_responses', 'false')
423
426
424 # Call split out functions that sanitize settings for each topic.
427 # Call split out functions that sanitize settings for each topic.
425 _sanitize_appenlight_settings(settings)
428 _sanitize_appenlight_settings(settings)
426 _sanitize_vcs_settings(settings)
429 _sanitize_vcs_settings(settings)
427
430
428 return settings
431 return settings
429
432
430
433
431 def _sanitize_appenlight_settings(settings):
434 def _sanitize_appenlight_settings(settings):
432 _bool_setting(settings, 'appenlight', 'false')
435 _bool_setting(settings, 'appenlight', 'false')
433
436
434
437
435 def _sanitize_vcs_settings(settings):
438 def _sanitize_vcs_settings(settings):
436 """
439 """
437 Applies settings defaults and does type conversion for all VCS related
440 Applies settings defaults and does type conversion for all VCS related
438 settings.
441 settings.
439 """
442 """
440 _string_setting(settings, 'vcs.svn.compatible_version', '')
443 _string_setting(settings, 'vcs.svn.compatible_version', '')
441 _string_setting(settings, 'git_rev_filter', '--all')
444 _string_setting(settings, 'git_rev_filter', '--all')
442 _string_setting(settings, 'vcs.hooks.protocol', 'pyro4')
445 _string_setting(settings, 'vcs.hooks.protocol', 'pyro4')
443 _string_setting(settings, 'vcs.server', '')
446 _string_setting(settings, 'vcs.server', '')
444 _string_setting(settings, 'vcs.server.log_level', 'debug')
447 _string_setting(settings, 'vcs.server.log_level', 'debug')
445 _string_setting(settings, 'vcs.server.protocol', 'pyro4')
448 _string_setting(settings, 'vcs.server.protocol', 'pyro4')
446 _bool_setting(settings, 'startup.import_repos', 'false')
449 _bool_setting(settings, 'startup.import_repos', 'false')
447 _bool_setting(settings, 'vcs.hooks.direct_calls', 'false')
450 _bool_setting(settings, 'vcs.hooks.direct_calls', 'false')
448 _bool_setting(settings, 'vcs.server.enable', 'true')
451 _bool_setting(settings, 'vcs.server.enable', 'true')
449 _bool_setting(settings, 'vcs.start_server', 'false')
452 _bool_setting(settings, 'vcs.start_server', 'false')
450 _list_setting(settings, 'vcs.backends', 'hg, git, svn')
453 _list_setting(settings, 'vcs.backends', 'hg, git, svn')
451 _int_setting(settings, 'vcs.connection_timeout', 3600)
454 _int_setting(settings, 'vcs.connection_timeout', 3600)
452
455
453
456
454 def _int_setting(settings, name, default):
457 def _int_setting(settings, name, default):
455 settings[name] = int(settings.get(name, default))
458 settings[name] = int(settings.get(name, default))
456
459
457
460
458 def _bool_setting(settings, name, default):
461 def _bool_setting(settings, name, default):
459 input = settings.get(name, default)
462 input = settings.get(name, default)
460 if isinstance(input, unicode):
463 if isinstance(input, unicode):
461 input = input.encode('utf8')
464 input = input.encode('utf8')
462 settings[name] = asbool(input)
465 settings[name] = asbool(input)
463
466
464
467
465 def _list_setting(settings, name, default):
468 def _list_setting(settings, name, default):
466 raw_value = settings.get(name, default)
469 raw_value = settings.get(name, default)
467
470
468 old_separator = ','
471 old_separator = ','
469 if old_separator in raw_value:
472 if old_separator in raw_value:
470 # If we get a comma separated list, pass it to our own function.
473 # If we get a comma separated list, pass it to our own function.
471 settings[name] = rhodecode_aslist(raw_value, sep=old_separator)
474 settings[name] = rhodecode_aslist(raw_value, sep=old_separator)
472 else:
475 else:
473 # Otherwise we assume it uses pyramids space/newline separation.
476 # Otherwise we assume it uses pyramids space/newline separation.
474 settings[name] = aslist(raw_value)
477 settings[name] = aslist(raw_value)
475
478
476
479
477 def _string_setting(settings, name, default):
480 def _string_setting(settings, name, default):
478 settings[name] = settings.get(name, default).lower()
481 settings[name] = settings.get(name, default).lower()
@@ -1,134 +1,139 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2016 RhodeCode GmbH
3 # Copyright (C) 2010-2016 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22 Set of custom exceptions used in RhodeCode
22 Set of custom exceptions used in RhodeCode
23 """
23 """
24
24
25 from webob.exc import HTTPClientError
25 from webob.exc import HTTPClientError
26 from pyramid.httpexceptions import HTTPBadGateway
26 from pyramid.httpexceptions import HTTPBadGateway
27
27
28
28
29 class LdapUsernameError(Exception):
29 class LdapUsernameError(Exception):
30 pass
30 pass
31
31
32
32
33 class LdapPasswordError(Exception):
33 class LdapPasswordError(Exception):
34 pass
34 pass
35
35
36
36
37 class LdapConnectionError(Exception):
37 class LdapConnectionError(Exception):
38 pass
38 pass
39
39
40
40
41 class LdapImportError(Exception):
41 class LdapImportError(Exception):
42 pass
42 pass
43
43
44
44
45 class DefaultUserException(Exception):
45 class DefaultUserException(Exception):
46 pass
46 pass
47
47
48
48
49 class UserOwnsReposException(Exception):
49 class UserOwnsReposException(Exception):
50 pass
50 pass
51
51
52
52
53 class UserOwnsRepoGroupsException(Exception):
53 class UserOwnsRepoGroupsException(Exception):
54 pass
54 pass
55
55
56
56
57 class UserOwnsUserGroupsException(Exception):
57 class UserOwnsUserGroupsException(Exception):
58 pass
58 pass
59
59
60
60
61 class UserGroupAssignedException(Exception):
61 class UserGroupAssignedException(Exception):
62 pass
62 pass
63
63
64
64
65 class StatusChangeOnClosedPullRequestError(Exception):
65 class StatusChangeOnClosedPullRequestError(Exception):
66 pass
66 pass
67
67
68
68
69 class AttachedForksError(Exception):
69 class AttachedForksError(Exception):
70 pass
70 pass
71
71
72
72
73 class RepoGroupAssignmentError(Exception):
73 class RepoGroupAssignmentError(Exception):
74 pass
74 pass
75
75
76
76
77 class NonRelativePathError(Exception):
77 class NonRelativePathError(Exception):
78 pass
78 pass
79
79
80
80
81 class HTTPRequirementError(HTTPClientError):
81 class HTTPRequirementError(HTTPClientError):
82 title = explanation = 'Repository Requirement Missing'
82 title = explanation = 'Repository Requirement Missing'
83 reason = None
83 reason = None
84
84
85 def __init__(self, message, *args, **kwargs):
85 def __init__(self, message, *args, **kwargs):
86 self.title = self.explanation = message
86 self.title = self.explanation = message
87 super(HTTPRequirementError, self).__init__(*args, **kwargs)
87 super(HTTPRequirementError, self).__init__(*args, **kwargs)
88 self.args = (message, )
88 self.args = (message, )
89
89
90
90
91 class HTTPLockedRC(HTTPClientError):
91 class HTTPLockedRC(HTTPClientError):
92 """
92 """
93 Special Exception For locked Repos in RhodeCode, the return code can
93 Special Exception For locked Repos in RhodeCode, the return code can
94 be overwritten by _code keyword argument passed into constructors
94 be overwritten by _code keyword argument passed into constructors
95 """
95 """
96 code = 423
96 code = 423
97 title = explanation = 'Repository Locked'
97 title = explanation = 'Repository Locked'
98 reason = None
98 reason = None
99
99
100 def __init__(self, message, *args, **kwargs):
100 def __init__(self, message, *args, **kwargs):
101 from rhodecode import CONFIG
101 from rhodecode import CONFIG
102 from rhodecode.lib.utils2 import safe_int
102 from rhodecode.lib.utils2 import safe_int
103 _code = CONFIG.get('lock_ret_code')
103 _code = CONFIG.get('lock_ret_code')
104 self.code = safe_int(_code, self.code)
104 self.code = safe_int(_code, self.code)
105 self.title = self.explanation = message
105 self.title = self.explanation = message
106 super(HTTPLockedRC, self).__init__(*args, **kwargs)
106 super(HTTPLockedRC, self).__init__(*args, **kwargs)
107 self.args = (message, )
107 self.args = (message, )
108
108
109
109
110 class IMCCommitError(Exception):
110 class IMCCommitError(Exception):
111 pass
111 pass
112
112
113
113
114 class UserCreationError(Exception):
114 class UserCreationError(Exception):
115 pass
115 pass
116
116
117
117
118 class NotAllowedToCreateUserError(Exception):
118 class NotAllowedToCreateUserError(Exception):
119 pass
119 pass
120
120
121
121
122 class RepositoryCreationError(Exception):
122 class RepositoryCreationError(Exception):
123 pass
123 pass
124
124
125
125
126 class VCSServerUnavailable(HTTPBadGateway):
126 class VCSServerUnavailable(HTTPBadGateway):
127 """ HTTP Exception class for VCS Server errors """
127 """ HTTP Exception class for VCS Server errors """
128 code = 502
128 code = 502
129 title = 'VCS Server Error'
129 title = 'VCS Server Error'
130 causes = [
131 'VCS Server is not running',
132 'Incorrect vcs.server=host:port',
133 'Incorrect vcs.server.protocol',
134 ]
130 def __init__(self, message=''):
135 def __init__(self, message=''):
131 self.explanation = 'Could not connect to VCS Server'
136 self.explanation = 'Could not connect to VCS Server'
132 if message:
137 if message:
133 self.explanation += ': ' + message
138 self.explanation += ': ' + message
134 super(VCSServerUnavailable, self).__init__()
139 super(VCSServerUnavailable, self).__init__()
@@ -1,64 +1,70 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <!DOCTYPE html>
2 <!DOCTYPE html>
3 <html xmlns="http://www.w3.org/1999/xhtml">
3 <html xmlns="http://www.w3.org/1999/xhtml">
4 <head>
4 <head>
5 <title>Error - ${c.error_message}</title>
5 <title>Error - ${c.error_message}</title>
6 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
6 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
7 <meta name="robots" content="index, nofollow"/>
7 <meta name="robots" content="index, nofollow"/>
8 <link rel="icon" href="${h.asset('images/favicon.ico')}" sizes="16x16 32x32" type="image/png" />
8 <link rel="icon" href="${h.asset('images/favicon.ico')}" sizes="16x16 32x32" type="image/png" />
9
9
10
10
11 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
11 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
12 %if c.redirect_time:
12 %if c.redirect_time:
13 <meta http-equiv="refresh" content="${c.redirect_time}; url=${c.url_redirect}"/>
13 <meta http-equiv="refresh" content="${c.redirect_time}; url=${c.url_redirect}"/>
14 %endif
14 %endif
15
15
16 <link rel="stylesheet" type="text/css" href="${h.asset('css/style.css', ver=c.rhodecode_version_hash)}" media="screen"/>
16 <link rel="stylesheet" type="text/css" href="${h.asset('css/style.css', ver=c.rhodecode_version_hash)}" media="screen"/>
17 <!--[if IE]>
17 <!--[if IE]>
18 <link rel="stylesheet" type="text/css" href="${h.asset('css/ie.css')}" media="screen"/>
18 <link rel="stylesheet" type="text/css" href="${h.asset('css/ie.css')}" media="screen"/>
19 <![endif]-->
19 <![endif]-->
20 <style>body { background:#eeeeee; }</style>
20 <style>body { background:#eeeeee; }</style>
21
21
22 <script type="text/javascript" src="${h.asset('js/scripts.js', ver=c.rhodecode_version_hash)}"></script>
22 <script type="text/javascript" src="${h.asset('js/scripts.js', ver=c.rhodecode_version_hash)}"></script>
23 </head>
23 </head>
24 <body>
24 <body>
25 <%include file="/base/flash_msg.html"/>
25 <%include file="/base/flash_msg.html"/>
26
26
27 <div class="wrapper error_page">
27 <div class="wrapper error_page">
28 <div class="sidebar">
28 <div class="sidebar">
29 <a href="${h.url('home')}"><img class="error-page-logo" src="${h.asset('images/RhodeCode_Logo_Black.png')}" alt="RhodeCode"/></a>
29 <a href="${h.url('home')}"><img class="error-page-logo" src="${h.asset('images/RhodeCode_Logo_Black.png')}" alt="RhodeCode"/></a>
30 </div>
30 </div>
31 <div class="main-content">
31 <div class="main-content">
32 <h1>
32 <h1>
33 <span class="error-branding">
33 <span class="error-branding">
34 ${h.branding(c.rhodecode_name)}
34 ${h.branding(c.rhodecode_name)}
35 </span><br/>
35 </span><br/>
36 ${c.error_message} | <span class="error_message">${c.error_explanation}</span>
36 ${c.error_message} | <span class="error_message">${c.error_explanation}</span>
37 </h1>
37 </h1>
38 %if c.redirect_time:
38 %if c.redirect_time:
39 <p>${_('You will be redirected to %s in %s seconds') % (c.redirect_module,c.redirect_time)}</p>
39 <p>${_('You will be redirected to %s in %s seconds') % (c.redirect_module,c.redirect_time)}</p>
40 %endif
40 %endif
41 <div class="inner-column">
41 <div class="inner-column">
42 <h4>Possible Cause</h4>
42 <h4>Possible Causes</h4>
43 <ul>
43 <ul>
44 % if c.causes:
45 %for cause in c.causes:
46 <li>${cause}</li>
47 %endfor
48 %else:
44 <li>The resource may have been deleted.</li>
49 <li>The resource may have been deleted.</li>
45 <li>You may not have access to this repository.</li>
50 <li>You may not have access to this repository.</li>
46 <li>The link may be incorrect.</li>
51 <li>The link may be incorrect.</li>
52 %endif
47 </ul>
53 </ul>
48 </div>
54 </div>
49 <div class="inner-column">
55 <div class="inner-column">
50 <h4>Support</h4>
56 <h4>Support</h4>
51 <p>For support, go to <a href="${c.visual.rhodecode_support_url}" target="_blank">${_('Support')}</a>.
57 <p>For support, go to <a href="${c.visual.rhodecode_support_url}" target="_blank">${_('Support')}</a>.
52 It may be useful to include your log file; see the log file locations <a href="${h.url('enterprise_log_file_locations')}">here</a>.
58 It may be useful to include your log file; see the log file locations <a href="${h.url('enterprise_log_file_locations')}">here</a>.
53 </p>
59 </p>
54 </div>
60 </div>
55 <div class="inner-column">
61 <div class="inner-column">
56 <h4>Documentation</h4>
62 <h4>Documentation</h4>
57 <p>For more information, see <a href="${h.url('enterprise_docs')}">docs.rhodecode.com</a>.</p>
63 <p>For more information, see <a href="${h.url('enterprise_docs')}">docs.rhodecode.com</a>.</p>
58 </div>
64 </div>
59 </div>
65 </div>
60 </div>
66 </div>
61
67
62 </body>
68 </body>
63
69
64 </html>
70 </html>
General Comments 0
You need to be logged in to leave comments. Login now